I'm implementing 3-tier architecture, and my question is: Where should I call SaveChanges();
(if applicable)? Where should I create and commit transaction? Please read the whole question.
In the code below I have only "read" operation, but you'll get the idea of the architecture. I'm struggling with the decision, but I came out with two options:
Reference EFCore in the
Core
project. Inject DbContext intoService
class and via method inject it to the Data layer. Like this: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(); } } }
Create an interface in the
Core
for Unit of work object. Which will have SaveChanges and Transaction, Commit methods. Then implement them in the Data layer and call accordingly in theService
class.
Here is the code. I have 3 projects:
- Infrstructure (Data) - references Core and EFCore
- Core (Business logic) - no dependencies
- Web API (MVC project) - references Core, Data and EFCore
- Core (no dependencies, not even a reference to 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);
}
}
- Data (reference to Core project and 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;
}
}
- Web 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);
}
}