none
Entity Framework Code First Fluent API configuration for one to one identifying relationship RRS feed

  • Question

  • I have the following class structure:

    enter image description here

    How to configure Fluent API to put identifying relationship in Cards table?

    I mean

    • Cards Table PK: Id, CustomerId
    • Cards Table FK: CustomerId

    I would like the previous Card be deleted when I assign a new one to Customer.Card property.

    So I defined my classes this way:

    public class Customer
    {
        public int Id { get; private set; }
        public virtual Card Card { get; set; }
    }
    
    public abstract class Card
    {
        public int Id { get; private set; }
    }
    
    public class Visa : Card
    {
    }
    
    public class Amex : Card
    {
    }

    DbContext looks like this:

    public class Context : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
        public DbSet<Card> Cards { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<Customer>()
                .HasRequired(c => c.Card)
                .WithRequiredPrincipal()
                .Map(a => a.MapKey("CustomerId"))
                .WillCascadeOnDelete();
    
            modelBuilder.Entity<Card>();
        }
    }

    Here is the test:

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var context = new Context();
            var customer = new Customer();
            context.Customers.Add(customer);
            customer.Card = new Visa();
            context.SaveChanges();
    
            customer.Card = new Amex();
            context.SaveChanges();
    
            Assert.AreEqual(1, context.Customers.Count());
            Assert.AreEqual(1, context.Cards.Count());
        }
    }

    It does not work at all. I have this on second save and I do not know how to specify identifying relationship here:

    Unhandled Exception: System.Data.Entity.Infrastructure.DbUpdateException: An err or occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a singl e entity cannot be identified as the source of the exception. Handling of except ions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details. ---> System.Data.Entity.Core.U pdateException: A relationship from the 'Customer_Card' AssociationSet is in the 'Deleted' state. Given multiplicity constraints, a corresponding 'Customer_Card _Target' must also in the 'Deleted' state.

    UPDATE It's easy to make it work for one-to-many relationships. You can find a full example below:

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var context = new Context();
            var customer = new Customer();
            context.Customers.Add(customer);
            customer.Cards.Add(new Visa());
            context.SaveChanges();
    
            customer.Cards[0] = new Amex();
            context.SaveChanges();
    
            Assert.AreEqual(1, context.Cards.Count());
        }
    }
    
    public class Customer
    {
        public Customer()
        {
            Cards = new List<Card>();
        }
    
        public int Id { get; private set; }
        public virtual List<Card> Cards { get; set; }
    }
    
    public abstract class Card
    {
        public int Id { get; private set; }
        public int CustomerId { get; private set; }
    }
    
    public class Visa : Card
    {
    }
    
    public class Amex : Card
    {
    }
    
    public class Context : DbContext
    {
        static Context()
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
        }
    
        public DbSet<Customer> Customers { get; set; }
        public DbSet<Card> Cards { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<Customer>()
                .HasMany(c => c.Cards)
                .WithRequired()
                .HasForeignKey(c => c.CustomerId)
                .WillCascadeOnDelete();
    
            modelBuilder.Entity<Card>()
                .HasKey(c => new { c.Id, c.CustomerId });
        }
    }



    Sunday, January 18, 2015 3:32 AM

Answers

All replies