Fluent API - Marking ID as FK and PK - c#

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);

Related

How to bridge a table on itself using EF Core Code-First

This is a operation i have done many times in the past using database-first approach. I'm now trying it with code-first using EF Core and i'm failing horribly.
I have the following model:
public class DataMapping
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Model { get; set; }
public string Property { get; set; }
public bool IgnoreProperty { get; set; }
[NotMapped] //<-- I had to add this as the migration was complaining that it did not know what the relation was
public List<DataMappingRelation> DataMappingRelations { get; set; }
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public DateTime? DateModified { get; set; }
}
and a Bridge model that basically creates a relations between two DataMapping items in the same table:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
[ForeignKey("DataMappingId")]
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
However this call does not work:
return _context.DataMappings.Where(x => x.Model == type.FullName)
.Include(x=>x.DataMappingRelations)
.ToList();
It does not like the Include and throws a null ref exception.
All i basically need to do here is for a given "DataMapping" get all the related DataMapping items based on the relations in the "DataMappingRelations" table.
Yes i have looked at this answer but again, it is an example of two seperate tables, not a single table bridging on itself.
I suspect i have done all of this wrong. How can i get this to work? All the examples i have found are bridging two seperate tables. this would be bridging the same table.
Its many-to-many with self but your whole configuration looks messy.
So first, your DataMapping model class should contain two list navigation properties for two foreign keys in the DataMappingRelation as follows:
public class DataMapping
{
......
public List<DataMappingRelation> DataMapping1Relations { get; set; }
public List<DataMappingRelation> DataMapping2Relations { get; set; }
.........
}
Now remove [ForeignKey("DataMappingId")] attribute from both DataMapping1 and DataMapping2 foreign keys as follows:
public class DataMappingRelation
{
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public long? DataMapping1Id { get; set; }
public DataMapping DataMapping1 { get; set; }
public long? DataMapping2Id { get; set; }
public DataMapping DataMapping2 { get; set; }
}
Then the Fluent API configuration should be as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping1)
.WithMany(dm => dm.DataMapping1Relations)
.HasForeignKey(dmr => dmr.DataMapping1Id)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<DataMappingRelation>()
.HasOne(dmr => dmr.DataMapping2)
.WithMany(dm => dm.DataMapping2Relations)
.HasForeignKey(dmr => dmr.DataMapping2Id)
.OnDelete(DeleteBehavior.Restrict);
}

EntityFramework 1 to 0, 1 or many relationship

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);

Configure one-to-one mapping EF code first with different column names. Fluent API

I have two entities:
public partial class Web_Vendor
{
public string VendorID { get; set; }
public virtual Deal_Brands DealBrand { get; set; }
}
public partial class Deal_Brands
{
public string Brand { get; set; }
public virtual Web_Vendor WebVendor { get; set; }
}
I need to configure one-to-one mapping using fields VendorID <-> Brand using fluent API.
modelBuilder.Entity<Web_Vendor>().HasOptional(x=>x.DealBrand).WithRequired(x=>x.WebVendor).
Map(x=>x.MapKey(""))
Can anyone help to continue this line?
Looking at your classes I see that the relationship between Web_Vendor and Deal_Brands is "one-to-zero-one" (1-0.1) not "one-to-one". It means that Deal_Brands primary key must be the Web_Vendor primary key. The classes would be something like this:
public partial class Web_Vendor
{
public string VendorID { get; set; }
public virtual Deal_Brands DealBrand { get; set; }
}
public partial class Deal_Brands
{
public string VendorID { get; set; }
public string Brand { get; set; }
public virtual Web_Vendor WebVendor { get; set; }
}
The mapping:
modelBuilder.Entity<Web_Vendor>()
.HasKey(i => i.VendorID);
modelBuilder.Entity<Deal_Brands>()
.HasKey(i => i.VendorID);
modelBuilder.Entity<Web_Vendor>()
.HasOptional(x=>x.DealBrand)
.WithRequired(x=>x.WebVendor);
Hope it helps!

Entity Y has 2 FK to entiy X not working with EF 6

I had this code working using EF5, not is complains about cyclic dependencies:
public class Supplier
{
public int SupplierId { get; set; }
[ForeignKey("SupplierId")]
public Company SupplierE { get; set; }
public int CompanyId { get; set; }
[ForeignKey("CompanyId")]
[InverseProperty("Suppliers")]
public virtual Company Company { get; set; }
}
public class Company
{
public int CompanyId { get; set; }
[InverseProperty("Company")]
public ICollection<Client> Clients { get; set; }
[InverseProperty("Company")]
public ICollection<Supplier> Suppliers { get; set; }
}
and the fluent configuration is something like:
modelBuilder
.Entity<Supplier>()
.HasRequired(c => c.Company)
.WithMany(v => v.Suppliers)
.WillCascadeOnDelete(false);
Also I have some code to configure the keys fr the Supplier Entity:
public class SupplierConfiguration: EntityTypeConfiguration<Supplier>
{
public SupplierConfiguration()
{
HasKey(c => new {c.CompanyId, c.SupplierId});
}
}
I get an error message about cyclic dependencies:
Introducing FOREIGN KEY constraint 'FK_dbo.Supplier_dbo.Company_CompanyId' on table 'Supplier' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
As I mentionned this used to work in EF5, I just migrated to EF6 and started getting this.In addition I have a similar class called Client and it works fine for it.
Any ideas?
EDIT:
this is the same code from client:
public class Client
{
public int ClientId { get; set; }
[ForeignKey("ClientId")]
public Company ClientE { get; set; }
public int CompanyId { get; set; }
[ForeignKey("CompanyId")]
[InverseProperty("Clients")]
public virtual Company Company { get; set; }
}

Entity Framework Creates unwanted relationship between abstract and derived tables

Using code first, I have some abstract classes and some classes derived from those abstracted classes.
// Abstracted Classes
public abstract class Brand
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class Model
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
// Derived Classes
[Table("ComparisonBrand")]
public class ComparisonBrand : Brand
{
public ComparisonBrand()
{
ComparisonValues = new List<ComparisonValue>();
Models = new List<ComparisonModel>();
}
public virtual ICollection<ComparisonValue> ComparisonValues { get; set; }
public virtual ICollection<ComparisonModel> Models { get; set; }
}
[Table("ComparisonModel")]
public class ComparisonModel : Model
{
public int? BrandId { get; set; }
public int? LogoId { get; set; }
[ForeignKey("BrandId")]
public virtual ComparisonBrand ComparisonBrand { get; set; }
[ForeignKey("LogoId")]
public virtual ComparisonLogo ComparisonBrand { get; set; }
public virtual ICollection<ComparisonValue> ComparisonValues { get; set; }
}
My issue is that the migration generates foreign keys for:
ComparisonModel.Id > Models.Id
ComparisonModel.BrandId > Brands.Id
ComparisonModel.BrandId > ComparisonBrand.Id
Since ComparisonBrand.Id is a FK to Brands.BrandId, I get an error when deleting a Brand record. If I delete the ComparisonModel.BrandId > ComparisonBrand.Id relationship, however, the delete works fine.
How can I prevent a relationship from being formed between both the abstracted table and the derived table (Brands and ComparisonBrand)?
You are using the virtual keyword this causes Lazy Loading. You are telling EF to generate Foreign keys for them through this feature. Drop the virtual and you will not create the keys any longer

Categories

Resources