EF6 + Ninject: данные не сохранены с использованием InRequestScope - PullRequest
0 голосов
/ 07 февраля 2020

Я создаю проект 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» каждого репозитория, который я использую во время вызова моего контроллера. Как лучше поступить?

...