Я создаю проект Web Api с Ninject в качестве DI-контейнера. Я разделяю слои, имеющие классы для бизнеса, доступа к данным и моделей. Моя цель состояла в том, чтобы использовать транзакцию на верхнем уровне бизнес-логики c и сделать инъекцию DbContext через конструктор доступа к данным.
Конфигурация Ninject:
public override void Load()
{
Bind<MyDbContext>().ToSelf().InRequestScope();
...
}
Пример Контроллер API:
public class ExampleController : ApiController
{
private readonly IMyObjectFactory _myFactory;
public ExampleController(IMyObjectFactory myFactory)
{
_myFactory = myFactory;
}
public HttpResponseMessage Post([FromBody] MyDto dto)
{
var manager = _myFactory.CreateFactory(dto.paramId);
// CreateFactory returns an object "IMyObject"
manager.DoSomething(dto.paramId);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
Контроллер просто вызывает бизнес-объект, где находятся все логики. Я поместил туда некоторые объекты моего уровня доступа к данным, чтобы выполнять различные действия в транзакции. В данный момент мне не нужно иметь доступ к DBContext, все, что мне нужно, это внедрить мои объекты доступа к данным:
public class MyBusinessObject : IMyBusinessObject
{
private readonly IDataWriterA _dataWriterA;
private readonly IDataWriterB _dataWriterB;
private readonly IDataReader _dataReader;
private MyModels.MyEntity _entity;
private readonly int _id;
public class MyBusinessObject : IMyBusinessObject
{
private readonly IDataWriterA _dataWriterA;
private readonly IDataWriterB _dataWriterB;
private readonly IDataReader _dataReader;
private MyModels.MyEntity _entity;
private readonly int _id;
public MyBusinessObject(IDataWriterA dataWriterA, IDataReader dataReader, IDataWriterBFactory dataWriterBFactory, int id)
{
_dataWriterA = dataWriterA;
_dataReader = dataReader;
_id = id;
// Get the entity object from a reader class
// It performs a FirstOrDefault on Entity table after injection of the DbContext
_entity = _dataReader.Get(_idPartita);
// This factory create classes with _entity as parameter, so i can use it instead of reading everytime from an id
// As you can see i haven't passed the IDataWriterB object into the constructor
_dataWriterB = dataWriterBFactory.CreateDataWriterB(_entity);
}
public void DoSomething()
{
using (var ts = new TransactionScope())
{
_dataWriterA.DoAOperation(_entity);
_dataWriterB.DoBOperation(_entity);
ts.Complete();
}
}
}
}
Теперь суть проблемы: реализация классов доступа к данным:
public class DataWriterA : IDataWriterA
{
private readonly MyModels.MyEntity _entity;
private readonly MyDbContext _ctx;
public DataRisultatoPartita(MyDbContext ctx, MyModels.MyEntity entity)
{
_ctx = ctx;
_entity = entity;
}
public void DoAOperation()
{
if (_entity == null)
throw new Exception("Object null");
_entity.FieldA = "rrr";
_entity.FieldB = "ttt";
_ctx.SaveChanges();
}
}
public class DataWriterB : IDataWriterB
{
private readonly MyModels.MyEntity _entity;
private readonly MyDbContext _ctx;
public DataRisultatoPartita(MyDbContext ctx, MyModels.MyEntity entity)
{
_ctx = ctx;
_entity = entity;
}
public void DoBOperation()
{
if (_entity == null)
throw new Exception("Object null");
_entity.FieldC = "aaa";
_entity.FieldD = "bbb";
_ctx.SaveChanges();
}
}
Как уже говорилось ранее, если я использую «InRequestScope», данные не сохраняются, вместо этого, если я использую «InSingletonScope», это работает. Я хотел бы использовать область запроса, но не могу понять, как с ней справиться.
ОБНОВЛЕНИЕ
После многих испытаний я понял, что InRequestScope работает хорошо, но проблема не сохраняющихся данных связана с тем, что при переходе из хранилища в другое я вынужден каждый раз получать сущность! В моем примере ниже я пытаюсь получить доступ к своей сущности, как если бы она была привязана к контексту, но это не так, и я не смог присоединить ее, потому что я получил ошибку. Чтобы это работало, мне нужно реализовать класс репозитория следующим образом:
public class DataWriterA : IDataWriterA
{
private readonly MyModels.MyEntity _entity;
private readonly MyDbContext _ctx;
public DataRisultatoPartita(MyDbContext ctx, MyModels.MyEntity entity)
{
_ctx = ctx;
_entity = entity;
// THIS LINE NECESSARY TO ATTACH THE ENTITY
_entity = _ctx.DbEntity.FirstOrDefault(x => x.Id == entity.Id);
}
public void DoAOperation()
{
if (_entity == null)
throw new Exception("Object null");
_entity.FieldA = "rrr";
_entity.FieldB = "ttt";
_ctx.SaveChanges();
}
}
Я хочу избегать отправки «Get» каждого репозитория, который я использую во время вызова моего контроллера. Как лучше поступить?