我在使用“Code First”时遇到了EF 4.1的问题。让我在开始发布任何代码之前设置我的情况。我在名为Data.EF的类库项目中有我的DBContext类,名为MemberSalesContext。我将POCO放在一个名为Domain的独立类库项目中。 My Domain项目对Entity Framework一无所知,没有引用,也没有任何内容。我的Data.EF项目引用了Domain项目,因此我的DB上下文类可以连接位于Data.EF.Mapping中的映射类中的所有内容。我正在使用EntityFramework中的EntityTypeConfiguration类来执行此命名空间中的所有映射。所有这些都是非常标准的东西。在Entity Framework之上,我使用的是Repository模式和Specification模式。
我的SQL Server数据库表定义了一个复合主键。作为密钥一部分的三列是Batch_ID,RecDate和Supplier_Date。此表作为标识列(数据库生成的值=> +1)称为XREF_ID,它不是PK的一部分。
我的映射类位于Data.EF.Mapping中,如下所示:
public class CrossReferenceMapping : EntityTypeConfiguration<CrossReference>
{
public CrossReferenceMapping()
{
HasKey(cpk => cpk.Batch_ID);
HasKey(cpk => cpk.RecDate);
HasKey(cpk => cpk.Supplier_Date);
Property(p => p.XREF_ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("wPRSBatchXREF");
}
}
我的MemberSalesContext类(继承自DBContext)如下所示:
public class MemberSalesContext : DbContext, IDbContext
{
//...more DbSets here...
public DbSet<CrossReference> CrossReferences { get; set; }
//...more DbSets here...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
//...more modelBuilder here...
modelBuilder.Configurations.Add<CrossReference>(new CrossReferenceMapping());
//...more modelBuilder here...
}
}
我在一个类中有一个私有方法,它使用我的存储库返回迭代的对象列表。我所指的列表是下面示例中最外层的foreach循环。
private void CloseAllReports()
{
//* get list of completed reports and close each one (populate batches)
foreach (SalesReport salesReport in GetCompletedSalesReports())
{
try
{
//* aggregate sales and revenue by each distinct supplier_date in this report
var aggregates = BatchSalesRevenue(salesReport);
//* ensure that the entire SalesReport breaks out into Batches; success or failure per SalesReport
_repository.UnitOfWork.BeginTransaction();
//* each salesReport here will result in one-to-many batches
foreach (AggregateBySupplierDate aggregate in aggregates)
{
//* get the batch range (type) from the repository
BatchType batchType = _repository.Single<BatchType>(new BatchTypeSpecification(salesReport.Batch_Type));
//* get xref from repository, *if available*
//* some will have already populated the XREF
CrossReference crossReference = _repository.Single<CrossReference>(new CrossReferenceSpecification(salesReport.Batch_ID, salesReport.RecDate, aggregate.SupplierDate));
//* create a new batch
PRSBatch batch = new PRSBatch(salesReport,
aggregate.SupplierDate,
BatchTypeCode(batchType.Description),
BatchControlNumber(batchType.Description, salesReport.RecDate, BatchTypeCode(batchType.Description)),
salesReport.Zero_Sales_Flag == false ? aggregate.SalesAmount : 1,
salesReport.Zero_Sales_Flag == false ? aggregate.RevenueAmount : 0);
//* populate CrossReference property; this will either be a crossReference object, or null
batch.CrossReference = crossReference;
//* close the batch
//* see PRSBatch partial class for business rule implementations
batch.Close();
//* check XREF to see if it needs to be added to the repository
if (crossReference == null)
{
//*add the Xref to the repository
_repository.Add<CrossReference>(batch.CrossReference);
}
//* add batch to the repository
_repository.Add<PRSBatch>(batch);
}
_repository.UnitOfWork.CommitTransaction();
}
catch (Exception ex)
{
//* log the error
_logger.Log(User, ex.Message.ToString().Trim(), ex.Source.ToString().Trim(), ex.StackTrace.ToString().Trim());
//* move on to the next completed salesReport
}
}
}
在外循环的第一次迭代中一切顺利。在外部循环的第二次迭代中,代码在_repository.UnitOfWork.CommitTransaction()处失败。返回的错误消息如下:
“对数据库的更改已成功提交,但更新对象上下文时发生错误.ObjectContext可能处于不一致状态。内部异常消息:AcceptChanges无法继续,因为对象的键值与另一个对象冲突ObjectStateManager。在调用AcceptChanges之前,请确保键值是唯一的。“
在这种情况下,第二次迭代的数据库更改未成功提交,但第一次迭代中的更改是。我确保外部和内部循环中的对象都是唯一的,并且遵守数据库主键。
我在这里缺少一些东西吗?如果证明有用,我愿意扩充我的代码示例。我已经完成了解决此问题的所有功能,减去了修改数据库表上的复合主键集。
任何人都可以帮忙吗?非常感谢提前!顺便说一句,对不起,很长的帖子!
答案 0 :(得分:0)
我在这里回答我自己的问题......
我的问题与我的映射类中如何定义复合主键有关。使用EF Code First定义复合主键时,必须如下定义:
HasKey(cpk => new { cpk.COMPANYID, cpk.RecDate, cpk.BATTYPCD, cpk.BATCTLNO });
与我之前定义的方式相反:
HasKey(cpk => cpk.COMPANYID);
HasKey(cpk => cpk.RecDate);
HasKey(cpk => cpk.BATTYPCD);
HasKey(cpk => cpk.BATCTLNO);
我收到的错误是ObjectContext包含多个不同的同一类型的元素。这成为我的UnitOfWork上的CommitTransaction问题。这是因为当我的DBContext类实例化了映射类时,它执行了上面显示的4个HasKey语句,只有属性BATCTLNO的最后一个成为主键(不是复合键)。如上面的第一个代码示例所示,内联定义它们可以解决问题。
希望这有助于某人!