简体   繁体   English

实体框架中的关系(代码优先)

[英]Relation in entity framework (code first)

I have 2 tables: 我有2张桌子:

tblInvestment
{
    InvestmentId char(10), --pk
    PrimaryPerformanceId char(10)
}

tblPerformance
{
    PerformanceId char(10), --pk,
    InvestmentId char(10)
}

And I created 2 entity clasees for these two table: 我为这两个表创建了2个实体分类:

[Table("tblInvestment")]
class Investment
{
    [Key]
    public string InvestmentId { get; set; }
    public string PrimaryPerformanceId { get; set; }

    [ForeignKey("PrimaryPerformanceId")]
    public virtual Performance PrimaryPerformance { get; set; }
    [ForeignKey("InvestmentId")]
    public virtual ICollection<Performance> Performances { get; set; }
}

[Table("tblPerformance")]
class Performance
{
    [Key]
    public string PerformanceId { get; set; }
    public string InvestmentId { get; set; }
    [ForeignKey("InvestmentId")]
    public virtual Investment Investment { get; set; }
}

When I create an new record for each table, and call the DbContext.SaveChanges function, I got an exception says: " Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values. " But if I remove the Investment.PrimaryPerformance property, I can save the records to database. 当我为每个表创建新记录并调用DbContext.SaveChanges函数时,出现一个异常消息:“ 无法确定相关操作的有效顺序。由于外键约束,模型要求或存储-产生的价值。“但是,如果我删除Investment.PrimaryPerformance财产,我可以将记录保存到数据库中。 Why? 为什么?

using(MyContext db = new MyContext)
{
    var inv = db.Investments.Add(new Investment{
        InvestmentId = "1",
        PrimaryPerformance = new Performance{
            PerformanceId = "A",
            InvestmentId = "1"
        }
    };
    db.SaveChanges();
}

Here is the stack info: 这是堆栈信息:

System.Data.Mapping.Update.Internal.UpdateTranslator.DependencyOrderingError(IEnumerable`1 remainder) System.Data.Mapping.Update.Internal.UpdateTranslator.ProduceCommands() System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) System.Data.Entity.Internal.InternalContext.SaveChanges() System.Data.Entity.Internal.InternalContext.SaveChanges() System.Data.Entity.Internal.LazyInternalContext.SaveChanges() System.Data.Entity.DbContext.SaveChanges() System.Data.Mapping.Update.Internal.UpdateTranslator.DependencyOrderingError(IEnumerable`1剩余)System.Data.Mapping.Update.Internal.UpdateTranslator.ProduceCommands()System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager ,IEntityAdapter适配器)System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache)System.Data.Objects.ObjectContext.SaveChanges(SaveOptions选项)System.Data.Entity.Internal.InternalContext.SaveChanges()System.Data.Entity.Internal .InternalContext.SaveChanges()System.Data.Entity.Internal.LazyInternalContext.SaveChanges()System.Data.Entity.DbContext.SaveChanges()

I think the problem is in your model where you have a double foreign key of "Performance" ==> 我认为问题出在您的模型中,您的双外键为“ Performance” ==>

[ForeignKey("PrimaryPerformanceId")]
public virtual Performance PrimaryPerformance { get; set; }
[ForeignKey("InvestmentId")]
public virtual ICollection<Performance> Performances { get; set; }

I think that you must remove one of them... 我认为您必须删除其中之一...

From MSDN: 从MSDN:

If placed on a foreign key property, the name of the associated navigation property. 如果放置在外键属性上,则为关联的导航属性的名称。 If placed on a navigation property, the name of the associated foreign key(s). 如果放置在导航属性上,则为关联的外键的名称。

You only need to associate the related fields within the same class (if they do not match the Naming conventions). 您只需要在同一类中关联相关字段(如果它们与命名约定不匹配)。 You do so to tell Code First that they belong to the same Foreign Key relation. 您这样做是为了告诉Code First它们属于同一外键关系。 Code First is then clever enough to figure out the primary keys of the Relation by itself, since you have specified the Key attribute on the related classes. 然后,由于您已经在相关类上指定了Key属性,因此Code First足够聪明,可以自行找出该关系的主键。

So your model should look like this instead: 因此,您的模型应如下所示:

[Table("tblInvestment")]
class Investment
{
    [Key]
    public string InvestmentId { get; set; }

    [ForeignKey("PrimaryPerformance")]
    public string PrimaryPerformanceId { get; set; } // This is the foreign key property           
    public virtual Performance PrimaryPerformance { get; set; } // This is the navigation property

    public virtual ICollection<Performance> Performances { get; set; }
}

[Table("tblPerformance")]
class Performance
{
    [Key]
    public string PerformanceId { get; set; }

    public virtual Investment Investment { get; set; }
}

OK, first off, you need to correct your object model since it gives you an incorrect Db schema as the way it stands now. 好,首先,您需要更正您的对象模型,因为它为您提供了一种不正确的Db模式,就像现在那样。 If you look into the resulting schema, you'll see that EF created an additional InvestmentId (with the name Investment_InvestmentId) on tblPerformance. 如果查看结果模式,您会看到EF在tblPerformance上创建了一个附加的InvestmentId(名称为Investment_InvestmentId)。 All you need to do is to specify that your association between Investment and Performance classes represented by Investment.Performances and Performance.Investment properties is bidirectional. 您需要做的就是指定由Investment.Performances和Performance.Investment属性表示的Investment和Performance类之间的关联是双向的。

If you prefer data annotations over the fluent API (as it looks) then you need to use the InverseProperty attribute like the following: 如果您喜欢数据注释而不是流利的API(看起来),则需要使用InverseProperty属性,如下所示:

[Table("tblPerformance")]
class Performance
{
    [Key]
    public string PerformanceId { get; set; }
    public string InvestmentId { get; set; }

    [InverseProperty("Performances")]
    public virtual Investment Investment { get; set; }
}


Now, the exception that you are getting is very normal. 现在,您得到的异常非常正常。 Think about it, you're asking EF to add two objects for you in one transaction: an investment object who has a primary performance object so EF tries to insert the performance record first in order to get a PrimaryPerformanceId for the investment object (with InvestmentId=1), but then it notices that the performance record also needs the very same Investment object (with Id=1) as its Investment. 想一想,您要EF在一次交易中为您添加两个对象:一个具有主要绩效对象的投资对象,因此EF尝试首先插入绩效记录,以便获得该投资对象的PrimaryPerformanceId(带有InvestmentId = 1),但随后它注意到效果记录还需要与其投资相同的投资对象(Id = 1)。 Which one is going to go first? 哪一个先去? Impossible in one transaction. 不可能在一笔交易中。

Therefore, the only way to make it work is to use two transactions to add your objects: 因此,使其工作的唯一方法是使用两个事务来添加对象:

using(Context db = new Context())
{
    var inv = db.Investments.Add(new Investment() { InvestmentId = "1"});
    db.SaveChanges();

    inv.PrimaryPerformance = new Performance()
    {
        PerformanceId = "A",
        InvestmentId = "1"
    };
    db.SaveChanges();
}

And this code works just fine. 此代码可以正常工作。

Note: I've used the latest EF release to run this code (v4.3.1). 注意:我使用最新的EF版本来运行此代码(v4.3.1)。 So please update your binaries if you haven't already. 因此,请更新二进制文件(如果尚未安装)。

The exception is thrown by TryTopologicalSort function, I guess this function is used for working out the dependency between the records. TryTopologicalSort函数引发异常,我猜想此函数用于计算记录之间的依赖关系。 It only supports one relationship between entities. 它仅支持实体之间的一种关系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM