EF 4 中的标识列

发布于 2024-09-18 01:11:16 字数 1181 浏览 8 评论 0原文

我遵循实体框架示例:

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 StoreGeneratePattern 设置为 Identity。

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

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

编辑

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

var schoolContext = new SchoolEntities();

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

            schoolContext.SaveChanges();

因为它将 Bill 添加到人员中,但是...因为 PersonID 不可为空,并且它用 id 0 插入了他。当然,当我尝试以相同的方式添加另一个人时我收到有关主键的错误:)

所以仍然没有任何想法...

有什么想法吗?

I follow by entity framework example :

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

and I have problem with Identity Columns.

Here is part of code of creating database:

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

In VS 2010 I build .edmx and at model I see that Person StoreGeneratedPattern is set to Identity.

But when I want to create Person, by :
alt text

Why I must put id, if this column is autoincrement?

EDITŁ

I thought that I found the way to resolve my problem by:

var schoolContext = new SchoolEntities();

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

            schoolContext.SaveChanges();

because it added Bill to persons, but...because PersonID is not nullable, and it inserted him with id 0. When I tried add another person the same way of course I get error about primary key :)

So still with nothing...

Any ideas ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

只等公子 2024-09-25 01:11:16

IMO 即使生成了 Id,它也是需要的。假设您正在使用外键关联(与独立关联不同的行为)。这意味着相关的子实体正在使用父实体的主键来建立关系。现在假设您要在单个工作单元中添加多个具有相关实体的父实体。您必须为每个父实体指定唯一(临时)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 是临时的,将在 store 中重新生成,但 context 不知道有关 store 配置的详细信息。当您在数据库中设置身份时,您还设置了种子和增量。上下文不知道这两个值,因此上下文无法派生存储尚未使用的有效唯一临时 ID。假设上下文错误地创建了一些临时 Id,然后加载已经使用此 Id = 问题的实体。

如果我只是更新我的代码以使用唯一的临时 ID(我知道商店是如何配置的),它将起作用。这就是我需要为 Create 方法提供临时 ID 的原因之一。

IMO the Id is needed even if it is generated. Suppose that you are using Foreign Key association (different behavior than independent association). It means that related child entities are using primary key of the parent entity to build relation. Now suppose that you are adding multiple parent entities with related entities in single unit of work. You have to specify unique (temporary) Id for each parent entity otherwise you will never configure your object graph. So the code generator probably makes this as default.

Edit:

I'm surprised that I have been downvoted for answer based on correct facts. So let me clarify my answer:

There are two types of relations available in EF 4.0. Independent association and Foreign key association. The diference is that the later one adds foreign key properties to entities. This allow you to work with relations in the same way as in database - simply by setting keys. You can read about these diferences in MSDN.

Now lets assume simple example. I have simple EDMX model with MyContext. Model consists of two entities Order and OrderLine. When I added Orders and OrderLines tables to the model I thicked Include foreign keys columns in model so I'm using Foreign keys associations instead of independent associations.

Order has store generated Id as a key and CustomerName as a property. Order line has store generated Id as a key, ProductTitle as property and OrderId as foreign key. I want to add two orders in single unit of work:

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.
}

The mistake I did in my code is setting Id of both new orders to 0. Even if this is temporary Id it still has to be unique if you want to use it for foreign keys. You can probably ask at the moment why the context doesn't handle Id by itself? Simple because it can't. Context knows that Id is temporary and will be regenerated in store but context doesn't know details about store configuration. When you set up Identity in database you also set up seed and increment. Those two values are not known to the context so the context is not able to derive valid unique temporary Id which is not already used by the store. Suppose that context wrongly create some temporary Id and after that you load entity which already uses this Id = problem.

If i simply update my code to use unique temporary Id (I know how the store is configured) it will work. That is IMO one reason why I need to provide temporary Id to Create methods.

埋情葬爱 2024-09-25 01:11:16

我无法告诉您为什么 EF 团队选择这样做 - 我唯一的猜测是创建 CreatePerson 方法的代码生成不会检查 ID 是否是自动增量并且只创建一个在任何情况下都可以工作的方法 - 无论 ID 是否自动增量。

如果这确实困扰您,您还可以受益于生成的实体类 Person 被定义为部分类,因此您可以轻松扩展它。创建一个名为 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;
 }

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

I cannot tell you why the EF team chose to do it this way - my only guess would be that the code generation that create the CreatePerson method doesn't check to see whether or not the ID is an autoincrement and just creates a method that will work in any circumstance - whether the ID is autoincrement or not.

If this really bothers you, you can also benefit from the fact that the generated entity class Person is defined as a partial class, so you can extend it easily. Create a new class file called e.g. PersonEx.cs and extend that partial class:

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

Now, you can easily create your Person entities without specifying an ID, and add them to your data:

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

     context.SaveChanges();

     int newID = newPerson.PersonID;
 }

It's not a perfect solution - but it should work just fine (at least it does for me).

嗼ふ静 2024-09-25 01:11:16

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

然后修改这个方法:

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;
}

You should be able to customize the t4 template that generates your classes to remove the property from the factory method. See Danny Simmons' blog post for info on how to create the t4. I haven't tested the resulting factory method, but I can't see why it wouldn't work.

Then modify this method:

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文