EF 4中的标识列

时间:2010-09-04 11:11:17

标签: c# entity-framework-4 primary-key

我遵循实体框架示例:

http://msdn.microsoft.com/en-us/library/bb399182.aspx

我的身份栏有问题。

以下是创建数据库代码的一部分:

CREATE TABLE [dbo].[Person](
    [PersonID] [int] IDENTITY(1,1) NOT NULL,
    [LastName] [nvarchar](50) NOT NULL,
    [FirstName] [nvarchar](50) NOT NULL,
    [HireDate] [datetime] NULL,
    [EnrollmentDate] [datetime] NULL,
 CONSTRAINT [PK_School.Student] PRIMARY KEY CLUSTERED 
(
    [PersonID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO

在VS 2010中,我构建.edmx,在模型中我看到Person StoreGeneratedPattern设置为Identity。

但是当我想创建Person时,通过: alt text

为什么我必须输入id,如果此列是自动增量?

EDITŁ

我以为我找到了解决问题的方法:

var schoolContext = new SchoolEntities();

            schoolContext.AddToPeople(new Person() { LastName = "Gates", FirstName = "Bil" });

            schoolContext.SaveChanges();

因为它向人们添加了Bill,但是...因为PersonID不可为空,并且它插入了id为0.当我尝试以同样的方式添加另一个人时,我得到关于主键的错误:)

所以一无所有......

有什么想法吗?

3 个答案:

答案 0 :(得分:7)

即使生成了ID,也需要IMO。假设您正在使用外键关联(与独立关联不同的行为)。这意味着相关子实体正在使用父实体的主键来构建关系。现在假设您在单个工作单元中添加了多个具有相关实体的父实体。您必须为每个父实体指定唯一(临时)ID,否则您将永远不会配置对象图。所以代码生成器可能会将其设置为默认值。

修改

我很惊讶我根据正确的事实被贬低了答案。所以让我澄清一下我的答案:

EF 4.0中有两种类型的关系。独立协会和外键协会。不同之处在于后者将外键属性添加到实体。这允许您以与数据库相同的方式处理关系 - 只需设置键即可。您可以在MSDN中了解这些差异。

现在让我们假设一个简单的例子。我有MyContext的简单EDMX模型。模型由两个实体Order和OrderLine组成。当我将Orders和OrderLines表添加到模型时,我在模型中包含外键列,因此我使用外键关联而不是独立关联。

订单将生成的Id存储为密钥,将CustomerName存储为属性。订单行将商店生成的Id作为键,将ProductTitle作为属性,将OrderId作为外键。我想在单个工作单元中添加两个订单:

using (var context = new MyContext())
{
  var ox = Order.CreateOrder(0, "CustomerX");
  var oy = Order.CreateOrder(0, "CustomerY");

  // Building relationship in the same way as in database
  var olx = OrderLine.CreateOrderLine(0, ox.Id, "ProductX");
  var oly = OrderLine.CreateOrderLine(0, oy.Id, "ProductY");

  context.Orders.AddObject(ox);
  context.Orders.AddObject(oy);
  context.OrderLines.AddObject(olx);
  context.OrderLines.AddObject(oly);
  context.SaveChanges(); // UpdateException: Unable determine principal end of Model.FK_OrderLine_Order relationship. Multiple added entities have the same primary key.
}

我在代码中所犯的错误是将两个新订单的Id设置为0.即使这是临时Id,如果要将其用于外键,它仍然必须是唯一的。您现在可能会问为什么上下文不能单独处理Id?很简单,因为它不能。 Context知道Id是临时的,并且将在存储中重新生成,但上下文不知道有关存储配置的详细信息。在数据库中设置Identity时,还可以设置种子和增量。上下文不知道这两个值,因此上下文无法派生商店尚未使用的有效唯一临时Id。假设上下文错误地创建了一些临时Id,然后加载已经使用此Id =问题的实体。

如果我只是更新我的代码以使用唯一的临时Id(我知道如何配置商店)它将起作用。这是IMO为什么我需要提供临时Id来创建方法的一个原因。

答案 1 :(得分:6)

我不能告诉你为什么EF团队选择这样做 - 我唯一的猜测是创建CreatePerson方法的代码生成不会检查ID是否是自动增量并且只创建一个适用于任何情况的方法 - 无论ID是否为自动增量。

如果这真的让您感到烦恼,那么您也可以从生成的实体类Person定义为部分类这一事实中受益,因此您可以轻松扩展它。创建一个名为eg的新类文件PersonEx.cs并扩展该部分类:

public partial class Person 
{
    public static Person CreatePerson(string lastName, string firstName)
    {
        return CreatePerson(-1, lastName, firstName);
    }
}

现在,您可以轻松创建Person个实体,而无需指定ID,并将其添加到您的数据中:

 using(SchoolEntities context = new SchoolEntities())
 {
     Person newPerson = Person.CreatePerson("Gates", "Bill");
     context.AddToPeople(newPerson);

     context.SaveChanges();

     int newID = newPerson.PersonID;
 }

这不是一个完美的解决方案 - 但它应该工作得很好(至少它对我有用)。

答案 2 :(得分:2)

您应该能够自定义生成类的t4模板,以从工厂方法中删除属性。有关如何创建t4的信息,请参阅Danny Simmons' blog post。我没有测试生成的工厂方法,但我不明白为什么它不起作用。

然后修改此方法:

bool IncludePropertyInFactoryMethod(StructuralType factoryType, EdmProperty edmProperty)
{
    if (edmProperty.Nullable)
    {
        return false;
    }

    if (edmProperty.DefaultValue != null)
    {
        return false;
    }

    if ((Accessibility.ForReadOnlyProperty(edmProperty) != "public" && Accessibility.ForWriteOnlyProperty(edmProperty) != "public") ||
        (factoryType != edmProperty.DeclaringType && Accessibility.ForWriteOnlyProperty(edmProperty) == "private")
       )
    {
        //  There is no public part to the property.
        return false;
    }

    /*********
     * This was added:
     */

    var identity = edmProperty.TypeUsage.Facets
        .Where(f => f.Name == "StoreGeneratedPattern").FirstOrDefault();

    if (identity != null && identity.Value.ToString() == "Identity")
    {
        // property is "Identity" generated, so skip from the factory method.
        return false;
    }

    /*********
     * end of the custom code
     */

    return true;
}