EF Code Первая проблема в CommitTransaction - с использованием шаблона Repository - PullRequest
0 голосов
/ 17 июня 2011

У меня проблема с EF 4.1 с использованием «Code First».Позвольте мне настроить ситуацию, прежде чем я начну публиковать любой код.У меня есть мой класс DBContext, называемый MemberSalesContext, в проекте библиотеки классов под названием Data.EF.У меня есть свои POCO в отдельном проекте библиотеки классов под названием Domain.Мой проект домена ничего не знает о Entity Framework, никаких ссылок, ничего.Мой проект Data.EF имеет ссылку на проект Domain, так что мой контекстный класс БД может соединить все в моих классах отображения, расположенных в Data.EF.Mapping.Я делаю все сопоставления в этом пространстве имен, используя класс EntityTypeConfiguration из EntityFramework.Все это довольно стандартные вещи.Поверх Entity Framework я использую шаблон репозитория и шаблон спецификации.

В моей таблице базы данных 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 убедитесь, что значения ключа уникальны. "

В этой ситуации изменения базы данных на второй итерации не были успешно завершены, ноИзменения в первой итерации были.Я убедился, что все объекты во внешнем и внутреннем циклах уникальны и привязаны к первичным ключам базы данных.

Есть ли что-то, чего мне здесь не хватает?Я готов дополнить мои примеры кода, если это окажется полезным.Я сделал все возможное, чтобы устранить эту проблему, за исключением изменения составного первичного ключа, установленного в таблице базы данных.

Может кто-нибудь помочь ???Большое спасибо заранее!Кстати, простите за длинный пост!

1 Ответ

0 голосов
/ 21 июня 2011

Я отвечаю на свой вопрос здесь ...

Моя проблема была связана с тем, как составной первичный ключ определялся в моем классе отображения.При определении составного первичного ключа с помощью 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 стал первичным ключом (не составным).Определение их встроенным, как в моем первом примере кода выше, решает проблему.

Надеюсь, это кому-нибудь поможет!

...