Entity Framework 5.0 component foreign key for non-primary key - is this possible?

I am using Entity Framework 5.0.0.0 in a .net 4.5 console application, and I need to access a database with two tables in it with a foreign key relationship between them as follows:

overview

The odd thing is that the foreign key is between B(Almost1, Almost2) and A(Almost1, Almost2) not from B(AId) to A(AId) . This is allowed by the SQL server, since Almost1 and Almost2 combined unique, and none of them can be zero (in table A at least - they appear on B as an optional connection, but this is by by by).

Here are some SQL to create this situation:

 CREATE TABLE [dbo].[A]( [AId] [int] IDENTITY(1,1) NOT NULL, [Almost1] [int] NOT NULL, [Almost2] [int] NOT NULL, CONSTRAINT [PK_A] PRIMARY KEY CLUSTERED ( [AId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY], CONSTRAINT [A_Constraint] UNIQUE NONCLUSTERED ( [Almost1] ASC, [Almost2] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] CREATE TABLE [dbo].[B]( [BId] [int] IDENTITY(1,1) NOT NULL, [Almost1] [int] NULL, [Almost2] [int] NULL, CONSTRAINT [PK_B] PRIMARY KEY CLUSTERED ( [BId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] ALTER TABLE [dbo].[B] ADD CONSTRAINT [FK_A_B] FOREIGN KEY([Almost1], [Almost2]) REFERENCES [dbo].[A] ([Almost1], [Almost2]) 

The fact is that this is not allowed by the Entity Framework - is that so, or am I just not correctly defining my model?

Here is my C #:

 public class MyContext : DbContext { public MyContext(string connectionString) : base(connectionString) { MyAs = Set<A>(); MyBs = Set<B>(); } public DbSet<A> MyAs { get; private set; } public DbSet<B> MyBs { get; private set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { var aEntity = modelBuilder.Entity<A>(); aEntity.ToTable("A"); aEntity.HasKey(a => a.AId); var bEntity = modelBuilder.Entity<B>(); bEntity.ToTable("B"); bEntity.HasKey(a => a.BId); bEntity .HasOptional(b => bA) .WithMany(a => a.Bs) .Map(m => m.MapKey("Almost1", "Almost2")); } } public class A { public int AId { get; set; } public int Almost1 { get; set; } public int Almost2 { get; set; } public virtual ICollection<B> Bs { get; private set; } public void AddB(B b) { if (b == null) throw new ArgumentNullException("b"); if (Bs == null) Bs = new List<B>(); if (!Bs.Contains(b)) Bs.Add(b); bA = this; } } public class B { public int BId { get; set; } public virtual AA { get; set; } } class Program { static void Main() { using (var ctx = new MyContext(@"connection string")) { ctx.MyAs.Add(new A { Almost1 = 1, Almost2 = 1 }); ctx.SaveChanges(); } } } 

It throws an InvalidOperationException message:

The specified foreign key columns of the association "Almost1, Almost2" are invalid. The number of columns specified must match the number of columns of the primary key.

If I ignore the AId column and instead create Almost1 and Almost2 composite primary key, so my OnModelCreating method now looks like this:

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { var aEntity = modelBuilder.Entity<A>(); aEntity.ToTable("A"); aEntity.HasKey(a => new { a.Almost1, a.Almost2 }); aEntity.Ignore(a => a.AId); var bEntity = modelBuilder.Entity<B>(); bEntity.ToTable("B"); bEntity.HasKey(a => a.BId); bEntity .HasOptional(b => bA) .WithMany(a => a.Bs) .Map(m => m.MapKey("Almost1", "Almost2")); } 

This works, but I really do not want to do this, because there is a table (let it be called C ) that refers to A traditional way, having the AId column and the foreign key goes from C.AId to A.AId .

Yes, this is a little strange, I know - but is it possible to deal with the Entity Framework?

+2
source share
1 answer

As already mentioned, since EF does not support unique keys and other SQL objects. I ended up creating some scripts as built-in resources and executed them as part of the drop / create initializer seed process.

Not sure if this will allow you to move between objects in the code, but it works very well so that the database is updated in one process.

+1
source

All Articles