【问题标题】:The declared type of navigation property Project.Models.Customer.SubCustomers is not compatible with the result of the specified navigation声明的导航属性类型 Project.Models.Customer.SubCustomers 与指定导航的结果不兼容
【发布时间】:2015-01-12 03:18:34
【问题描述】:

我查看了类似错误的答案,但似乎找不到特定错误的答案。

我正在运行迁移,运行种子方法时发生错误(迁移工作正常)。

导航属性 Project.Models.Customer.SubCustomers 声明的类型与指定导航的结果不兼容。

这是一个循环引用,即一个客户可以有 0..* 子客户和 0..1 父客户。

客户模型:

public class Customer : Entity
{
    public int CustomerId { get; set;}
    public string Name { get; set; }

    // Relationships

    public int? ParentCustomerId { get; set; }
    public virtual ICollection<Customer> SubCustomers { get; set; } // a Customer has many Customers as SubCustomers, a Customer has zero or one ParentCustomer

    // Other relationships
}

来自上下文流畅的 api:

modelBuilder.Entity<Customer>()
            .HasKey(customer => customer.CustomerId);
modelBuilder.Entity<Customer>()
            .Property(customer => customer.Name)
            .IsRequired()
            .HasColumnType("nvarchar")
            .HasMaxLength(500);
modelBuilder.Entity<Customer>() // a Customer has many Customers as SubCustomers, a Customer has zero or one ParentCustomer
            .HasOptional(customer => customer.SubCustomers)
            .WithMany()
            .HasForeignKey(customer => customer.ParentCustomerId);

从种子方法:(客户在数据库中创建,子客户不起作用)

// Default Customers - create and save
var customers = new[]{
    new Customer { Name = "Custa" },
    new Customer { Name = "Custb" },
    new Customer { Name = "Custc" }
};
context.Customers.AddOrUpdate(r => r.Name, customers[0], customers[1], customers[2]);
context.SaveChanges();

// Add SubCustomers b & c to Customer a (ids calculated beforehand, e.g. aId, as linq does not support array index)
var aId = customers[0].CustomerId;
var a = context.Customers.Include(c => c.SubCustomers).SingleOrDefault(c => c.CustomerId == aId);
if (a.SubCustomers == null)
    a.SubCustomers = new List<Customer>();
var bId = customers[1].CustomerId;
var b = a.SubCustomers.SingleOrDefault(c => c.CustomerId == bId);
if (b == null)
    a.SubCustomers.Add(context.Customers.Single(c => c.CustomerId == bId));
var cId = customers[2].CustomerId;
var c = a.SubCustomers.SingleOrDefault(c => c.CustomerId == cId);
if (c == null)
    a.SubCustomers.Add(context.Customers.Single(c => c.CustomerId == cId));
context.SaveChanges();

如果有人能找出导致错误的原因,我将不胜感激。非常感谢!

【问题讨论】:

    标签: c# ef-code-first entity-framework-6 entity-framework-migrations ef-fluent-api


    【解决方案1】:

    经过一番阅读和反复试验,我终于弄清楚了这一点。我很困惑,因为它是自引用的,并且遗漏了一个关键元素。

    通常,当在两个不同的对象之间创建一对多关系时,您会得到如下内容:

    public class Foo {
        public int FooId {get; set;}
        public string Name {get; set;}
    
        // Relationships
        public virtual ICollection<Bar> Bars {get; set;} // a Foo has many bars, a Bar has one optional Foo
    }
    
    public class Bar {
        public int BarId {get; set;}
        public string Name {get; set;}
    
        // Relationships
        public int? FooId {get; set;} // a Foo has many bars, a Bar has one optional Foo
        public virtual Foo Foo {get; set;}
    }
    

    然后在上下文中(fluent API):

    modelBuilder.Entity<Bar>() // a Foo has many bars, a Bar has one optional Foo
        .HasRequired(bar => bar.Foo)
        .WithMany(foo => foo.Bars)
        .HasForeignKey(bar => bar.FooId);
    modelBuilder.Entity<Bar>()
                .HasKey(bar => bar.BarId);
    
    modelBuilder.Entity<Foo>()
                .HasKey(foo => foo.FooId);
    

    在我的代码中,在我最初的问题中,在这种情况下,我省略了 public virtual Foo Foo {get; set;} 的等价物。

    因此,要创建一个自引用 Foo,您需要关系的所有三个元素,在上面的示例中,它们在两个对象之间拆分,位于 Foo 对象中(真的很合乎逻辑!):

    public class Foo {
        public int FooId {get; set;}
        public string Name {get; set;}
    
        // Relationships
        public int? ParentFooId {get; set;} // a Foo has many SubFoos, a Foo has one optional ParentFoo
        public virtual Foo ParentFoo {get; set;}
        public virtual ICollection<Foo> SubFoos {get; set;}
    }
    

    还有上下文(fluent API):

    modelBuilder.Entity<Foo>() // a Foo has many SubFoos, a Foo has one optional ParentFoo
        .HasOptional(foo => foo.ParentFoo)
        .WithMany(foo => foo.SubFoos)
        .HasForeignKey(foo => foo.ParentCustomerId);
    modelBuilder.Entity<Foo>()
                .HasKey(foo => foo.FooId);
    

    要将数据播种到这种自引用关系中,我需要 Configuration.cs 中的以下内容:

    // Default Foos - create
    var foos = new[]{
            new Foo { Name = "foo1", SubFoos = new List<Foo>() },
            new Foo { Name = "foo2", SubFoos = new List<Foo>() },
            new Foo { Name = "foo3", SubFoos = new List<Foo>() },           
    context.Tabs.AddOrUpdate(t => t.View, foos[0], foos[1], foos[2]);
    context.SaveChanges();
    
    // Add a SubFoo to a Foo
    var parentFooId = foos[0].FooId;
    var parentFoo = context.Foos.Include(f => f.SubFoos).SingleOrDefault(f => f.FooId == parentFooId);
    // If no current SubFoos initiate an empty list
    if (parentFoo.SubFoos == null)
        parentFoo.SubFoos = new List<Foo>();
    // Get another Foo to add as a SubFoo
    var childFooId = foos[1].FooId;
    // Check if child foo already exists and add if not
    var childFoo = parentFoo.SubFoos.SingleOrDefault(f => f.FooId == childFooId);
    if (childFoo == null)
        parentFoo.SubFoos.Add(context.Foos.Single(f => f.FooId == childFooId));
    context.SaveChanges();
    

    【讨论】:

    • 感谢您发布此问题和答案。今天早上我们已经为这个确切的问题苦苦挣扎了几个小时,这篇文章为我们指明了正确的方向。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 2012-11-26
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多