2011-06-17 60 views
0

我遇到了使用「代碼優先」的EF 4.1問題。讓我在開始發佈任何代碼之前設置自己的狀況。我在名爲Data.EF的類庫項目中有我的DBContext類,名爲MemberSalesContext。我在一個名爲Domain的獨立類庫項目中有我的POCO。 My Domain項目對Entity Framework一無所知,沒有任何引用,沒有任何內容。我的Data.EF項目有一個對Domain項目的引用,以便我的數據庫上下文類可以連接位於Data.EF.Mapping中的映射類中的所有內容。我正在使用EntityFramework的EntityTypeConfiguration類來完成這個命名空間中的所有映射。所有這些都是非常標準的東西。在Entity Framework之上,我使用了Repository模式和Specification模式。EF代碼CommitTransaction上的第一個問題 - 使用存儲庫模式

我的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

你爲什麼明確處理交易? – Eranga 2011-06-17 04:08:25

+0

我不確定...但EF可能會因爲您聲明XREF_ID屬性具有數據庫生成選項「Identity」而感到困惑。也許EF認爲這是這種情況下的PK?你玩過那個映射嗎? – 2011-06-18 22:10:41

回答

0

我在這裏回答我的問題...

我的問題曾與如何複合主鍵在我的映射類被定義做。當定義使用EF代碼第一次複合主鍵,你必須像這樣定義它:

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成爲主鍵(不是組合鍵)。像我在上面的第一個代碼示例中那樣定義它們,可以解決問題。

希望這可以幫助別人!