i am using asp.net mvc5 app. I got generalize entities for following database structure
that is diploma_markscheme and non_diploma_MarkScheme...
now i have model class as these table as
ElementMarkScheme
[Table("ElementMarkScheme")]
public class ElementMarkScheme : IElementMarkScheme
{
public ElementMarkScheme()
{
}
[Key]
public int ElementMarkSchemeID { get; set; }
public int QualificationElementID { get; set; }
public int MarkSchemeID { get; set; }
public DiplomaMarkScheme DiplomaMarkScheme { get; set; }
public MarkScheme MarkScheme { get; set; }
public Non_DiplomaMarkScheme Non_DiplomaMarkScheme { get; set; }
public QualificationElement QualificationElement { get; set; }
}
Diploma_markscheme
[Table("DiplomaMarkScheme")]
public class DiplomaMarkScheme : IDiplomaMarkScheme
{
public DiplomaMarkScheme()
{
}
[Key]
public int ElementMarkSchemeID { get; set; }
[Required(ErrorMessage = "Required Pass Mark")]
[Display(Name = "Pass Mark")]
public int PassMark { get; set; }
[Display(Name = "Available Mark")]
public int AvailableMark { get; set; }
public ElementMarkScheme ElementMarkScheme { get; set; }
}
Non_Diploma_MarkScheme
[Table("Non_DiplomaMarkScheme")]
public class Non_DiplomaMarkScheme : INon_DiplomaMarkScheme
{
public Non_DiplomaMarkScheme()
{
}
[Key]
public int ElementMarkSchemeID { get; set; }
[Required(ErrorMessage = "Required Pass Mark")]
[Display(Name = "Pass Mark")]
public int PassMark { get; set; }
[Required(ErrorMessage = "Required Merit Mark")]
[Display(Name = "Merit Mark")]
public int MeritMark { get; set; }
[Required(ErrorMessage = "Required Distinction Mark")]
[Display(Name = "Distinction Mark")]
public int DistinctionMark { get; set; }
[Display(Name = "Available Mark")]
public int AvailableMark { get; set; }
public ElementMarkScheme ElementMarkScheme { get; set; }
}
at run time I get following Error I believe I am missing something or doing wrong in model class....
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Unable to determine the principal end of an association between the types 'LEO.DAL.Model.ElementMarkScheme' and 'LEO.DAL.Model.DiplomaMarkScheme'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Honestly, your database is kind of odd to me. Why is your primary key for DiplomaMarkScheme a foreign key to ElementMarkSchemeID? Anyway, it appears as though you are trying to achieve a one-to-one relationship, so please consult this document http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx wherein the proper models are configured like so:
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public int BillingAddressId { get; set; }
public int DeliveryAddressId { get; set; }
public Address BillingAddress { get; set; }
public Address DeliveryAddress { get; set; }
}
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
}
And add this override to your DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(a => a.BillingAddress)
.WithMany()
.HasForeignKey(u => u.BillingAddressId);
modelBuilder.Entity<User>()
.HasRequired(a => a.DeliveryAddress)
.WithMany()
.HasForeignKey(u => u.DeliveryAddressId);
}
If you are trying, instead, to implement a different type of relationship, let me know and I'll try to help. You may want to consider restructuring your database in a more logical way, as I can't seem to understand the way you have it setup currently.
Related
I am trying to add a migration using the code-first workflow, but it seems that the EF core does not recognize my many-to-many relationship between "TeamMember" and "Step" classes.
My "step" class:
public class Step {
public int Id { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
[Required]
[StringLength(30, MinimumLength = 3, ErrorMessage = "The step name must have between 3 and 30 characters!")]
public string Name { get; set; }
[Required]
[StringLength(500, MinimumLength = 10, ErrorMessage = "The step description must have between 10 and 500 characters!")]
public string Description { get; set; }
public ICollection<TeamMember> TeamMembers{ get; set; }
public Step() {
}
}
My "TeamMember" class:
public class TeamMember {
public int Id { get; set; }
public int UserId{ get; set; }
public User User{ get; set; }
public string Role { get; set; }
public int TeamId { get; set; }
public Team Team{ get; set; }
public ICollection<Step> Steps{ get; set; }
public TeamMember() {
}
}
My Context class:
public class ProjectContext : IdentityDbContext<User> {
public ProjectContext(DbContextOptions<ProjectContext> options)
: base(options) {
}
public DbSet<User> User { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<Team> Teams { get; set; }
public DbSet<Step> Steps { get; set; }
public DbSet<TeamMember> TeamMembers{ get; set; }
protected override void OnModelCreating(ModelBuilder builder) {
builder.Entity<User>().HasKey(m => m.Id);
builder.Entity<Project>().HasKey(m => m.Id);
builder.Entity<Team>().HasKey(m => m.Id);
builder.Entity<Step>().HasKey(m => m.Id);
builder.Entity<TeamMember>().HasKey(m => m.Id);
base.OnModelCreating(builder);
}
}
Error string: Unable to determine the relationship represented by
navigation property 'Step.TeamMembers' of type
'ICollection'. Either manually configure the relationship,
or ignore this property using the '[NotMapped]' attribute or by using
'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
As you can see I have an ICollection in each one of the classes and I can't figure out the error.
EF Core doesn't support many to many relationships by default.
You have to create a join table in order to achieve the relationship:
You can easily configure it like this:
Step:
public class Step {
public ICollection<TeamMemberStep> TeamMemberStep { get; set; }
}
Team Member:
public class TeamMember {
public ICollection<TeamMemberStep> TeamMemberStep { get; set; }
}
Join table:
public class TeamMemberStep
{
public string TeamMemberId { get; set; }
public virtual TeamMember TeamMember { get; set; }
public int StepId { get; set; }
public virtual Step Step { get; set; }
}
DbContext:
modelBuilder.Entity<TeamMemberStep>()
.HasKey(aup => new { aup.TeamMemberId, aup.StepId });
public DbSet<TeamMemberStep> TeamMemberSteps { get; set; }
Now you query the TeamMemberStep table to get your data from either sides.
I have a problem with my entities. I'm using EF code-first migrations and the migrations are failing with this error:
Introducing FOREIGN KEY constraint 'FK_OrdersChildsProducts_Orders_OrderId' on table 'OrdersChildsProducts' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Here's my PersonJceProfile entity :
[Table("PersonJceProfiles")]
public class PersonJceProfile : BaseEntity
{
[ForeignKey("Ces")]
public int? CeId { get; set; }
public ICollection<Child> Children { get; set; }
public Order Order { get; set; }
public PersonJceProfile()
{
Children = new List<Child>();
}
}
Here's my Order entity :
[Table("Orders")]
public class Order : BaseEntity
{
[Key]
public int Id { get; set; }
//ForeignKey
[Required]
[ForeignKey("PersonJceProfiles")]
public int PersonJceProfileId { get; set; }
[Required]
public int OrderStatus { get; set; }
[Required]
public bool IsSecurePayment { get; set; }
public int LeftToPayPersonOrder { get; set; }
public string Delivery { get; set; }
public ICollection<OrderChildProduct> OrderChildProduct { get; set; }
public Order()
{
OrderChildProduct = new Collection<OrderChildProduct>();
}
}
Here's my Child entity :
[Table("Childrens")]
public class Child :BaseEntity
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public DateTime? BirthDate { get; set; }
[Required]
public string Gender { get; set; }
public bool? IsActif { get; set; }
public decimal AmountParticipationCe { get; set; }
public bool? IsRegrouper { get; set; }
[Required]
[ForeignKey("PersonJceProfiles")]
public int PersonJceProfileId { get; set; }
}
Here's my Product Entity
public class Product : Good
{
public string File { get; set; }
public bool? IsDisplayedOnJCE { get; set; }
public bool? IsBasicProduct { get; set; }
public int? PintelSheetId { get; set; }
public int OriginId { get; set; }
[Required]
[ForeignKey("Suppliers")]
public int SupplierId { get; set; }
}
Here's my OrderChildProduct entity :
[Table("OrdersChildsProducts")]
public class OrderChildProduct
{
public int OrderId { get; set; }
public int ChildId { get; set; }
public int ProductId { get; set; }
public int LeftToPayChildOrder { get; set; }
public Order Order { get; set; }
public Child Child { get; set; }
public Product Product { get; set; }
}
Here's my context :
modelBuilder.Entity<OrderChildProduct>().HasKey(ccp => new { ccp.OrderId, ccp.ChildId, ccp.ProductId });
I suppose i do destro a relationship like this :
modelBuilder.Entity<Entity>()
.HasRequired(c => c.ForeignKey)
.WithMany()
.WillCascadeOnDelete(false);
but I can't see between which. Because
When I delete PersonJceProfiles : Order must be deleted - OrderChildProduct must be deleted - Child must be deleted
When I delete Order : OrderChildProduct must be deleted
When I delete Order childProduct : nothing must be deleted expect himself
What am I doing wrong? Thanks
The error is self described. You must configure the relationships in the OrderChildProduct table like:
entity.HasOne(p => p.Order)
.WithMany(p => p.OrderChildProduct)
.HasForeignKey(p => p.OrderId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
You can use either Restrict or Cascade depending on your requirements.
Also the other 2 relationships must be defined as well.
This is not an error. This is more like a warning... the way EF tells you that it doesn't fully understand the relationships and you must configure them manually.
I've been for a while trying to find out why the Include clause is not loading the related collection: I have two classes with a one-to-many relationship:
public class AgencyNote : IAutId
{
[Key]
public int aut_id { get; set; }
public string Comment { get; set; }
[DisplayName("Note Created Date")]
public DateTime NoteDate { get; set; }
[DisplayName("Contact Date")]
public DateTime ContactDate { get; set; }
[ForeignKey("tbl_agency")]
public int AgencyId { get; set; }
[DisplayName("User")]
public string RipsUser { get; set; }
public virtual ICollection<AgencyNoteAttachment> AgencyNoteAttachments { get; set; }
public virtual tbl_agency tbl_agency { get; set; }
}
and
public class AgencyNoteAttachment
{
[Key]
public int aut_id { get; set; }
public string Url { get; set; }
public string FileName { get; set; }
public int AgencyNoteId { get; set; }
[NotMapped]
[ForeignKey("AgencyNoteId")]
public virtual AgencyNote AgencyNote { get; set; }
}
Context class:
public DbSet<AgencyNote> AgencyNotes { get; set; }
public DbSet<AgencyNoteAttachment> AgencyNoteAttachments { get; set; }
This is the action where I'm using the Include clause:
private IQueryable<AgencyNote> GetNotes(int agencyId)
{
return _ctx.AgencyNotes
.Include(a => a.tbl_agency)
.Include(a => a.AgencyNoteAttachments)
.OrderByDescending(f => f.NoteDate)
.Where(x => x.AgencyId == agencyId);
}
I'm getting AgencyNotesAttachments always null from this action even if I know it's not null, what's going on? Any question let me know...
If you add just the navigation properties between the related entities, then EF will create the FK column for you in the AgencyNoteAttachment table. Now, EF by convention can interpret AgencyNoteId is the FK of that relationship, but is good idea do that explicitly as you already have in your model or using ForeignKey attribute on FK property:
public class AgencyNoteAttachment
{
[Key]
public int aut_id { get; set; }
public string Url { get; set; }
public string FileName { get; set; }
[ForeignKey("AgencyNote")]
public int AgencyNoteId { get; set; }
public virtual AgencyNote AgencyNote { get; set; }
}
If you want to learn more about conventions, take a look this link
I am writing an application which uses inheritance and I'm trying to map this to a SQL Server database with TPT structure.
However, for some reason EF generates duplicate foreign keys in both the superclass and subclass tables.
I have these classes:
public abstract class Answer
{
public int AnswerId { get; set; }
[Required]
[MaxLength(250, ErrorMessage = "The answer cannot contain more than 250 characters")]
public String Text { get; set; }
[Required]
[MaxLength(1500, ErrorMessage = "The description cannot contain more than 1500 characters")]
public String Description { get; set; }
public User User { get; set; }
}
public class AgendaAnswer : Answer
{
[Required]
public AgendaModule AgendaModule { get; set; }
}
public class SolutionAnswer : Answer
{
[Required]
public SolutionModule SolutionModule { get; set; }
}
public abstract class Module
{
// for some reason EF doesn't recognize this as primary key
public int ModuleId { get; set; }
[Required]
public String Question { get; set; }
public String Description { get; set; }
[Required]
public DateTime StartTime { get; set; }
[Required]
public DateTime EndTime { get; set; }
public virtual IList<Tag> Tags { get; set; }
}
public class AgendaModule : Module
{
public IList<AgendaAnswer> AgendaAnswers { get; set; }
}
public class SolutionModule : Module
{
public IList<SolutionAnswer> SolutionAnswers { get; set; }
}
public class User
{
public int UserId { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public int Zip { get; set; }
public bool Active { get; set; }
public virtual IList<AgendaAnswer> AgendaAnswers { get; set; }
public virtual IList<SolutionAnswer> SolutionAnswers { get; set; }
}
And this is the content of my DbContext class:
public DbSet<AgendaModule> AgendaModules { get; set; }
public DbSet<SolutionModule> SolutionModules { get; set; }
public DbSet<AgendaAnswer> AgendaAnswers { get; set; }
public DbSet<SolutionAnswer> SolutionAnswers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Module>().HasKey(m => m.ModuleId);
modelBuilder.Entity<Answer>().HasKey(a => a.AnswerId);
modelBuilder.Entity<AgendaAnswer>().Map(m =>
{
m.ToTable("AgendaAnswers");
});
modelBuilder.Entity<SolutionAnswer>().Map(m =>
{
m.ToTable("SolutionAnswers");
});
}
When I run my application, EF creates the tables how I want them (TPT), but it duplicates the foreign key to users in each of them (see picture).
Thanks in advance
As noted in my comment above, the solution is to remove the AgendaAnswers and SolutionAnswers properties from the User class.
If you want to keep those collections in the User class, you might have to remove the User and UserId properties from the Answer class and instead duplicate them in the AgendaAnswer and SolutionAnswer classes. See this SO question for more information.
I just started playing around with the CTP4 and Code-First. I have the following setup for a possible dating site:
public class User
{
[Key]
public int Id { get; set; }
[Required]
public string LoginName { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
public string Street { get; set; }
[Required]
public string Zip { get; set; }
[Required]
public string City { get; set; }
[Required]
public bool Gender { get; set; }
[Required]
public int SoughtGender { get; set; }
[Required]
public string Password { get; set; }
[Required]
public double Latitude { get; set; }
[Required]
public double Longitude { get; set; }
}
public class Vote
{
[Key]
public int ID { get; set; }
[Required]
public User Voter { get; set; }
[Required]
public User TargetUser { get; set; }
[Required]
public int Decision { get; set; }
[Required]
public DateTime Timestamp { get; set; }
}
public class MySQLContext : DbContext
{
public MySQLContext (string constring)
: base(constring)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Vote> Votes { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
modelBuilder.Entity<Vote>().HasRequired(b => b.Voter).WithMany();
modelBuilder.Entity<Vote>().HasRequired(b => b.TargetUser).WithMany();
base.OnModelCreating(modelBuilder);
}
}
Now the framework does a nice job of creating the DB with all the proper keys. Now I inserted some dummy data and fired up the following query:
public override IEnumerable<Domain.Vote> FindVotes(Domain.User user)
{
var query = from v in context.Votes where v.Voter.Id == user.Id select v;
return from v in query.AsEnumerable() select v;
}
The query does return the proper Vote entities but the two User properties of the Vote object are Null. Shouldnt the framework populate those properties with the foreign keys of the users referenced in the Vote table?
Let me give you some background on EF so you can understand how this works. EF from day 1 only supported explicit load like one below
Customer.Orders.Load();
Hmm, the feedback was not welcomed by the community and developers wanted lazy loading. To support Lazy Loading EF team said you must mark your navigation property as virtual. So at runtime, Ef creates a proxy object that derives from your entity and overrides the virtual property. Below is an example of such code.
public class Customer
{
public string Name{get;set;}
public virtual ICollection<Order> Orders{get;set;}
}
At runtime there is a proxy that implements IEntityWithChangeTracker and concrete type of the collection is an entitycollection which has been around since version 1.
public class CustomerProxy:Customer,IEntityWithChangeTracker
{
private ICollection<Order> orders;
public override ICollection<Order> Orders
{
if(orders == null)
{
orders = new EntityCollection<Order>();
orders.Load();
}
return orders;
}
}
change your class to the follow
public class Vote {
[Key]
public int ID { get; set; }
[Required]
public virtual User Voter { get; set; }
[Required]
public virtual User TargetUser { get; set; }
[Required]
public int Decision { get; set; }
[Required]
public DateTime Timestamp { get; set; }
}
Notice I've added virtual to the Voter && TargetUser properties and you should be good to go.