我遵循实体框架示例:
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时,通过:
为什么我必须输入id,如果此列是自动增量?
EDITŁ
我以为我找到了解决问题的方法:
var schoolContext = new SchoolEntities();
schoolContext.AddToPeople(new Person() { LastName = "Gates", FirstName = "Bil" });
schoolContext.SaveChanges();
因为它向人们添加了Bill,但是...因为PersonID不可为空,并且它插入了id为0.当我尝试以同样的方式添加另一个人时,我得到关于主键的错误:)
所以一无所有......
有什么想法吗?
答案 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;
}