Entity Framework 一对多关系 Code First

3

我正在EF 4.1中迈出我的第一步。因为我一直使用NHibenate,所以代码优先方法对我来说似乎是最好的方法。我在一对���(或多对一)关系的良好映射方面遇到了问题。假设我有两个实体:

class ClientModel
{
    int ClientID;
    string Name;
    virtual IList<OrderModel> Orders;
}

class OrderModel
{
    int OrderID;
    string Details;
    virtual ClienModel Client;
}

当我这样离开的时候,在生成数据库的过程中会出现错误 - 表中的键丢失了。我想到可以通过将键的名称更改为ID(但这与我的命名约定不符)或添加[Key]注释来解决此问题。即使我添加了此注释,表的名称仍然是错误的 - 就像类名称一样,但带有's'。

所以我尝试使用流畅的API - 我进行了映射。但是,如果我设置映射就像这里一样:

class ClientMapping
{
    ClientMapping()
    {
        this.HasKey(e => e.ClientID).Property(e => e.ID).HasColumnName("ClientID");
        this.Property(e => e.Name).HasColumnName("Name");
        this.HasMany(e => e.Orders).WithOptional().Map(p => p.MapKey("OrderID")).WillCascadeOnDelete();
        this.ToTable("Clients");
    }
}

class OrderMapping
{
    OrderMapping()
    {
        this.HasKey(e => e.OrderID).Property(e => e.OrderID).HasColumnName("OrderID");
        this.Property(e => e.Details).HasColumnName("Details");
        this.HasRequired(e => e.Client).WithMany().Map(p=>p.MapKey("Client")).WillCascadeOnDelete(false);
        this.ToTable("Orders");
    }
}

数据库中表之间的关系是双重的。使用代码优先方法进行一对多关系的适当方法是什么?我的想法是正确的吗,还是错误的方法?
编辑
好的,我已按@Eranga所示的方式完成了,但仍存在一个问题。当我从数据库获取客户端时,它的订单属性为空(但在数据库中它有一些订单,其中Order.ClientID == Client.ClientID)。

我测试了我的答案中给出的配置,它可以正常工作。请确保Orders是一个属性而不是一个字段。 - Eranga
尝试使用virtual List<OrderModel> Orders;代替virtual IList<OrderModel> Orders; - Jo Smo
2个回答

6

您需要映射参与关系的两个属性。您需要在订单表中添加ClientID列。

class ClientMapping
{
    ClientMapping()
    {
        this.HasKey(e => e.ClientID).Property(e => e.ID).HasColumnName("ClientID");
        this.Property(e => e.Name).HasColumnName("Name");

        this.HasMany(e => e.Orders).WithRequired(o => o.Client)
           .Map(p => p.MapKey("ClientID")).WillCascadeOnDelete();

        this.ToTable("Clients");
    }
}

class OrderMapping
{
    OrderMapping()
    {
        this.HasKey(e => e.OrderID).Property(e => e.OrderID).HasColumnName("OrderID");
        this.Property(e => e.Details).HasColumnName("Details");
        this.ToTable("Orders");
    }
}

只需要从一个实体配置关系即可。


1
你需要映射参与关系的两个属性。你需要在订单表中添加ClientID列。这句话的意思是什么? - rideronthestorm
1
我还是不明白。我难道没有将它们都映射吗?也许你可以把完整的代码发布出来? 而且为什么在订单(OrderModel?)中需要ClientID? - rideronthestorm
1
@rideronthestorm,我已经发布了映射的完整代码。你目前的映射方式会被EF解释为两个不同的关系。由于Order需要Client,所以你需要在Order表中添加外键列ClientID - Eranga
1
作为外键,ClienModel Client不够吗? - rideronthestorm
你是说我应该使用int ClientID而不是ClientModel Client吗?为什么没有Client或ClientID的映射呢? - rideronthestorm
显示剩余5条评论

0

这可能会有所帮助(当我无法弄清楚它的工作原理时,它帮了我一个忙):

如果您的类像这样:

class ClientModel
{
    int ClientId;
    string Name;
}

class OrderModel
{
    int OrderId;
    string Details;
    int ClientId;
}

那么这将在您的数据库中表示2个表,它们之间不会通过外键连接(它们将通过OrderModel中的ClientId连接),您可以从数据库中获取数据,例如“GetAllOrdersWithSomeClientId”和“GetTheClientNameForSomeClientId”。但是,当您从数据库中删除一个Client时,问题就会出现。因为此时仍然会有一些Orders包含一个不存在于Client表中的ClientId,这将导致数据库中的异常。

ClientModel中的virtual List<OrderModel> Orders;(以及OrderModel中的virtual ClienModel Client;)需要创建关系,即表ClientModelOrderModel之间的外键。

有一件事情我仍然不太确定,就是在OrderModel中的int ClientId;。我猜测它必须要和ClientModel中的ClientId名称相同,这样实体框架才能知道两个属性之间的外键连接关系。如果有人能够详细解释一下这个问题就太好了。

另外,如果出现问题,请将此代码添加到您的DbContext构造函数中:

this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接