I am new to MVC and EF Core and have been trying to seed some data. All my tables seed correctly except for one table and I am tired of banging my head against a wall. I have even recreated the entire application with no luck. I am certain I am missing something obvious.
My Models
public class AgreementType
{
public int AgreementTypeID { get; set; }
[Required]
[StringLength(100)]
public string Description { get; set; }
[DisplayName("Creation Date")]
public DateTime CreationDate { get; set; }
[StringLength(100)]
[DisplayName("Created By")]
public string CreatedBy { get; set; }
public Agreement Agreement { get; set; }
}
public class ContactType
{
public int ContactTypeID { get; set; }
[Required]
[StringLength(100)]
public string Description { get; set; }
[DisplayName("Creation Date")]
public DateTime CreationDate { get; set; }
[StringLength(100)]
[DisplayName("Created By")]
public string CreatedBy { get; set; }
public Contact Contact { get; set; }
}
public class Branch
{
public int BranchID { get; set; }
public string Name { get; set; }
[DisplayName("Creation Date")]
public DateTime CreationDate { get; set; }
[StringLength(100)]
[DisplayName("Created By")]
public string CreatedBy { get; set; }
public int IntermediaryID { get; set; }
public Intermediary Intermediary { get; set; }
public int RegionID { get; set; }
public virtual Region Region { get; set; }
}
My Context Class
public class BizDevHubContext : DbContext
{
public BizDevHubContext(DbContextOptions<BizDevHubContext> options) : base(options)
{
}
public DbSet<AgreementType> AgreementType { get; set; }
public DbSet<Branch> Branch { get; set; }
public DbSet<ContactType> ContactTypes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AgreementType>().ToTable("AgreementType");
modelBuilder.Entity<Branch>().ToTable("Branch");
modelBuilder.Entity<ContactType>().ToTable("ContactType");
// One to One
modelBuilder.Entity<Agreement>()
.HasOne(b => b.AgreementType)
.WithOne(i => i.Agreement)
.HasForeignKey<Agreement>(b => b.AgreementTypeID);
modelBuilder.Entity<Contact>()
.HasOne(b => b.ContactType)
.WithOne(i => i.Contact)
.HasForeignKey<Contact>(b => b.ContactTypeID);
//One to Many
modelBuilder.Entity<Region>()
.HasMany(c => c.Branch)
.WithOne(e => e.Region)
.HasForeignKey((p => p.BranchID));
// Set default dates
modelBuilder.Entity<AgreementType>()
.Property(b => b.CreationDate)
.HasDefaultValueSql("getdate()");
modelBuilder.Entity<Branch>()
.Property(b => b.CreationDate)
.HasDefaultValueSql("getdate()");
modelBuilder.Entity<ContactType>()
.Property(b => b.CreationDate)
.HasDefaultValueSql("getdate()");
}
}
My Initializer Class
if (context.Branch.Any())
{
return;
}
var branches = new Branch[]
{
new Branch{Name="Bloemfontein",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Cape Town",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Durban",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Nelspruit",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Johannesburg",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Port Shepstone",CreationDate=DateTime.Now,CreatedBy="System"},
new Branch{Name="Pretoria",CreationDate=DateTime.Now,CreatedBy="System"}
};
foreach (Branch b in branches)
{
context.Branch.Add(b);
}
context.SaveChanges();
The other tables all seed except the Branch table.
Please help!
Related
I'm creating an API with ASP .NET CORE 6.0 and I also create a Many to Many with the same table (Users), the idea is that sponsors can sponsor one or more students and students can have one or more sponsors.
These are the tables and setup I already made.
Sponsors Table
public class Sponsors
{
[Key]
public int SponsorhipId { get; set; }
public string SponsorId { get; set; } = default!;
[ForeignKey("SponsorId")]
public Users Sponsor { get; set; } = default!;
public string SponsoredId { get; set; } = default!;
[ForeignKey("SponsoredId")]
public Users Sponsored { get; set; } = default!;
}
Users Table
public class Users : IdentityUser
{
[Required]
public string IdentificationCard { get; set; } = default!;
public string FirstName { get; set; } = default!;
public string SecondName { get; set; } = default!;
public string LastName { get; set; } = default!;
public string SecondLastName { get; set; } = default!;
public DateTime DateBirth { get; set; } = default!;
public int Age { get; set; } = default!;
public Gender Gender { get; set; } = default!;
public string Photo { get; set; } = default!;
public States State { get; set; } = default!;
public ContactMethods ContactMethods { get; set; } = default!;
public string Address { get; set; } = default!;
public Grades Grades { get; set; }
public Sponsors? Sponsor1 { get; set; }
public Sponsors? Sponsor2 { get; set; }
public Comments? Comment1 { get; set; }
public Comments? Comment2 { get; set; }
public ICollection<Courses> Courses { get; set; } = default!;
public string? RefreshToken { get; set; }
public DateTime RefreshTokenExpiryTime { get; set; }
}
ModelCreating of ApplicationDbContext
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Users>().HasIndex(u => u.IdentificationCard).IsUnique();
builder.Entity<Sponsors>()
.HasOne(m => m.Sponsor)
.WithOne(t => t.Sponsor1)
.HasForeignKey<Sponsors>(m => m.SponsorId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<Sponsors>()
.HasOne(m => m.Sponsored)
.WithOne(t => t.Sponsor2)
.HasForeignKey<Sponsors>(m => m.SponsoredId)
.OnDelete(DeleteBehavior.Restrict);
}
The problem is that right now a Sponsor can only have a relationship with a Student and viceversa.
Any idea how can I modify this to allow a Sponsor have one or more relationships with a Student and viceversa?
Thanks
Starting with EF Core 5.0, many-to-many relationships are supported without explicit mapping of the join table. EntityFramework will automatically create a join table for you if you have a collection navigation property in both entities.
In your use case it would look like this (I left out some properties for the sake of simplicity).
public class Sponsor
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Student> Students { get; set; }
}
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Sponsor> Sponsors { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Sponsor> Sponsors { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=localhost;Initial Catalog=test;Integrated Security=True;TrustServerCertificate=True");
}
}
I'm new to .NET Core having been in standard ASP.NET for a long time.
I'm really struggling with creating a base class for all of my models as they all share a couple of fields.
Basically in each table I have the following columns:
Id
CreatedByUserID
CreationDateTime
UpdatedByUserID
UpdatedDateTime
DeletedByUserID
DeletionDateTime
IsDeleted
My base entity code:
public abstract class BaseEntity
{
public Guid Id { get; set; }
public DateTime CreationDate { get; set; }
public Guid CreatedByUserId { get; set; }
public DateTime UpdateDate { get; set; }
public Guid UpdateByUserId { get; set; }
public bool IsDeleted { get; set; }
public DateTime DeletedDate { get; set; }
public Guid DeletedByUserId { get; set; }
public virtual User? CreatedByUser { get; set; }
public virtual User? DeletedByUser { get; set; }
public virtual User? UpdateByUser { get; set; }
public BaseEntity()
{
Id = Guid.NewGuid();
CreationDate = DateTime.Now;
CreatedByUserId = Guid.Empty;
UpdateDate = DateTime.Now;
UpdateByUserId = Guid.Empty;
DeletedDate = new DateTime(1, 1, 1);
DeletedByUserId = Guid.Empty;
IsDeleted = false;
}
}
Part of my user code:
public class User : IdentityUser<Guid>
{
public User()
{
BaseCreatedByUsers = new HashSet<BaseEntity>();
BaseDeletedByUsers = new HashSet<BaseEntity>();
BaseUpdateByUsers = new HashSet<BaseEntity>();
}
public String FirstName { get; set; }
public String LastName { get; set; }
public int IdentificationNumber { get; set; }
public string PassportNumber { get; set; }
public bool Active { get; set; }
public String DisplayName
{
get { return FirstName + " " + LastName; }
}
public DateTime CreationDate { get; set; }
public Guid CreatedByUserId { get; set; }
public DateTime UpdateDate { get; set; }
public Guid UpdateByUserId { get; set; }
public bool IsDeleted { get; set; }
public DateTime DeletedDate { get; set; }
public Guid DeletedByUserId { get; set; }
public virtual ICollection<BaseEntity> BaseCreatedByUsers { get; set; }
public virtual ICollection<BaseEntity> BaseDeletedByUsers { get; set; }
public virtual ICollection<BaseEntity> BaseUpdateByUsers { get; set; }
}
If I do not include base entity in the ApplicationDbContext file, then I get the error:
Unable to determine the relationship represented by navigation 'User.BaseCreatedByUsers' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
But if I do include it in ApplicationDbContext:
modelBuilder.Entity<BaseEntity>(entity =>
{
entity.HasOne(d => d.CreatedByUser)
.WithMany(p => p.BaseCreatedByUsers)
.HasForeignKey(d => d.CreatedByUserId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Base_User");
entity.HasOne(d => d.DeletedByUser)
.WithMany(p => p.BaseDeletedByUsers)
.HasForeignKey(d => d.DeletedByUserId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Base_User2");
entity.HasOne(d => d.UpdateByUser)
.WithMany(p => p.BaseUpdateByUsers)
.HasForeignKey(d => d.UpdateByUserId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Base_User1");
});
Then I get this error:
Both 'ReceivingSheetTemplateDetail' and 'BaseEntity' are mapped to the table 'BaseEntity'. All the entity types in a hierarchy that don't have a discriminator must be mapped to different tables.
In this case ReceivingSheetTemplateDetail is using the base entity class.
My goal in all of this is just to take out the hassle of adding these fields to every model and to make saving easier as I can overwrite on class instead of having to do it for each.
I know I'm missing something so I would appreciate any assistance :)
How fix the problem ?
Error Message
In Entity Framework Core, I'm attempting to create a system with 4 Db Models - User,UserProfile,Review, ApplicationInfo.
I tried a lot of things, well, I don’t understand the concept of how to fix the situation, I clearly gave cascade in modelOnCreate
Model User.
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Token { get; set; }
public UserProfile UserProfile { get; set; }
}
Model UserProfile
public class UserProfile
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public IEnumerable<ApplicationInfo> ApplicationInfos { get; set; }
public IEnumerable<Review> Reviews { get; set; }
}
Model Review
public class Review
{
public int Id { get; set; }
public string Text { get; set; }
public string ImageLocation { get; set; }
public string Version { get; set; }
public string Rate { get; set; }
//One(AppInfo) to Many(Review)
public int ApplicationInfoId { get; set; }
public ApplicationInfo ApplicationInfo { get; set; }
//One(UserProfile) to Many(Review)
public int UserProfileId { get; set; }
[Required]
public UserProfile UserProfile { get; set; }
}
Model ApplicationInfo
public class ApplicationInfo
{
[Key]
public int Id { get; set; }
[Required]
public string ApplicationId { get; set; }
[Required]
public string AppName { get; set; }
[Required]
public string PublisherEmail { get; set; }
public string Genre { get; set; }
public string Price { get; set; }
public string Description { get; set; }
public string Version { get; set; }
public string IconUrl { get; set; }
public string PublisherName { get; set; }
public string AllRatingCount { get; set; }
public string AllRating { get; set; }
//One(UserProfile) To Many(AppInfo)
public int UserProfileId { get; set; }
public UserProfile UserProfile { get; set; }
public IEnumerable<Review> Reviews { get; set; }
}
OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().
HasOne<UserProfile>(s=>s.UserProfile).
WithOne(s=>s.User).
HasForeignKey<UserProfile>(s=>s.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationInfo>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.ApplicationInfos)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Review>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.Reviews)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Review>()
.HasOne<ApplicationInfo>(s => s.ApplicationInfo)
.WithMany(s => s.Reviews)
.OnDelete(DeleteBehavior.Cascade);
}
Just Change OnModelCreating and change OnDelete for Review and AppInfo to Restrict(No Action)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOne<UserProfile>(s => s.Profile)
.WithOne(s => s.User)
.HasForeignKey<UserProfile>(s=>s.UserId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationInfo>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.ApplicationInfos)
.HasForeignKey(s => s.UserProfileId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Review>()
.HasOne<UserProfile>(s => s.UserProfile)
.WithMany(s => s.Reviews)
.HasForeignKey(s => s.UserProfileId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Review>()
.HasOne<ApplicationInfo>(s => s.ApplicationInfo)
.WithMany(s => s.Reviews)
.HasForeignKey(s => s.ApplicationInfoId)
.OnDelete(DeleteBehavior.Restrict);
base.OnModelCreating(modelBuilder);
}
I am trying to accomplish many-to-many relationship using code-first EF with ASP.NET Identity table at one side. The join table is not generated in the DB, though. What am I missing? Here are my model classes:
AppUser.cs:
public class AppUser : IdentityUser
{
public AppUser()
{
Notes = new HashSet<Note>();
}
public DateTime? LastSuccessfulLoginDate { get; set; }
public virtual ICollection<Note> Notes { get; set; }
}
and
Note.cs:
public class Note
{
public Note() {
NoteAssignedToUsers = new HashSet<AppUser>();
}
[Key]
public int NoteID { get; set; }
[Required]
public int FileID { get; set; }
[Required]
public string Content { get; set; }
[Required]
public Importance? Importance { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:dd.MM.yyyy HH.mm}")]
public DateTime AddedOn { get; set; }
[Required]
public string CreatorID { get; set; }
[ForeignKey("FileID")]
public virtual OAFile OAFile { get; set; }
[ForeignKey("CreatorID")]
public virtual AppUser CreatedBy { get; set; }
public virtual ICollection<AppUser> NoteAssignedToUsers { get; set; }
}
In your dbcontext you can configure:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Note>()
.HasMany<AppUser>(s => s.NoteAssignedToUsers )
.WithMany(c => c.Notes)
.Map(cs =>
{
cs.MapLeftKey("AppUserId");
cs.MapRightKey("NoteId");
cs.ToTable("AppUsersNotes");
});
}
This is a followup-question of this question, where i had a similar problem. But this is solved now by default foreign key convention.
My problem now is (in short), that my migrations generates
int ReferencedEntityID;
int ReferencedEntity_ReferencedEntityID;
where one is an integer property in my model and the other one is a virtual property.
My migrations generates this:
"dbo.Contracts",
c => new
{
ContractId = c.Int(nullable: false, identity: true),
PricePerUnit = c.Double(nullable: false),
Unit = c.Int(nullable: false),
Currency = c.Int(nullable: false),
ClientId = c.Int(nullable: false),
CompanyId = c.Int(nullable: false),
ArticleId = c.Int(nullable: false),
Client_ClientId = c.Int(),
Article_ArticleId = c.Int(),
})
As you can see, Client & Article are referenced twice.
Here are my models
public class Client {
public Client() { }
[Key]
public int ClientId { get; set; }
public string Number { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string Memo { get; set; }
public bool isMerchant { get; set; }
public string Name
{
get
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
public int? MerchantReferenceId { get; set; }
public virtual Client MerchantReference { get; set; }
[Required]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Article {
public Article() { }
[Key]
public int ArticleId { get; set; }
[Required]
public string Code { get; set; }
public string Name { get; set; }
public bool TrackStock { get; set; }
public int CurrentStock { get; set; }
public double? Price { get; set; }
[Required]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
[Required]
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Contract> Contracts { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Contract {
public Contract() { }
[Key]
public int ContractId { get; set; }
public double PricePerUnit { get; set; }
public int Unit { get; set; }
public int Currency { get; set; }
[Required]
public int ClientId { get; set; }
// [ForeignKey("ClientId")]
public virtual Client Client { get; set; }
[Required]
public int CompanyId { get; set; }
//[ForeignKey("CompanyID")]
public virtual Company Company { get; set; }
[Required]
public int ArticleId { get; set; }
// [ForeignKey("ArticleId")]
public virtual Article Article { get; set; }
}
Here is my OnModelCreating()
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// modelBuilder.Entity<Contract>().HasRequired(bm => bm.Company).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Contract>().HasRequired(bm => bm.Article).WithMany().HasForeignKey(dl => dl.ArticleId).WillCascadeOnDelete(false);//.Map( dl => dl.MapKey("ArticleId"))
modelBuilder.Entity<Contract>().HasRequired(bm => bm.Client).WithMany().HasForeignKey(dl => dl.ClientId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("ClientId"))
modelBuilder.Entity<Article>().HasRequired(bm => bm.Company).WithMany().HasForeignKey(dl => dl.CompanyId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("CompanyId"))
modelBuilder.Entity<Measurement>().HasRequired(bm => bm.Company).WithMany().HasForeignKey(dl => dl.CompanyId).WillCascadeOnDelete(false); //.Map(dl => dl.MapKey("CompanyId"))
modelBuilder.Entity<Order>().HasRequired(bm => bm.Client).WithMany().HasForeignKey(dl => dl.ClientId).WillCascadeOnDelete(false); //.Map(dl => dl.MapKey("ClientId"))
modelBuilder.Entity<Order>().HasRequired(bm => bm.Article).WithMany().HasForeignKey(dl => dl.ArticleId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("ArticleId"))
modelBuilder.Entity<IncomingMeasurement>().HasRequired(bm => bm.client).WithMany().HasForeignKey(dl => dl.ClientId).WillCascadeOnDelete(false);//.Map(dl => dl.MapKey("ClientId"))
modelBuilder.Entity<Client>().HasOptional(c => c.MerchantReference).WithMany().HasForeignKey(dl => dl.MerchantReferenceId); //.Map(dl => dl.MapKey("MerchantReferenceId"))
//Required fields
base.OnModelCreating(modelBuilder);
}
What do i have to do, to create them both:
Required
Both in one property in my db-schema (as it should)
It is OK, even recommended, to have primitive FK properties (like ArticleId) accompanying the "real" references. In EF this is called a foreign key association as opposed to an independent association where there is only a reference (like Article.Company).
So you can keep your model the way it is. You just have to specify the foreign keys.
I tried with a few classes in the model of your previous question and this produced the desired results:
modelBuilder.Entity<Article>().HasMany(a => a.Contracts)
.WithRequired(c => c.Article)
.HasForeignKey(c => c.ArticleID).WillCascadeOnDelete(false);
modelBuilder.Entity<Client>().HasMany(c => c.Contracts)
.WithRequired(c => c.Client)
.HasForeignKey(c => c.ClientID).WillCascadeOnDelete(false);
modelBuilder.Entity<Company>().HasMany(c => c.Articles)
.WithRequired(a => a.Company)
.HasForeignKey(c => c.CompanyID).WillCascadeOnDelete(false);
Note that I turned around the definitions because when I did it your way, but with HasForeignKey it still duplicated the FK fields. I'm not sure why.