编程语言
首页 > 编程语言> > c# – 实体框架代码First Fluent API配置,用于一对一的识别关系

c# – 实体框架代码First Fluent API配置,用于一对一的识别关系

作者:互联网

我有以下类结构:

如何配置Fluent API以将标识关系放入Cards表中?

我的意思是

>卡表PK:Id,CustomerId
>卡表FK:CustomerId

我想在将新的卡分配给Customer.Card属性时删除之前的卡.

所以我用这种方式定义了我的类:

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看起来像这样:

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

这是测试:

[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());
    }
}

它根本不起作用.我在第二次保存时有这个,我不知道如何在这里指定识别关系:

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.

更新很容易让它适用于一对多的关系.您可以在下面找到完整示例:

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

解决方法:

EF实现一对一的方式是使从属实体具有主键,该主键也是主要实体的外键.因此,依赖的PK自然受限于现有的PK值.

所以使用你的类,稍加修改:

public class Customer
{
    public int CustomerId { get; private set; }
    public virtual Card Card { get; set; }
}

public abstract class Card
{
    public int CustomerId { get; private set; }
}

public class Visa : Card { }

public class Amex : Card { }

和映射:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Customer>().HasRequired(c => c.Card)
                                   .WithRequiredPrincipal();
    modelBuilder.Entity<Card>().HasKey(c => c.CustomerId);
}

所以Card只有CustomerId作为PK和FK,而不是两个单独的字段.

试着这个,我发现EF(6.1.2)中有一个错误.这就是我做的:

using (var db = new TempModelsContext())
{
    var cst = new Customer { Name = "Customer1", 
                             Card = new Amex { Number = "Amex" } };
    db.Customers.Add(cst);
    db.SaveChanges();
}

using (var db = new TempModelsContext())
{
    var cst = db.Customers.Include(c => c.Card).Single(c => c.CustomerId == 1);
    cst.Card = new Visa { Number = "Visa" };
    db.SaveChanges();
}

(为方便起见,添加了名称和号码).

通常情况下这没关系. EF很聪明,可以看到1:1依赖实体被替换,它只是更新了Number字段(有效地删除了旧卡).

但EF忽略了继承(我使用了默认的TPH).当然它也应该更新鉴别器字段,但事实并非如此.如果您从数据库中重新获取项目,最终会得到一张Amex卡,并将“Visa”作为号码.

所以,遗憾的是,即使使用此模型,您首先必须删除旧卡,然后添加新卡:

var cst = db.Customers.Include(c => c.Card).Single(c => c.CustomerId == 1);
db.Cards.Remove(cst.Card);
db.SaveChanges();

cst.Card = new Visa { Number = "Visa" };
db.SaveChanges();

这很笨拙,更不用说你也想在TransactionScope中包装它.

标签:c,entity-framework,entity-framework-6,code-first,ef-fluent-api
来源: https://codeday.me/bug/20190624/1275878.html