ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2019-06-24 01:53:14  阅读:320  来源: 互联网

标签:c entity-framework entity-framework-6 code-first ef-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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有