Browse Source

Implemented basic unit of work.

pull/81/head
Halil İbrahim Kalkan 10 years ago
parent
commit
dcbb625d88
  1. 5
      src/AbpDesk/AbpDesk.Application.Contracts/AbpDesk/Tickets/ITicketAppService.cs
  2. 30
      src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs
  3. 3
      src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/TicketLister.cs
  4. 7
      src/AbpDesk/AbpDesk.UI/Controllers/TicketsController.cs
  5. 56
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Repositories/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs
  6. 6
      src/Volo.Abp/Volo/Abp/Data/DefaultConnectionStringResolver.cs
  7. 18
      src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs
  8. 11
      src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs
  9. 11
      src/Volo.Abp/Volo/Abp/Uow/IUnitOfWorkManager.cs
  10. 55
      src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs
  11. 11
      test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/Tickets/TicketAppService_Tests.cs

5
src/AbpDesk/AbpDesk.Application.Contracts/AbpDesk/Tickets/ITicketAppService.cs

@ -1,4 +1,5 @@
using AbpDesk.Tickets.Dtos; using System.Threading.Tasks;
using AbpDesk.Tickets.Dtos;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Application.Services.Dtos; using Volo.Abp.Application.Services.Dtos;
@ -6,6 +7,6 @@ namespace AbpDesk.Tickets
{ {
public interface ITicketAppService : IApplicationService public interface ITicketAppService : IApplicationService
{ {
ListResultDto<TicketDto> GetAll(GetAllTicketsInput input); Task<ListResultDto<TicketDto>> GetAll(GetAllTicketsInput input);
} }
} }

30
src/AbpDesk/AbpDesk.Application/AbpDesk/Tickets/TicketAppService.cs

@ -1,8 +1,10 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using AbpDesk.Tickets.Dtos; using AbpDesk.Tickets.Dtos;
using Volo.Abp.Application.Services.Dtos; using Volo.Abp.Application.Services.Dtos;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq.Extensions; using Volo.Abp.Linq.Extensions;
using Volo.Abp.Uow;
using Volo.ExtensionMethods; using Volo.ExtensionMethods;
namespace AbpDesk.Tickets namespace AbpDesk.Tickets
@ -10,22 +12,32 @@ namespace AbpDesk.Tickets
public class TicketAppService : ITicketAppService public class TicketAppService : ITicketAppService
{ {
private readonly IQueryableRepository<Ticket, int> _ticketRepository; private readonly IQueryableRepository<Ticket, int> _ticketRepository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public TicketAppService(IQueryableRepository<Ticket, int> ticketRepository) public TicketAppService(
IQueryableRepository<Ticket, int> ticketRepository,
IUnitOfWorkManager unitOfWorkManager
)
{ {
_ticketRepository = ticketRepository; _ticketRepository = ticketRepository;
_unitOfWorkManager = unitOfWorkManager;
} }
public ListResultDto<TicketDto> GetAll(GetAllTicketsInput input) public async Task<ListResultDto<TicketDto>> GetAll(GetAllTicketsInput input)
{ {
var tickets = _ticketRepository using (var unitOfWork = _unitOfWorkManager.Begin())
.WhereIf( {
!input.Filter.IsNullOrWhiteSpace(), var tickets = _ticketRepository
t => t.Title.Contains(input.Filter) || t.Body.Contains(input.Filter) .WhereIf(
).Select(t => new TicketDto { Id = t.Id, Title = t.Title, Body = t.Body }) !input.Filter.IsNullOrWhiteSpace(),
.ToList(); t => t.Title.Contains(input.Filter) || t.Body.Contains(input.Filter)
).Select(t => new TicketDto { Id = t.Id, Title = t.Title, Body = t.Body })
.ToList();
return new ListResultDto<TicketDto>(tickets); await unitOfWork.CompleteAsync();
return new ListResultDto<TicketDto>(tickets);
}
} }
} }
} }

3
src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/TicketLister.cs

@ -1,6 +1,7 @@
using System; using System;
using AbpDesk.Tickets; using AbpDesk.Tickets;
using AbpDesk.Tickets.Dtos; using AbpDesk.Tickets.Dtos;
using Volo.Abp.Threading;
using Volo.DependencyInjection; using Volo.DependencyInjection;
namespace AbpDesk.ConsoleDemo namespace AbpDesk.ConsoleDemo
@ -16,7 +17,7 @@ namespace AbpDesk.ConsoleDemo
public void List() public void List()
{ {
var result = _ticketAppService.GetAll(new GetAllTicketsInput()); var result = AsyncHelper.RunSync(() => _ticketAppService.GetAll(new GetAllTicketsInput()));
foreach (var ticket in result.Items) foreach (var ticket in result.Items)
{ {

7
src/AbpDesk/AbpDesk.UI/Controllers/TicketsController.cs

@ -1,4 +1,5 @@
using AbpDesk.Models.Tickets; using System.Threading.Tasks;
using AbpDesk.Models.Tickets;
using AbpDesk.Tickets; using AbpDesk.Tickets;
using AbpDesk.Tickets.Dtos; using AbpDesk.Tickets.Dtos;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -15,9 +16,9 @@ namespace AbpDesk.Controllers
_ticketAppService = ticketAppService; _ticketAppService = ticketAppService;
} }
public IActionResult Index(GetAllTicketsInput input) public async Task<IActionResult> Index(GetAllTicketsInput input)
{ {
var result = _ticketAppService.GetAll(input); var result = await _ticketAppService.GetAll(input);
var model = new IndexViewModel var model = new IndexViewModel
{ {

56
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Repositories/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs

@ -1,31 +1,65 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Uow; using Volo.Abp.Uow;
namespace Volo.Abp.Repositories.EntityFrameworkCore namespace Volo.Abp.Repositories.EntityFrameworkCore
{ {
public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbContext> public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbContext>
where TDbContext : AbpDbContext<TDbContext> where TDbContext : AbpDbContext<TDbContext>
{ {
private readonly TDbContext _dbContext;
private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IConnectionStringResolver _connectionStringResolver;
public UnitOfWorkDbContextProvider( public UnitOfWorkDbContextProvider(
TDbContext dbContext, IUnitOfWorkManager unitOfWorkManager,
IUnitOfWorkManager unitOfWorkManager) //TODO: Should create this dynamically inside a unit of work. IConnectionStringResolver connectionStringResolver)
{ {
_dbContext = dbContext;
_unitOfWorkManager = unitOfWorkManager; _unitOfWorkManager = unitOfWorkManager;
_connectionStringResolver = connectionStringResolver;
} }
public TDbContext GetDbContext() public TDbContext GetDbContext()
{ {
//if (_unitOfWorkManager.Current == null) var unitOfWork = _unitOfWorkManager.Current;
//{ if (unitOfWork == null)
// throw new AbpException("A DbContext can only be created inside a unit of work!"); {
//} throw new AbpException("A DbContext can only be created inside a unit of work!");
}
var moduleName = "";//TODO: Get module name from DbContext?
var dbContextKey = $"{moduleName}_{typeof(TDbContext).FullName}_{_connectionStringResolver.Resolve(moduleName)}";
var databaseApi = unitOfWork.GetOrAddDatabaseApi(
dbContextKey,
() => new DbContextDatabaseApi<TDbContext>(
unitOfWork.ServiceProvider.GetRequiredService<TDbContext>()
));
return ((DbContextDatabaseApi<TDbContext>)databaseApi).DbContext;
}
}
public class DbContextDatabaseApi<TDbContext> : IDatabaseApi
where TDbContext : AbpDbContext<TDbContext>
{
public TDbContext DbContext { get; }
return _dbContext; public DbContextDatabaseApi(TDbContext dbContext)
{
DbContext = dbContext;
}
public Task SaveChangesAsync()
{
return DbContext.SaveChangesAsync();
}
public Task CommitAsync()
{
return DbContext.SaveChangesAsync();
} }
} }
} }

6
src/Volo.Abp/Volo/Abp/Data/ConnectionStringResolver.cs → src/Volo.Abp/Volo/Abp/Data/DefaultConnectionStringResolver.cs

@ -5,11 +5,11 @@ using Volo.ExtensionMethods.Collections.Generic;
namespace Volo.Abp.Data namespace Volo.Abp.Data
{ {
public class ConnectionStringResolver : IConnectionStringResolver, ITransientDependency public class DefaultConnectionStringResolver : IConnectionStringResolver, ITransientDependency
{ {
private readonly DbConnectionOptions _options; //TODO: Use IOptionsSnapshot private readonly DbConnectionOptions _options; //TODO: Use IOptionsSnapshot?
public ConnectionStringResolver(IOptions<DbConnectionOptions> options) public DefaultConnectionStringResolver(IOptions<DbConnectionOptions> options)
{ {
_options = options.Value; _options = options.Value;
} }

18
src/Volo.Abp/Volo/Abp/Uow/ChildUnitOfWork.cs

@ -1,3 +1,4 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
@ -5,6 +6,8 @@ namespace Volo.Abp.Uow
{ {
internal class ChildUnitOfWork : IUnitOfWork internal class ChildUnitOfWork : IUnitOfWork
{ {
public IServiceProvider ServiceProvider => _parent.ServiceProvider;
private readonly IUnitOfWork _parent; private readonly IUnitOfWork _parent;
public ChildUnitOfWork([NotNull] IUnitOfWork parent) public ChildUnitOfWork([NotNull] IUnitOfWork parent)
@ -23,6 +26,21 @@ namespace Volo.Abp.Uow
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }
public IDatabaseApi FindDatabaseApi(string id)
{
return _parent.FindDatabaseApi(id);
}
public IDatabaseApi GetOrAddDatabaseApi(string id, Func<IDatabaseApi> factory)
{
return _parent.GetOrAddDatabaseApi(id, factory);
}
public IDatabaseApi AddDatabaseApi(string id, IDatabaseApi databaseApi)
{
return _parent.AddDatabaseApi(id, databaseApi);
}
public void Dispose() public void Dispose()
{ {

11
src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs

@ -1,11 +1,20 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.DependencyInjection; using Volo.DependencyInjection;
namespace Volo.Abp.Uow namespace Volo.Abp.Uow
{ {
public interface IUnitOfWork : IDisposable, ITransientDependency public interface IUnitOfWork : IDisposable, IServiceProviderAccessor, ITransientDependency
{ {
[CanBeNull]
IDatabaseApi FindDatabaseApi([NotNull] string id);
[NotNull]
IDatabaseApi GetOrAddDatabaseApi(string id, Func<IDatabaseApi> factory);
IDatabaseApi AddDatabaseApi(string id, IDatabaseApi databaseApi);
Task SaveChangesAsync(); Task SaveChangesAsync();
Task CompleteAsync(); Task CompleteAsync();

11
src/Volo.Abp/Volo/Abp/Uow/IUnitOfWorkManager.cs

@ -1,4 +1,6 @@
namespace Volo.Abp.Uow using System.Threading.Tasks;
namespace Volo.Abp.Uow
{ {
public interface IUnitOfWorkManager public interface IUnitOfWorkManager
{ {
@ -6,4 +8,11 @@
IUnitOfWork Begin(); IUnitOfWork Begin();
} }
public interface IDatabaseApi
{
Task SaveChangesAsync();
Task CommitAsync();
}
} }

55
src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs

@ -1,23 +1,68 @@
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.ExtensionMethods.Collections.Generic;
namespace Volo.Abp.Uow namespace Volo.Abp.Uow
{ {
public class UnitOfWork : IUnitOfWork public class UnitOfWork : IUnitOfWork
{ {
public IServiceProvider ServiceProvider { get; }
private readonly Dictionary<string, IDatabaseApi> _databaseApis;
public UnitOfWork(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
_databaseApis = new Dictionary<string, IDatabaseApi>();
}
public void Dispose() public void Dispose()
{ {
throw new NotImplementedException(); //TODO: Remove itself from IUnitOfWorkManager
}
public async Task SaveChangesAsync()
{
foreach (var databaseApi in _databaseApis.Values)
{
await databaseApi.SaveChangesAsync();
}
} }
public Task SaveChangesAsync() public async Task CompleteAsync()
{ {
throw new NotImplementedException(); foreach (var databaseApi in _databaseApis.Values)
{
await databaseApi.CommitAsync();
}
} }
public Task CompleteAsync() public IDatabaseApi FindDatabaseApi(string id)
{ {
throw new NotImplementedException(); return _databaseApis.GetOrDefault(id);
}
public IDatabaseApi GetOrAddDatabaseApi(string id, Func<IDatabaseApi> factory)
{
Check.NotNull(id, nameof(id));
Check.NotNull(factory, nameof(factory));
return _databaseApis.GetOrAdd(id, factory);
}
public IDatabaseApi AddDatabaseApi(string id, IDatabaseApi databaseApi)
{
Check.NotNull(id, nameof(id));
Check.NotNull(databaseApi, nameof(databaseApi));
if (_databaseApis.ContainsKey(id))
{
throw new AbpException($"There is already a database api with same id: {id}");
}
return _databaseApis[id] = databaseApi;
} }
} }
} }

11
test/AbpDesk/AbpDesk.Application.Tests/AbpDesk/Tickets/TicketAppService_Tests.cs

@ -1,4 +1,5 @@
using AbpDesk.Tickets.Dtos; using System.Threading.Tasks;
using AbpDesk.Tickets.Dtos;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Shouldly; using Shouldly;
using Xunit; using Xunit;
@ -15,11 +16,11 @@ namespace AbpDesk.Tickets
} }
[Fact] [Fact]
public void GetAll_Test() public async Task GetAll_Test()
{ {
//Act //Act
var result = _ticketAppService.GetAll(new GetAllTicketsInput()); var result = await _ticketAppService.GetAll(new GetAllTicketsInput());
//Assert //Assert
@ -27,11 +28,11 @@ namespace AbpDesk.Tickets
} }
[Fact] [Fact]
public void GetAll_Filtered_Test() public async Task GetAll_Filtered_Test()
{ {
//Act //Act
var result = _ticketAppService.GetAll(new GetAllTicketsInput { Filter = "non-existing-text" }); var result = await _ticketAppService.GetAll(new GetAllTicketsInput { Filter = "non-existing-text" });
//Assert //Assert

Loading…
Cancel
Save