Как управлять жизнью EFCore DbContext в веб-API ASP.NET Core? - PullRequest
0 голосов
/ 02 октября 2019

Я реализую 3-уровневую архитектуру, и мой вопрос: Куда мне звонить SaveChanges(); (если применимо)? Где я должен создать и совершить транзакцию? Пожалуйста, прочитайте весь вопрос.

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

  1. Ссылка EFCore в проекте Core. Вставьте DbContext в класс Service и с помощью метода внедрите его в слой данных. Например:

    public class ItemService
    {
        private MyContext _ctx;
        private readonly IItemData _itemData;   
    
        public ItemService(IItemData itemData)
        {
            _itemData = itemData;
        }   
    
        public void InjectCtx(MyContext ctx)
        {
            _ctx = ctx;
            _itemData.InjectCtx(_ctx);
        }   
    
        public void Operation(int itemId)
        {
            using (var transaction = _ctx.Database.BeginTransaction())
            {
                //Do something  
    
                _ctx.SaveChanges();
                transaction.Commit();
            }
        }
    }
    
  2. Создайте интерфейс в Core для объекта Единица работы. Которые будут иметь методы SaveChanges и Transaction, Commit. Затем реализуйте их на уровне данных и, соответственно, вызовите в классе Service.

Вот код. У меня есть 3 проекта:

  • Инфраструктура (Данные) - ссылки на Core и EFCore
  • Core (бизнес-логика) - без зависимостей
  • Web API (проект MVC) - ссылается на Core, Data и EFCore

- Core (без зависимостей, даже не ссылка на EFCore):

public class ItemDto
{
    public int ItemId { get; set; }
    public string Name { get; set; }
}

public interface IItemData
{
    Task<ItemDto> GetItem(int itemId);
}

public class ItemService
{
    private readonly IItemData _itemData;

    public ItemService(IItemData itemData)
    {
        _itemData = itemData;
    }

    public async Task<ItemDto> GetItem(int itemId)
    {
        return await _itemData.GetItem(itemId);
    }
}

- Данные (ссылка на базовый проект и EFCore)

   public class ItemData : IItemData
    {
        private readonly MyContext _ctx;

        public ItemData(MyContext ctx)
        {
            _ctx = ctx;
        }

        public async Task<ItemDto> GetItem(int itemId)
        {
            var item = await _ctx.Items
                .Where(i => i.ItemId == itemId)
                .Select(row => new ItemDto()
                {
                    ItemId = row.ItemId,
                    Name = row.ItemName
                })
                .FirstOrDefaultAsync();
            return item;
        }
    }

- веб-API:

[Route("api/[controller]")]
[ApiController]
public class ItemsController : ControllerBase
{
    private readonly ItemService _itemService;

    public ItemsController(ItemService itemService)
    {
        _itemService = itemService;
    }

    [HttpGet("{itemId}")]
    public async Task<ItemDto> Get(int itemId)
    {
        return await _itemService.GetItem(itemId);
    }
}

1 Ответ

0 голосов
/ 02 октября 2019

Если вы действительно чувствуете, что вам нужны транзакции, я бы попытался обработать их «выше» ваших служб, чтобы эти службы не отвечали за их обработку.

Один из способов сделать это может быть:

  • Используйте область действия DbContext.

  • Когда запускается запрос или вызывается метод обслуживания, создайте транзакцию с помощью Database.BeginTransaction().

  • Ваш уровень обслуживания вызывает ваш уровень данных и обрабатывает бизнес-логику.

  • Ваш уровень данных применяется .SaveChanges() везде, где это необходимо.

  • Когда запрос завершается или завершается вызов метода службы, либо выполните transaction.Commit() или transaction.Rollback().

Способ достижения этих транзакций создания / фиксации/ Откатывать, не делая ваши сервисы отзывчивыми, можно с помощью фильтров, промежуточного программного обеспечения или перехватчиков .

...