0

У меня есть веб-служба WCF, которая в настоящее время сталкивается с некоторыми проблемами параллелизма. На данный момент нагрузка небольшая, но ожидается, что в ближайшие несколько дней она увеличится.Операция не может быть завершена, потому что DbContext был удален - LifestylePerWcfOperation

Общая настройка WCF, Entity Framework 6, Injection Dependency (Castle Windsor), UnitOfWork & Образцовый шаблон.

я настроить стресс-тест, который попадает на службу параллельно, и я могу воссоздать ошибки параллелизма, такие как ....

  • сообщалось ошибка при совершении транзакции базы данных, но он не может определить, являются ли транзакция выполнена успешно или сбой на сервере базы данных.
  • Изменения в базе данных были успешно завершены, но при обновлении контекста объекта произошла ошибка. Объект ObjectContext может находиться в несогласованном состоянии. Внутреннее сообщение об исключении: AcceptChanges не может продолжаться, потому что ключевые значения объекта конфликтуют с другим объектом в ObjectStateManager. Перед вызовом AcceptChanges убедитесь, что значения ключа уникальны.

  • Идентификатор объекта 'property' является частью ключевой информации объекта и не может быть изменен.

Из исследования я сделал, это выглядит, как я должен инъекционным мой DbContext с LifestylePerWcfOperation. В настоящее время это делается с LifestyleTransient.

Когда я перейти на LifestylePerWcfOperation и повторите тест, я получаю это каждый раз, когда:

  • Операция не может быть завершена, поскольку DbContext были захоронены

Так что я на самом деле есть 2 вопроса :

  1. Я исхожу в предположении, что истинное решение этого вопроса заключается в переключении моего DbContext на LifestylePerWcfOperation?
  2. Если да, то понятно, почему мой DbContext получает ворс?

Вот код:

AppStart.cs:

IocContainer.AddFacility<TypedFactoryFacility>(); 

IocContainer.Register(Component.For<IRepositoryFactory>().AsFactory()); 

IocContainer.Register(Component.For<IUnitOfWork>() 
           .ImplementedBy<UnitOfWork>) 
          /*.LifestylePerWcfOperation()*/); 

IocContainer.Register(Component.For(typeof(IDbContext)) 
           .ImplementedBy(dbContextType) 
           .DependsOn(Dependency.OnValue("connectionString", String.Format(ConfigurationManager.ConnectionStrings["---"].ConnectionString)))); 
          /*.LifestylePerWcfOperation()*/ 

IocContainer.Register(Component.For(typeof(IRepository<>)) 
           .ImplementedBy(typeof(Repository<>)) 
           .LifestyleTransient() 
          /*.LifestylePerWcfOperation()*/); 

// Register the actual dbContextType so consuming applications can access it without an interface 
IocContainer.Register(Component.For(dbContextType) 
           .Named(dbContextType.Name) 
           .DependsOn(Dependency.OnValue("connectionString", String.Format(ConfigurationManager.ConnectionStrings["----"].ConnectionString))); 
          /*.LifestylePerWcfOperation()*/ 

UnitOfWork.cs:

public class UnitOfWork : IUnitOfWork 
{ 
    private readonly IDbContext _dbContext; 
    private bool _disposed; 

    public UnitOfWork(IDbContext dbContext) 
    { 
     _dbContext = dbContext; 
    } 

    public int Commit() 
    { 
     return _dbContext.SaveChanges(); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!_disposed) 
     { 
      if (disposing) 
      { 
       _dbContext.Dispose(); 
      } 

      _disposed = true; 
     } 
    } 
} 

Repository.cs

public class Repository<T> : IRepository<T> where T : class 
{ 
    private readonly IDbSet<T> _dbset; 

    public Repository(IDbContext dbContext) 
    { 
     _dbset = dbContext.Set<T>(); 
    } 

    public IQueryable<T> Query() 
    { 
     return _dbset.AsQueryable(); 
    } 

    public virtual IEnumerable<T> GetAll() 
    { 
     return _dbset.AsEnumerable(); 
    } 

    public virtual void Add(T entity) 
    { 
     _dbset.Add(entity); 
    } 

    public virtual void Delete(T entity) 
    { 
     _dbset.Remove(entity); 
    } 

    public virtual void Delete(ICollection<T> entities) 
    { 
     entities.ToList().ForEach(Delete); 
    } 
} 

При необходимости я могу добавить дополнительный код. Спасибо за любую помощь, которую вы можете предоставить!

+0

В вашей регистрации DBContext не указывается стиль жизни, поэтому применяется значение Windsor по умолчанию: Singleton. Это, безусловно, вызовет проблемы. –

ответ

1

В ответ на вопросы:

1) Вы правы в изменении образа жизни, чтобы LifestylePerWcfOperation

2) Вы Располагая DbContext изнутри UnitOfWork. DBContext вводится Windsor, поэтому Windsor будет вызывать Dispose на нем. При использовании windsor никогда не удаляйте инъецируемые объекты, только если вы создали объект с использованием фабрики или Resolve, вам нужно освободить (не удалять) объект, чтобы Windsor мог его утилизировать.

Я бы ожидал, что с помощью LifeStylePerWcfOperations и удаления кода Dispose код должен работать нормально.

Удачи, Marwijn.

+0

Спасибо Marwijn! Это был первый шаг. Были и другие изменения, которые мне нужно было сделать в коде, который не был отправлен здесь. –

0

Я смог решить эту проблему.

Исправление было именно тем, что упоминалось в Marwijn. После исправления этого я все равно получил аналогичную ошибку.

Последнее исправление должно было сделать каждую регистрацию и сделать ее LifestylePerWcfOperation. У меня были некоторые объекты, зарегистрированные с более коротким сроком службы, и поэтому DbContext также располагался с этими объектами.

Если кто-либо получает эту ошибку, и вы используете Castle/EF, не запрещайте явно использовать DbContext (пусть это делает Castle) и делают все LifestylePerWcfOperation. Это должно исправить это. Затем вернитесь назад и выборочно сбросьте образ жизни любых объектов, которые вы не хотите быть LifestylePerWcfOperation, и убедитесь, что он не сломается, когда вы идете.