I'm learning ASP.NET Core with Entity Framework and I'm trying to add an FK in my UserDetails table. These are the model:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual UserDetails UserDetail { get; set; }
}
public class UserDetails
{
public string UserId { get; set; }
public string Biography { get; set; }
public string Country { get; set; }
public Uri FacebookLink { get; set; }
public Uri TwitterLink { get; set; }
public Uri SkypeLink { get; set; }
public virtual User UserKey { get; set; }
}
The table User is the Master table which contains all the registered user in my application (I'm using AspNetCore.Identity).
Actual I want add as FK the property UserId which must bound the Id of User. So inside the ApplicationContext class I did the following:
public class DemoAppContext : IdentityDbContext<ApplicationUser>
{
public DemoAppContext(DbContextOptions<DemoAppContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<UserDetails>(entity =>
{
entity.Property(e => e.Biography).HasMaxLength(150);
entity.Property(e => e.Country).HasMaxLength(10);
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey(d => d.???; <- problem here
});
}
public DbSet<User> Users { get; set; }
public DbSet<UserDetails> UserDetails { get; set; }
}
I overrided the OnModelCreating and using the ModelBuilder I defined for UserDetails table the MaxLength of some properties. In the last line of builder.Entity<UserDetails> I tried to assign the FK creating the relationship with HasOne => UserKey which contains the object User. The relationship is 1 to 1 so I used WithOne and assigned UserDetail which contains the UserDetails object.
At the end I used HasForeignKey but when I type d. the compiler doesn't show any properties.
What I did wrong? Maybe I overcomplicated the things?
Sorry for any errors, and thanks in advance for any explanation.
The following code will work:
builder.Entity<UserDetails>(entity =>
{
entity.Property(e => e.Biography).HasMaxLength(150);
entity.Property(e => e.Country).HasMaxLength(10);
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey<UserDetails>(x => x.UserId); //???; < -problem here
});
Can you try this:
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey<User>(b => b.Id);
or
public class UserDetails
{
[ForeignKey(nameof(UserKey))]
public string UserId { get; set; }
public string Biography { get; set; }
public string Country { get; set; }
public Uri FacebookLink { get; set; }
public Uri TwitterLink { get; set; }
public Uri SkypeLink { get; set; }
public virtual User UserKey { get; set; }
}
Related
I have the following one-to-one relationship with ApplicationUser:
public class Person
{
public Guid PersonId { get; set; }
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public string Role { get; set; }
public string Country { get; set; }
public DateTime CreatedDate { get; set; }
public bool IsActive { get; set; }
public ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser
{
public Guid PersonId { get; set; }
public string Provider { get; set; } = "LOCAL";
public string ExternalUserId { get; set; }
public Person Person { get; set; }
}
DbContext:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> opts) : base(opts) { }
public DbSet<Person> Person { get; set; }
public DbSet<ApplicationUser> User { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new PersonConfiguration());
modelBuilder.Entity<ApplicationUser>(e => {
e.ToTable(name: "User");
e.HasOne(p => p.Person).WithOne(u => u.User);
//e.HasOne(p => p.Person).WithOne().HasForeignKey;
});
modelBuilder.Entity<IdentityRole>(e => e.ToTable(name: "Role"));
modelBuilder.Entity<IdentityUserRole<string>>(e => e.ToTable(name: "UserRole"));
modelBuilder.Entity<IdentityUserClaim<string>>(e => e.ToTable(name: "UserClaim"));
modelBuilder.Entity<IdentityUserLogin<string>>(e => e.ToTable(name: "UserLogin"));
modelBuilder.Entity<IdentityUserToken<string>>(e => e.ToTable(name: "UserToken"));
modelBuilder.Entity<IdentityRoleClaim<string>>(e => e.ToTable(name: "RoleClaim"));
}
}
Person entity configuration:
public class PersonConfiguration : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.ToTable("Person");
builder.HasOne(u => u.User)
.WithOne(p => p.Person)
.HasForeignKey<Person>(p => p.UserId);
}
}
The problem is when I get the person data from db the related user returns null even using the Include extension.
FYI: I've tried to load the users from db using the dbcontext but it returns null too.
I tested your code,I think there is no problem with your configuration,so,the issue may caused by your data insert.You can try to add a new Person and try to find them like bellow:
var user = new ApplicationUser
{
Email = "www.example.com"
};
var p = new Person
{
FirstName = "AA",
//...
User = user,
};
_context.Persons.Add(p);
_context.SaveChanges();
var u = _context.User.ToList();
var pe = _context.Persons.Include(c => c.User).ToList();
I'm trying to create a one-to-many map, I tried a lot ways to do that, but I just I haven't figured it out yet :/
I have 2 entites, Wallet and Transfer, I want to add in Transfer a FK WalletId, so one transfer has just one wallet, but a wallet can be related to more than one transfer.
Wallet.cs -
public class Wallet
{
public int Id { get; private set; }
public decimal Balance { get; private set; }
}
Transfer.cs -
public class Transfer
{
#region Properties
public int Id { get; private set; }
public decimal Value { get; private set; }
public DateTime? TransferDate { get; private set; }
public DateTime RegisterDate { get; private set; }
public ETransferType TransferType { get; private set; }
}
WalletMap.cs -
public class WalletMap : IEntityTypeConfiguration<Wallet>
{
public void Configure(EntityTypeBuilder<Wallet> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Balance).HasColumnType("Money").IsRequired();
}
}
TransferMap.cs -
public class TransferMap : IEntityTypeConfiguration<Transfer>
{
public void Configure(EntityTypeBuilder<Transfer> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Value).HasColumnType("Money").IsRequired();
builder.Property(x => x.TransferDate);
builder.Property(x => x.RegisterDate).IsRequired();
builder.Property(x => x.TransferType).IsRequired();
}
}
Add a foreign key and navigation property:
public class Wallet
{
public int Id { get; set; }
public decimal Balance { get; set; }
public virtual ICollection<Transfer> Transfers { get; set; } // Navigation Property
}
public class Transfer
{
pubic int Id { get; private set; }
public decimal Value { get; private set; }
public Datetime? TransferDate { get; private set; }
//.....Remaining properties
public int WalletId { get; set; } //Foreign Key
public virtual Wallet Wallet { get; set; } //Reference Navigation
}
This will add the foreign key and navigation properties needed.
//using fluent api
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Transfer>().HasOne(x => x.Wallet)
.WithMany(x => x.Transfers)
.HasForeignKey(x => x.WalletId);
modelBuilder.Entity<Wallet>.HasMany(x => x.Transfers)
.WithOne();
}
To Wallet.cs add:
public virtual ICollection<Transfer> Tranfers { get; set; }
To Transfer.cs add:
public virtual Wallet Wallet { get; set; }
To TransferMap.cs add in your Configure block:
builder.HasRequired(x => x.Wallet).WithMany(x => x.Transfers).Map(x => x.MapKey("WalletId")).WillCascadeOnDelete();
You can remove the WillCascadeOnDelete() if you have not configured a cascading delete in the database. This assumes you already have WalletId defined in your Transfers table in the database.
I'm using Entity Framework Core 2.2.6 and I have two entities Profile and Category. Each Category will have ProfileId to identify to which Profile it belongs. I'm trying to enforce uniqueness for Name and ProfileId in Category. But however my unique constraint fails.
Here is my Entities,
BaseEntity:
public class BaseEntity<TKey>
{
public TKey Id { get; set; }
public bool Active { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public string CreatedBy { get; set; }
public DateTimeOffset? ModifiedAt { get; set; }
public string ModifiedBy { get; set; }
}
Profile:
public class Profile : BaseEntity<Guid>, IAggregateRoot
{
private Profile()
{
// required by EF
}
public Profile(string brandName)
{
Guard.Against.NullOrEmpty(brandName, nameof(brandName));
BrandName = brandName;
}
public string BrandName { get; set; }
public string Caption { get; set; }
}
Category:
public class Category : BaseEntity<Guid>, IAggregateRoot
{
private Category()
{
// required by EF
}
public Category(string name)
{
Guard.Against.NullOrEmpty(name, nameof(name));
Name = name;
}
public Category(string name, string code) : this(name)
{
Guard.Against.NullOrEmpty(code, nameof(code));
Code = code;
}
public string Name { get; set; }
public string Code { get; set; }
public Guid ProfileId { get; set; }
}
Category Entity Configuration:
public class CategoryConfiguration : IEntityTypeConfiguration<Category>
{
public void Configure(EntityTypeBuilder<Category> builder)
{
builder.HasAlternateKey(c => new { c.ProfileId, c.Name });
// builder.HasIndex(c => new { c.ProfileId, c.Name }).IsUnique();
builder.Property(c => c.Name)
.IsRequired()
.HasMaxLength(50);
builder.Property(c => c.Code)
.HasMaxLength(10);
builder.HasOne<Profile>()
.WithMany()
.HasForeignKey(p => p.ProfileId)
.IsRequired();
}
}
I tried builder.HasAlternateKey(c => new { c.ProfileId, c.Name }); and builder.HasIndex(c => new { c.ProfileId, c.Name }).IsUnique();. But both doesn't seem to work. Please can you assist on where I go wrong?
First of all I have these two models to store a post in two tables one for shared data and the other contains cultured data for English and Arabic
public class Post
{
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public virtual ICollection<PostContent> Contents { get; set; }
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }
public int PostId { get; set; }
[InverseProperty("PostId")]
public virtual Post Post{ set; get; }
}
Mapping
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(p => p.Post).WithMany(p => p.Contents).HasForeignKey(p=>p.PostId);
ToTable("PostContents");
}
}
I have two questions
1- Is these models are connected properly. Is there something else I need to do ?
2- I need to select all Posts with their contents where the culture of the content 'en' for example. I used this:
var res = context.Posts.Include(p => p.Contents.Single(c => c.Culture.Equals("en")));
and have this error:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.Parameter name: path
If you know you are not going to support more than two cultures then I would just add to your Post class.
public class Post
{
public Post()
{
Contents = new List<PostContent>();
}
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public int? EnglishContentId { get;set;}
public int? ArabicContentId { get;set;}
PostContent EnglishContent {get;set;}
PostContent ArabicContent {get;set;}
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }/*This property is not required*/
}
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
HasOptional(p => p.EnglishContent).WithMany().HasForeignKey(p=>p.EnglishContentId);
HasOptional(p => p.ArabicContent).WithMany().HasForeignKey(p=>p.ArabicContentId);
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("PostContents");
}
}
The Above design will simplify your design and queries, will improve the performance alot.
But if you might have to support more cultures then you got the design and mapping right.
As far as EF 5, include does not allow filters, but I am not sure about EF 6.0
atleast you can get all posts that have english contents as follows
Add using System.Data.Entity;
var res = context.Posts.Include(p => p.Contents).Where(c => c.Contents.Any(cp=>cp.Culture.Equals("en")));
Hi i am using CTP5 to map between two entities like that:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool IsManager { get; set; }
public decimal Credit { get; set; }
public int CreditAlertCount { get; set; }
public decimal TelPrice { get; set; }
public decimal CellPrice { get; set; }
public DateTime InsertDate { get; set; }
public IList<string> PhoneList { get; set; }
public int UserTypeId { get; set; }
public virtual UserType UserType { get; set; }
}
public class UserType
{
public int Id { get; set; }
public int UserLevel { get; set; }
public string TypeDescription { get; set; }
}
//here is configurations
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("ID");
Property(c => c.InsertDate).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("INSERT_DATE");
Property(c => c.IsManager).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("IS_MANAGER");
Property(c => c.UserName).HasMaxLength(25).IsRequired().HasColumnName("USER_NAME");
Property(c => c.Password).HasMaxLength(25).IsRequired().HasColumnName("USER_PASSWORD");
Property(c => c.CellPrice).IsRequired().HasColumnName("CELL_PRICE");
Property(c => c.TelPrice).IsRequired().HasColumnName("TEL_PRICE");
Property(c => c.CreditAlertCount).IsRequired().HasColumnName("CREDIT_ALERT_COUNT");
Property(c => c.Credit).IsRequired().HasColumnName("CREDIT");
Property(c => c.UserTypeId).IsOptional().HasColumnName("USER_TYPE_ID");
/*relationship*/
HasRequired(p => p.UserType).WithMany().IsIndependent().Map(m => m.MapKey(p => p.Id, "USER_TYPE_ID"));
ToTable("CRMC_USERS", "GMATEST");
}
}
public class UserTypeConfig : EntityTypeConfiguration<UserType>
{
public UserTypeConfig()
{
/*Identity*/
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("ID");
/*simple scalars*/
Property(s => s.TypeDescription).IsRequired().HasColumnName("DESCRITPION");
Property(s => s.UserLevel).IsRequired().HasColumnName("USER_LEVEL");
ToTable("CRMC_USER_TYPES", "GMATEST");
}
}
What do i do wrong my User.UserType = null?
How to hell do i map this to work!?
I am dying here for 3 days to work it off.
I'm using DevArt Connection 6.058... some thing
Oracle 10g, C# EntityFramework 4.0
You've setup a required association between User and UserType, therefore you cannot have a User object without a UserType (i.e. User.UserType == null). To be able to do that you need to make 2 changes to your object model and fluent API:
1.Change the type of UserTypeId property to int?:
public int? UserTypeId { get; set; }
2.Remove the code from your fluent API that reads:
HasRequired(p => p.UserType).WithMany().IsIndependent().Map(m => m.MapKey...
You don't need any of those stuff. Everything will be configured by Code First based on convention for you.