Consider I have the following classes:
class User
{
[Key]
public Guid Id {get; set;}
public string UserName {get; set;}
}
class MyUsersContext: DbContext
{
DbSet<User> Users {get; set;}
}
class MyRepository
{
MyUsersContext _db; //INJECTED
User AddUser(string userName)
{
var newBorn = new User() { UserName = userName };
_db.Users.Add(newBorn);
return newBorn;
}
}
class MyMemProvider: MembershipProvider
{
MyRepository repo; //INJECTED
public override MembershipUser CreateUser(string username...
{
var ret = repo.AddUser(username);
return new MembershipUser(System.Web.Security.Membership.Provider.Name, ret.UserName...
}
}
This is a stripped down version of my custom membership provider for ASP.NET MVC3 site. At what point User.Id gets filled with actual Guid in this scenario?
You have to apply DatabaseGeneratedOption on your Id attribute.
class User
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id {get; set;}
public string UserName {get; set;}
}
Related
I have the following classes: User, Post and UserPost. User and Post have a 1-to-many relationship. There's a third join table called UserPost that tracks the up/down vote each post gets. To ensure each user can only upvote/downvote once, the ID (PK) of this table is a composite key of User and Post ID.
public class User {
public Guid Id {get; set;}
public string UserName {get; set;}
public ICollection<Post> Posts {get; set;}
}
public class Post {
public Guid Id {get; set;}
public string Content {get;set;}
public User User {get; set;}
}
public UserPost {
public Guid Id {get; set;} // This should be a composite key of User ID and Post ID
public Guid PostId {get;set;}
public Guid UserId {get;set;}
public VoteType VoteType {get; set;}
}
public enum VoteType {
Up = 1,
Down = 0
}
In my DB Context class I defined the User/Post relationship like this:
builder.Entity<User>()
.HasMany(u => u.Posts)
.WithOne(p => p.User)
Now I want to define the relationship for the UserPost model but not sure how to go about it. So far I have:
builder.Entity<UserPost>()
.HasKey(x => new { x.UserId, x.PostId })
Does it require anything further?
Write your whole relationship as follows:
public class User
{
public Guid Id {get; set;}
public string UserName {get; set;}
public ICollection<Post> Posts {get; set;}
}
public class Post
{
public Guid Id {get; set;}
public string Content {get;set;}
public Guid UserId {get; set;}
public User User {get; set;}
}
public UserVote // Rename this from UserPost to UserVote to keep naming consistency.
{
public Guid PostId {get;set;}
public Guid UserId {get;set;}
public VoteType VoteType {get; set;}
public Post Post {get; set;}
public User User {get; set;}
}
public enum VoteType {
Up = 1,
Down = 0
}
Now, Fluent API configuration for the UserVote as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserVote>(e =>
{
e.HasKey(uv => new { uv.PostId, uv.UserId}); //<-- Here is the composite key.
e.HasOne(uv => uv.Post).WithMany().HasForeignKey(uv => uv.PostId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(uv => uv.User).WithMany().HasForeignKey(uv => uv.UserId).OnDelete(DeleteBehavior.Restrict);
});
}
I have an entity user with roles and I need to load the user with our roles (but not proxy object).
class User{
public int Id{get; set;}
public string Name{get; set;}
public ICollection<Role> Roles{get; set;}
}
class Role{
public int Id{get; set;}
public string Name{get; set;}
public virtual User User{get; set;}
}
When I use this: DbContext.Users.Where(x => x.Id == id).Include(x => x.Roles).FirstOrDefault() I get User object with Role Proxy object.
I need User object with Role object.
How to do it? Thanks
Answering your specific answer, just disable proxy creation just for a specific code block, but you will have to eager load your entities like so:
try
{
DbContext.Configuration.ProxyCreationEnabled = false;
var userWithRoles = DbContext.Users.Include(x => x.Roles).Where(x => x.Id == id).FirstOrDefault();
}
finally
{
DbContext.Configuration.ProxyCreationEnabled = true;
}
...which will affect only that instance. I wrapped this in a try finally block, because if any exception occur while loading entities, you can make sure that option will be reversed.
You could also set this globally inside your DbContext constructor, but I don't recommend this:
public class YourDbContext : DbContext
{
public YourDbContext() : base("name=ConnectionString")
{
this.Configuration.ProxyCreationEnabled = true;
}
}
My recommendation is to avoid exposing you database entities to an API. You can create DTO classes and expose them, instead:
// original entities
public class User {
public int Id{get; set;}
public string Name{get; set;}
public ICollection<Role> Roles{get; set;}
// other fields like birthdate, status, password, etc...
}
public class Role {
public int Id{get; set;}
public string Name{get; set;}
public virtual User User{get; set;}
}
// DTO classes, keeping only the fields you want
// original entities
public class UserDTO {
public int Id{get; set;}
public string Name{get; set;}
public ICollection<RoleDTO> Roles{get; set;}
}
public class RoleDTO {
public int Id{get; set;}
public string Name{get; set;}
}
// project them like this:
var userDTO = DbContext.Users.Where(x => x.Id == id)
.Select(u => new UserDTO
{
Id = u.Id,
Name = u.Name,
Roles = u.Roles.Select(r => new RoleDTO
{
Id = r.Id,
Name = r.Name
}),
})
.FirstOrDefault();
Then you could return just the DTO. There are tools like AutoMapper which makes easier and cleaner to project DTO classes, this is just an example.
I have a simple AspNetCore Web Api with 2 entities a Product and a Person which derives from IdentityUser
public class Product
{
public int Id {get;set;}
public string Name {get; set;}
public Person Person {get; set; }
}
public class Person : IdentityUser
{
public string FirstName {get; set;}
}
And the ViewModel and the AutoMapper profile
public class ProductVM
{
public string Name {get; set;}
public string PersonWhoAdded {get;set;}
}
public class ProductProfile : Profile
{
public ProductProfile()
{
CreateMap<Product, ProductVM>()
.ForMember(p=> PersonWhoAdded , opt.MapFrom(model=>model.Person.FirstName)).ReverseMap();
}}
And here is my repo
`public class ProductRepo: IProductRepo
{
public IEnumerable<Product> GetProducts()
{
return context.ProductSet.Include(p=> p.Person).ToList();
}
}`
And my controller
[Route("api/[controller]")
public class ProductController: Controller
{
IMapper _mapper;
ctor(mapper){
_mapper=mapper;
}
[HttpGet]
public IActionResult Get()
{
IEnumerable<Product> _products = _repo.GetProducts();
// omitted Status codes for simplicity
return Ok(_mapper.Map<IEnumerable<ProductVM>>(_products);
}
}
The problem is it shows me null on personWhoAdded instead of the FirstName
I can authenticate and do PUSH from different users after I register and login but if I want a get to list the products and the firstName of the User it shows me null ...
Maybe there's a different approach but I am not an expert so any help would do
Thanks
I have the following (abbreviated) DbContext:
public class O19Context : BaseContext<O19Context>
{
public DbSet<PRJ> O19Set { get; set; }
}
[Table("AUFK")]
public class AUFK
{
[ForeignKey("PRJ_GUID")]
public PRJ PRJ {get; set;}
[Key]
public Guid AUFK_GUID {get; set;}
}
[Table("PRJ")]
public class PRJ
{
[Key]
public Guid PRJ_GUID {get; set;}
public IQueryable<AUFK> AUFKS {get; set;}
}
When I run the following code:
var db = new O19Context();
var prj = db.O19Set.Include("AUFKS")
.Single(o => o.PRJ_GUID ==
new Guid("6FE5E97B-9970-4E24-B051-9A710C03A030"));
I get an invalid Include path error. The EntityType PRJ does not declare a navigation property with the name AUFKS.
Where am I going wrong?
Pamela
A good way to see if EF likes your POCO is the EF Power Tools "View Entity Data Model (Read Only)".
http://www.infoq.com/news/2013/10/ef-power-tools-beta4
I made two changes to your code and it now creates a little diagram...
[TestClass]
public class O19Tests
{
[TestMethod]
public void O19Test1()
{
var db = new O19Context();
var prj = db.O19Set.Include("AUFKS")
.FirstOrDefault(o => o.PRJ_GUID ==
new Guid("6FE5E97B-9970-4E24-B051-9A710C03A030"));
}
}
public class O19Context : DbContext//<O19Context>
{
public DbSet<PRJ> O19Set { get; set; }
}
[Table("AUFK")]
public class AUFK
{
//[ForeignKey("PRJ_GUID")] //remove this...
//there is no PRJ_GUID field IN THIS CLASS
public PRJ PRJ { get; set; }
[Key]
public Guid AUFK_GUID { get; set; }
}
[Table("PRJ")]
public class PRJ
{
[Key]
public Guid PRJ_GUID { get; set; }
public IList<AUFK> AUFKS { get; set; } //use IList
}
A navigation property must implement ICollection<T> - you define the property as IQueryable<T> which I do not believe is supported
I am trying out EF and Code First approach
I have an Account model which looks like
public class Account
{
public virtual int AccountID {get; set;}
public virtual string AccountName {get; set;}
public virtual Account ParentAccount {get; set;}
public virtual Contact AccountOwner {get; set}
}
Contact class is a simple class
public class Contact
{
public virtual int ContactID {get; set;}
public virtual string ContactName {get; set;}
}
Now I have no idea how to add IDs to the account class.
Suppose I have set up the context correctly, what modifications are needed to the Account class so that
I can add an Account as the parent of another Account
I can access the ParentAccount of a particular Account
I can access the AccountOwner of a particular Account
I am new to this. Any help would be appreciated
Here's how I would solve this.
Add foreign key properties to your classes. I'm talking about ParentAccountId and AccountOwnerId. To make navigating things a bit easier, I have also added a collection of child accounts to Account and a collection of owned accounts to Contact. Note that it's not necessary to make "normal" properties virtual. Only navigational properties should be made virtual.
public class Account
{
public int AccountID {get; set;}
public string AccountName {get; set;}
public int? ParentAccountId { get; set; }
public int? AccountOwnerId { get; set; }
public virtual Account ParentAccount { get; set; }
public virtual ICollection<Account> ChildAccounts { get; set; }
public virtual Contact AccountOwner { get; set; }
public Account()
{
ChildAccounts = new List<Account>();
}
}
public class Contact
{
public int ContactID { get; set; }
public string ContactName { get; set; }
public virtual ICollection<Account> OwnedAccounts { get; set; }
public Contact()
{
OwnedAccounts = new List<Account>();
}
}
Next, create a mapping for the Account class to explain to EF how to setup the relationships.
public class AccountMapping : EntityTypeConfiguration<Account>
{
public AccountMapping()
{
HasOptional(x => x.ParentAccount).WithMany(x => x.ChildAccounts).HasForeignKey(x => x.ParentAccountId);
HasOptional(x => x.AccountOwner).WithMany(x => x.OwnedAccounts).HasForeignKey(x => x.AccountOwnerId);
}
}
Finally, add the mapping to your DbContext class.
public MyContext : DbContext
{
public DbSet<Account> Accounts { get; set; }
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AccountMapping());
}
}
Note that I have assumed that the AccountOwner and ParentAccount are optional. If they are required, simply change the type of the foreign properties from int? to int and change the HasOptional in the mappings to HasRequired.
public class Account
{
[Key]
public virtual int AccountID {get; set;}
public virtual string AccountName {get; set;}
public virtual Account ParentAccount {get; set;}
public virtual Contact AccountOwner {get; set}
public virtual IEnummerable<Account> ChiledAccount {get; set}
}
public class Contact
{
[Key]
public virtual int ContactID {get; set;}
public virtual string ContactName {get; set;}
}
You can do this by without using Fluent API.That is by default conventions are maintained by EF.B'cos your mappings are simple.
You're having Account (1) : Contact (m) Relationship.
So try with below models
public class Account
{
public int Id {get; set;}
public string AccountName {get; set;}
public virtual ICollection<Contact> AccountOwners { get; set; }
}
public class Contact
{
public int Id {get; set;}
public string ContactName {get; set;}
public virtual Account ParentAccount {get; set;}
}
Then Your database tables will be created like below:
Accounts Table
Id int NotNull
AccountName nvarchar(100) AllowNull
Contacts Table
Id int NotNull
ContactName nvarchar(100) AllowNull
Account_Id int NotNull
If you need to do advance mappings then you have to learn Fluent API.