EntityFramework 1 to 0, 1 or many relationship - c#

I am struggling to create a relationship in Entity framework between three tables..
I am developing WPF application using C#, Sqlite 3.0 and Entity Framework 6.
I have Following tables(classes):
Investor
Investment
ImageStore
I want to create a model so that Investor and Investment (and future other classes) can store either 0, 1 or Many images.. (as it is obvious from class names that Investor can have only one profile image and Investment class can have multiple images)
My ImageStore class looks something like this:
public class ImageStore : PropertyChangedNotification
{
[Key]
public int ImageStoreId { get; set; }
[Required]
public string ImageFile { get; set; }
[Required]
public Byte[] ImageBlob { get; set; }
public string FileName { get; set; }
[Required]
public int FileSize { get; set; }
//public virtual ImageData ImageData { get; set; }
}
In order to create 1 to 0,1 or Many relationship, I created one more intermediate table called: ImageData as seen below (I don't know whether it is really a good approach but that is only what I can think of right now..)
public class ImageData : PropertyChangedNotification
{
[Key]
public int ImageDataId { get; set; }
[ForeignKey("Investment")]
public long? InvestmentId { get; set; }
[ForeignKey("Investor")]
public long? InvestorId { get; set; }
[ForeignKey("ImageStore")]
public int ImageStoreId { get; set; }
public virtual ImageStore ImageStore { get; set; }
public virtual Investment Investment { get; set; }
public virtual Investor Investor { get; set; }
}
My Investor class looks like this:
public class Investor : PropertyChangedNotification
{
[Key]
public long InvestorId { get; set; }
[NotMapped]
[ForeignKey("ImageData")]
public List<int> ImageDataList { get; set; }
public virtual ICollection<ImageData> ImageDataCollection { get; set; }
public virtual ICollection<Investment> Investments { get; set; }
}
My Investment Class Looks like this:
public class Investment : PropertyChangedNotification
{
[Key]
public long InvestmentId { get; set; }
[ForeignKey("Investor")]
[Required]
public long FirstInvestorId { get; set; }
[NotMapped]
[ForeignKey("ImageData")]
public List<int> ImageDataList { get; set; }
public virtual ICollection<ImageData> ImageDataCollection { get; set; }
public virtual Investor Investor { get; set; }
[NotMapped]
[Required (ErrorMessage = "First Investor is Required")]
public Investor FirstInvestor { get; set; }
}
This is my related fluent configuration:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// MyData Database does not pluralize table names
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Investor>().HasOptional(s => s.ImageDataCollection);
modelBuilder.Entity<Investment>().HasOptional(s => s.ImageDataCollection);
//modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
}
When I start debugging the application, I get the following error:
ImageData_Investment_Source: : Multiplicity is not valid in Role 'ImageData_Investment_Source' in relationship 'ImageData_Investment'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'
ImageData_Investor_Source: : Multiplicity is not valid in Role 'ImageData_Investor_Source' in relationship 'ImageData_Investor'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'
Can someone please suggest me either a solution to this problem and/or optimal approach to achieve what I need, I really will appreciate.
Thanks

The fluent configuration
modelBuilder.Entity<Investor>().HasOptional(s => s.ImageDataCollection);
modelBuilder.Entity<Investment>().HasOptional(s => s.ImageDataCollection);
is incomplete.
Since the you already have the necessary data annotations and navigation / FK properties, you can simply remove it. Or if you want to provide fluent configuration (which I personally prefer because it allows you to specify everything explicitly and not rely on conventions and specifically for relationships, not so intuitive ForegnKey and InverseProperty data annotations), then you should make sure it reflects exactly the presense/absence of the navigation and FK properties in the involved entities.
The correct fluent configuration reflecting your model so far is like this:
modelBuilder.Entity<Investor>()
.HasMany(e => e.ImageDataCollection)
.WithOptional(e => e.Investor)
.HasForeignKey(e => e.InvestorId);
modelBuilder.Entity<Investment>()
.HasMany(e => e.ImageDataCollection)
.WithOptional(e => e.Investment)
.HasForeignKey(e => e.InvestmentId);

Related

Is This Data Model Meeting My Requirements?

I'm creating a Razor Pages application that resembles a "hockey league". As I'm still grasping the concept of foreign/primary keys, I'm not quite sure if I'm setting up my data model correctly. After attempting to update my database after a migration I am getting the following error that has led me to believe I didn't set them up correctly:
Introducing FOREIGN KEY constraint 'FK_Team_Division_DivisionID' on table 'Team' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Based on these three entities, am I clearly doing something wrong?
public class Team
{
public int ID { get; set; }
public int? CoachID { get; set; }
public int? DivisionID { get; set; }
public int? ConferenceID { get; set; }
[Display(Name = "Team")]
public string TeamName { get; set; }
[Display(Name = "Location")]
public string TeamLocation { get; set; }
public Coach Coach { get; set; }
public Division Division { get; set; }
public Conference Conference { get; set; }
public ICollection<Player> Players { get; set; }
}
public class Conference
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
[Display(Name = "Conference")]
public string ConferenceName { get; set; }
public ICollection<Division> Divisions { get; set; }
public ICollection<Team> Teams { get; set; }
}
public class Division
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
public int ConferenceID { get; set; }
[Display(Name = "Division")]
public string DivisionName { get; set; }
public Conference Conference { get; set; }
public ICollection<Team> Teams { get; set; }
}
My idea is that every Team will belong to a Conference and a Division. There can be many Teams in a Division, and many Divisions in a Conference.
The problem you're running into is that SQL server doesn't know how to handle a Delete of an item that has multiple parents. You'll need to help it out a bit. Choose a route that you want Team to be deleted on, for instance:
Conference --> Division --> Team
Then you must determine the routes that you don't want it to be deleted on, for instance:
Conference --> Team
Once you've decided which routes won't be used for deletion, you can specify it in the OnModelCreating(DbModelBuilder modelBuilder) method for your context
modelBuilder.Entity<Conference>()
.HasRequired(x => x.Team)
.WithMany()
.WillCascadeOnDelete(false);
EDIT
Pretty sure I got that backwards above, try this:
modelBuilder.Entity<Team>()
.HasRequired(x => x.Conference)
.WithMany()
.WillCascadeOnDelete(false);

Multiple zero or one to one relationships EF 6 Code First

I have a contact table
public class Contact : EntityBase
{
public int TrivialContactProperty { get; set; }
...
public virtual FiContact FiContact { get; set; }
public virtual PuContact PuContact { get; set; }
public virtual TrContact TrContact { get; set; }
}
and then a FiContact, PuContact, and TrContact table.
public class FiContact : EntityBase
{
public int TrivialFiProperty { get; set; }
...
public virtual Contact Contact { get; set; }
}
public class PuContact : EntityBase
{
public int TrivialPuProperty { get; set; }
...
public virtual Contact Contact { get; set; }
}
public class TrContact : EntityBase
{
public int TrivialTRProperty { get; set; }
...
public virtual Contact Contact { get; set; }
}
The contact table should have a zero or one relationship with all three other tables. So a contact can exist without any of the other three, or it can be related to one or two or all three of them.
Using fluent API I tried to configure this, after doing some research, and I came up with:
modelBuilder.Entity<FiContact>()
.HasRequired(r => r.Contact)
.WithOptional(o => o.FiContact);
modelBuilder.Entity<PuContact>()
.HasRequired(r => r.Contact)
.WithOptional(o => o.PuContact);
modelBuilder.Entity<TrContact>()
.HasRequired(r => r.Contact)
.WithOptional(o => o.TrContact);
But I am still getting the following error when I try to add a migration for this change:
FiContact_Contact_Source: : Multiplicity is not valid in Role 'FiContact_Contact_Source' in relationship 'FiContact_Contact'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '(asterisk symbol here)'.
PuContact_Contact_Source: : Multiplicity is not valid in Role 'PuContact_Contact_Source' in relationship 'PuContact_Contact'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '(asterisk symbol here)'.
TrContact_Contact_Source: : Multiplicity is not valid in Role 'TrContact_Contact_Source' in relationship 'TrContact_Contact'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '(asterisk symbol here)'.
From more research I saw that the primary key on the dependent entity is supposed to also be the foreign key? The only problem is that all of my entities inherit from a class "EntityBase" which defines common fields in all entities, including the primary key:
public abstract class EntityBase : IEntity
{
[Key]
public int Id { get; set;}
public bool IsActive { get; set; }
public DateTime CreatedOnDate { get; set; }
public int? CreatedByUserId { get; set; }
[ForeignKey("CreatedByUserId")]
public User CreatedByUser { get; set; }
public DateTime LastUpdatedOnDate { get; set; }
public int? LastUpdatedByUserId { get; set; }
[ForeignKey("LastUpdatedByUserId")]
public User LastUpdatedByUser { get; set; }
}
Is there a way to make this kind of table/entity relationship work with EF 6 Code First? Any help getting this type of relationship to work would be much appreciated.

EF Code first 1 to 1 relationship error

I have two classes:
Main class:
public class CCourseDetailModel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CourseDetailId { get; set; }
[ForeignKey("CourseOutcomes")]
public int CourseOutcomesId { get; set; }
public virtual CACourseOutcomesModel CourseOutcomes { get; set; }
}
Dependent class:
public class CACourseOutcomesModel
{
[Key, ForeignKey("CourseDetail")]
public int CourseOutcomesId { get; set; }
[Required]
public virtual CCourseDetailModel CourseDetail { get; set; }
}
I have 10 or so similar classes, with 1 to 1 relationships that work fine. This is the only one giving me the following error:
CACourseOutcomesModel_CourseDetail_Target: : Multiplicity is not valid in Role 'CACourseOutcomesModel_CourseDetail_Target' in
relationship 'CACourseOutcomesModel_CourseDetail'. Because the
Dependent Role properties are not the key properties, the upper bound
of the multiplicity of the Dependent Role must be ''.*
Any idea where I'm going wrong? Need a fresh set of eyes please. Thanks!
In a one to one relationship, one end must be principal and the another one must be dependent, so you can't have a FK property in both sides. Remove the FK property in the principal (CCourseDetailModel) and in CACourseOutcomesModel you don't need to use Required attribute. Using ForeignKey attribute you already are telling to EF who is the dependent end.
In Fluent Api would be:
modelBuilder.Entity<CACourseOutcomesModel>()
.HasRequired(p => p.CourseDetail)
.WithOptional(p => p.CourseOutcomes);
So your model should be this way:
public class CCourseDetailModel
{
[Key]
//[DatabaseGenerated(DatabaseGeneratedOption.Identity)] don't need this, it's the configuration by default.
public int CourseDetailId { get; set; }
public virtual CACourseOutcomesModel CourseOutcomes { get; set; }
}
public class CACourseOutcomesModel
{
[Key, ForeignKey("CourseDetail")]
public int CourseOutcomesId { get; set; }
public virtual CCourseDetailModel CourseDetail { get; set; }
}

Fluent API - Marking ID as FK and PK

I've tried to work this out myself, but i'm not a database guy and it has confused me heavily.
I have two classes (cut down for ease of reading)
public class Project : IModel
{
//Internal DB Id
[DataMember]
public int ProjectID { get; set; }
//Project No - Auto Generated at service level
[DataMember]
public string ProjectNo { get; set; }
}
public class ProjectExtra : IModel
{
[DataMember]
public int ProjectID { get; set; }
[DataMember]
public string Description { get; set; }
}
These are used to generate my database using EF6, however I need to mark the ProjectID in ProjectExtra as both the PK and the FK, and i cannot work out how to do that with Fluent API.
ProjectExtra is optional, it may exist once for a project, or not at all.
Can someone perhaps point me in the right direction, I have already setup a couple of others in the method
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Project>().HasMany(p => p.Managers).WithMany();
modelBuilder.Entity<Project>().HasMany(p => p.Clients).WithMany();
}
When we are mapping a one-to-one association with fluent API, we don't need to specify the foreign key. Since EF only supports one-to-one associations on primary keys, it will automatically create the relationship in the database on the primary keys.
First you should add a navigation property Project:
public class ProjectExtra : IModel
{
[DataMember]
public int ProjectID { get; set; }
[DataMember]
public string Description { get; set; }
// navigation property to Project
[IgnoreDataMember]
public virtual Project Project { get; set; }
}
And then mapping:
modelBuilder.Entity<ProjectExtra>().HasKey(t => t.ProjectID);
modelBuilder.Entity<ProjectExtra>().HasRequired(p => p.Project).WithOptional();
Reference: Associations in EF Code First
You need to modify the both Project and ProjectExtra to make them have reference to each other:
public class Project : IModel
{
//Internal DB Id
[DataMember]
public int ProjectID { get; set; }
//Project No - Auto Generated at service level
[DataMember]
public string ProjectNo { get; set; }
public ProjectExtra ProjectExtra { get; set; }
}
public class ProjectExtra : IModel
{
[DataMember]
public int ProjectID { get; set; }
[DataMember]
public string Description { get; set; }
public Project Project { get; set; }
}
And then change your OnModelCreating like such:
modelBuilder.Entity<ProjectExtra>().HasKey(p => p.ProjectID);
modelBuilder.Entity<ProjectExtra>().HasRequired(p => p.Project)
.WithOptional(p => p.ProjectExtra);

How to configure the EF model when 1-0..1 and 1-many are both necessary

I have two entity model, OptionWidget and OptionWidgetValue, OptionWidget have many OptionWidgetValue like a option list, and sometimes one of those values will be a default one. I know that I can add one more field in OptionWidgetValue like Default to implement this relationship. But when I try to make another way, that is define the default in OptionWidget as the code below, I encounter some errors:
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
The following is my model definition:
public class OptionWidget : ModelBase
{
[MaxLength(100)]
public string Name { get; set; }
public int? DefaultValueId { get; set; }
[ForeignKey("DefaultValueId")]
public virtual OptionWidgetValue DefaultValue { get; set; }
[Required]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<OptionWidgetValue> OptionWidgetValues { get; set; }
}
public class OptionWidgetValue : ModelBase
{
public string Value { get; set; }
public decimal UnitPrice { get; set; }
public virtual ICollection<ValueDependency> Dependencies { get; set; }
[Required]
public int OptionWidgetId { get; set; }
[ForeignKey("OptionWidgetId")]
public virtual OptionWidget OptionWidget { get; set; }
}
For the 1-many relationship, I define them with fluent API like this and it works fine. But how should I define the 1-0..1 relationship for the default value. Please help me if you know, thank you very much.
builder.Entity<OptionWidget>()
.HasMany(e => e.OptionWidgetValues)
.WithRequired()
.HasForeignKey(e => e.OptionWidgetId)
.WillCascadeOnDelete(false);
You could use following Fluent API configuration:
modelBuilder.Entity<OptionWidget>()
.HasOptional(e => e.DefaultValue)
.WithOptionalPrincipal();
This code makes to side of relation optional but if you want make OptionWidget required for OptionWidgetValue use this:
modelBuilder.Entity<OptionWidget>()
.HasOptional(e => e.DefaultValue)
.WithRequired();

Categories

Resources