From ce0063f925b2d08777e0ac6ee5c200b930825048 Mon Sep 17 00:00:00 2001 From: Carlos Carrero Date: Wed, 8 Nov 2017 13:17:35 -0500 Subject: [PATCH 1/6] Refactoring to implement the un it of work and repository pattern with entity framework core --- .../Entities/Entity.cs | 7 + .../PagedList/IPagedList.cs | 23 ++ .../Repository/IQueryableRepository.cs | 67 ++++++ .../Repository/IRepository.cs | 36 +++ .../Repository/IRepositoryFactory.cs | 9 + .../UnitOfWork/IUnitOfWork.cs | 22 ++ .../UnitOfWork/IUnitOfWorkOfT.cs | 12 + ...Yuxi.SharedKernel.Data.Abstractions.csproj | 19 ++ .../IEnumerablePagedListExtensions.cs | 16 ++ .../PagedList/IQueryablePageListExtensions.cs | 38 ++++ .../PagedList/PagedList.cs | 105 +++++++++ .../Repository/QueryableRepository.cs | 209 ++++++++++++++++++ .../Repository/Repository.cs | 92 ++++++++ .../Repository/UnitOfWork.cs | 135 +++++++++++ .../UnitOfWorkServiceCollectionExtensions.cs | 58 +++++ ...aredKernel.Data.EntityFrameworkCore.csproj | 21 ++ .../Contracts/IStorableEntityMapper.cs | 5 +- ...teGenericRepositoryReliableStateManager.cs | 83 ++++--- .../Yuxi.SharedKernel.Storage.csproj | 5 +- Yuxi.SharedKernel.sln | 18 +- 20 files changed, 921 insertions(+), 59 deletions(-) create mode 100644 Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs create mode 100644 Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs create mode 100644 Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs create mode 100644 Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs create mode 100644 Yuxi.SharedKernel.Data.Abstractions/Repository/IRepositoryFactory.cs create mode 100644 Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs create mode 100644 Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs create mode 100644 Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IEnumerablePagedListExtensions.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/PagedList.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj diff --git a/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs b/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs new file mode 100644 index 0000000..c9d62c3 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs @@ -0,0 +1,7 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.Entities +{ + public abstract class Entity + { + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs new file mode 100644 index 0000000..35d3bec --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs @@ -0,0 +1,23 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.PagedList +{ + using System.Collections.Generic; + + public interface IPagedList + { + int IndexFrom { get; } + + int PageIndex { get; } + + int PageSize { get; } + + int TotalCount { get; } + + int TotalPages { get; } + + IList Items { get; } + + bool HasPreviousPage { get; } + + bool HasNextPage { get; } + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs b/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs new file mode 100644 index 0000000..e748502 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs @@ -0,0 +1,67 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.Repository +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore.Query; + using PagedList; + using Specification.Contracts; + + public interface IQueryableRepository + { + IPagedList GetPagedList(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true); + + Task> GetPagedListAsync(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)); + + IPagedList GetPagedList(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true) where TResult : class; + + Task> GetPagedListAsync(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)) where TResult : class; + + TEntity GetFirstOrDefault(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true); + + TResult GetFirstOrDefault(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true); + + IQueryable FromSql(string sql, params object[] parameters); + + TEntity Find(params object[] keyValues); + + Task FindAsync(params object[] keyValues); + + Task FindAsync(object[] keyValues, CancellationToken cancellationToken); + + int Count(Expression> predicate = null); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs new file mode 100644 index 0000000..fdbbbe9 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs @@ -0,0 +1,36 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.Repository +{ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public interface IRepository where TEntity : class + { + void Insert(TEntity entity); + + void Insert(params TEntity[] entities); + + void Insert(IEnumerable entities); + + Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)); + + Task InsertAsync(params TEntity[] entities); + + Task InsertAsync(IEnumerable entities, + CancellationToken cancellationToken = default(CancellationToken)); + + void Update(TEntity entity); + + void Update(params TEntity[] entities); + + void Update(IEnumerable entities); + + void Delete(object id); + + void Delete(TEntity entity); + + void Delete(params TEntity[] entities); + + void Delete(IEnumerable entities); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepositoryFactory.cs b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepositoryFactory.cs new file mode 100644 index 0000000..75456b5 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepositoryFactory.cs @@ -0,0 +1,9 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.Repository +{ + public interface IRepositoryFactory + { + IRepository GetRepository() where TEntity : class; + + IQueryableRepository GetQueryableRepository() where TEntity : class; + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs new file mode 100644 index 0000000..25eb5ef --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs @@ -0,0 +1,22 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.UnitOfWork +{ + using System; + using System.Linq; + using System.Threading.Tasks; + using Repository; + + public interface IUnitOfWork : IDisposable + { + IRepository GetRepository() where TEntity : class; + + IQueryableRepository GetQueryableRepository() where TEntity : class; + + int SaveChanges(bool ensureAutoHistory = false); + + Task SaveChangesAsync(bool ensureAutoHistory = false); + + int ExecuteSqlCommand(string sql, params object[] parameters); + + IQueryable FromSql(string sql, params object[] parameters) where TEntity : class; + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs new file mode 100644 index 0000000..c5d3818 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs @@ -0,0 +1,12 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.UnitOfWork +{ + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + + public interface IUnitOfWork : IUnitOfWork where TContext : DbContext + { + TContext DbContext { get; } + + Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj b/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj new file mode 100644 index 0000000..da84d8b --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + + diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IEnumerablePagedListExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IEnumerablePagedListExtensions.cs new file mode 100644 index 0000000..b9cff76 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IEnumerablePagedListExtensions.cs @@ -0,0 +1,16 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.PagedList +{ + using System; + using System.Collections.Generic; + using Abstractions.PagedList; + + internal static class IEnumerablePagedListExtensions + { + public static IPagedList ToPagedList(this IEnumerable source, int pageIndex, int pageSize, + int indexFrom = 0) => new PagedList(source, pageIndex, pageSize, indexFrom); + + public static IPagedList ToPagedList(this IEnumerable source, + Func, IEnumerable> converter, int pageIndex, int pageSize, + int indexFrom = 0) => new PagedList(source, converter, pageIndex, pageSize, indexFrom); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs new file mode 100644 index 0000000..3187996 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs @@ -0,0 +1,38 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.PagedList +{ + using System; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + using Abstractions.PagedList; + + internal static class IQueryablePageListExtensions + { + public static async Task> ToPagedListAsync(this IQueryable source, int pageIndex, + int pageSize, int indexFrom = 0, CancellationToken cancellationToken = default(CancellationToken)) + { + if (indexFrom > pageIndex) + { + throw new ArgumentException( + $"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex"); + } + + var count = await source.CountAsync(cancellationToken).ConfigureAwait(false); + var items = await source.Skip((pageIndex - indexFrom) * pageSize) + .Take(pageSize).ToListAsync(cancellationToken).ConfigureAwait(false); + + var pagedList = new PagedList() + { + PageIndex = pageIndex, + PageSize = pageSize, + IndexFrom = indexFrom, + TotalCount = count, + Items = items, + TotalPages = (int) Math.Ceiling(count / (double) pageSize) + }; + + return pagedList; + } + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/PagedList.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/PagedList.cs new file mode 100644 index 0000000..1ed2e2a --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/PagedList.cs @@ -0,0 +1,105 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.PagedList +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Abstractions.PagedList; + + internal class PagedList : IPagedList + { + public int PageIndex { get; set; } + + public int PageSize { get; set; } + + public int TotalCount { get; set; } + + public int TotalPages { get; set; } + + public int IndexFrom { get; set; } + + + public IList Items { get; set; } + + public bool HasPreviousPage => PageIndex - IndexFrom > 0; + + public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages; + + internal PagedList(IEnumerable source, int pageIndex, int pageSize, int indexFrom) + { + if (indexFrom > pageIndex) + { + throw new ArgumentException( + $"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex"); + } + + PageIndex = pageIndex; + PageSize = pageSize; + IndexFrom = indexFrom; + TotalCount = source.Count(); + TotalPages = (int) Math.Ceiling(TotalCount / (double) PageSize); + + Items = source.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToList(); + } + + internal PagedList() => Items = new T[0]; + } + + internal class PagedList : IPagedList + { + public int PageIndex { get; } + + public int PageSize { get; } + + public int TotalCount { get; } + + public int TotalPages { get; } + + public int IndexFrom { get; } + + public IList Items { get; } + + public bool HasPreviousPage => PageIndex - IndexFrom > 0; + + public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages; + + public PagedList(IEnumerable source, Func, IEnumerable> converter, + int pageIndex, int pageSize, int indexFrom) + { + if (indexFrom > pageIndex) + { + throw new ArgumentException( + $"indexFrom: {indexFrom} > pageIndex: {pageIndex}, must indexFrom <= pageIndex"); + } + + PageIndex = pageIndex; + PageSize = pageSize; + IndexFrom = indexFrom; + TotalCount = source.Count(); + TotalPages = (int) Math.Ceiling(TotalCount / (double) PageSize); + + var items = source.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToArray(); + + Items = new List(converter(items)); + } + + public PagedList(IPagedList source, Func, IEnumerable> converter) + { + PageIndex = source.PageIndex; + PageSize = source.PageSize; + IndexFrom = source.IndexFrom; + TotalCount = source.TotalCount; + TotalPages = source.TotalPages; + + Items = new List(converter(source.Items)); + } + } + + internal static class PagedList + { + public static IPagedList Empty() => new PagedList(); + + public static IPagedList From(IPagedList source, + Func, IEnumerable> converter) => + new PagedList(source, converter); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs new file mode 100644 index 0000000..a845c9e --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs @@ -0,0 +1,209 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Repository +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Query; + using Abstractions.PagedList; + using Abstractions.Repository; + using PagedList; + using Specification.Contracts; + + internal class QueryableRepository : IQueryableRepository where TEntity : class + { + protected readonly DbContext DbContext; + protected readonly DbSet DbSet; + + public QueryableRepository(DbContext dbContext) + { + DbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + DbSet = DbContext.Set(); + } + + public IPagedList GetPagedList(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true) + { + IQueryable query = DbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + } + + return orderBy != null + ? orderBy(query).ToPagedList(pageIndex, pageSize) + : query.ToPagedList(pageIndex, pageSize); + } + + public Task> GetPagedListAsync(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)) + { + IQueryable query = DbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + } + + return orderBy?.Invoke(query).ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken) ?? + query.ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken); + } + + public IPagedList GetPagedList(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true) + where TResult : class + { + IQueryable query = DbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + } + + return orderBy != null + ? orderBy(query).Select(selector).ToPagedList(pageIndex, pageSize) + : query.Select(selector).ToPagedList(pageIndex, pageSize); + } + + public Task> GetPagedListAsync(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)) + where TResult : class + { + IQueryable query = DbSet; + + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + } + + return orderBy?.Invoke(query).Select(selector) + .ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken) ?? query.Select(selector) + .ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken); + } + + public TEntity GetFirstOrDefault(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true) + { + IQueryable query = DbSet; + + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + } + + return orderBy != null ? orderBy(query).FirstOrDefault() : query.FirstOrDefault(); + } + + public TResult GetFirstOrDefault(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true) + { + IQueryable query = DbSet; + + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (predicate != null) + { + query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + } + + return orderBy != null + ? orderBy(query).Select(selector).FirstOrDefault() + : query.Select(selector).FirstOrDefault(); + } + + public IQueryable FromSql(string sql, params object[] parameters) => DbSet.FromSql(sql, parameters); + + public TEntity Find(params object[] keyValues) => DbSet.Find(keyValues); + + public Task FindAsync(params object[] keyValues) => DbSet.FindAsync(keyValues); + + public Task FindAsync(object[] keyValues, CancellationToken cancellationToken) => + DbSet.FindAsync(keyValues, cancellationToken); + + public int Count(Expression> predicate = null) => + predicate == null ? DbSet.Count() : DbSet.Count(predicate); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs new file mode 100644 index 0000000..2a8674a --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs @@ -0,0 +1,92 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Repository +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + using Abstractions.Repository; + + internal class Repository : IRepository where TEntity : class + { + protected readonly DbContext DbContext; + protected readonly DbSet DbSet; + + public Repository(DbContext dbContext) + { + DbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); + DbSet = DbContext.Set(); + } + + public void Insert(TEntity entity) + { + DbSet.Add(entity); + } + + public void Insert(params TEntity[] entities) => DbSet.AddRange(entities); + + public void Insert(IEnumerable entities) => DbSet.AddRange(entities); + + public Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + { + return DbSet.AddAsync(entity, cancellationToken); + + // Shadow properties? + //var property = _dbContext.Entry(entity).Property("Created"); + //if (property != null) { + //property.CurrentValue = DateTime.Now; + //} + } + + public Task InsertAsync(params TEntity[] entities) => DbSet.AddRangeAsync(entities); + + public Task InsertAsync(IEnumerable entities, + CancellationToken cancellationToken = default(CancellationToken)) => + DbSet.AddRangeAsync(entities, cancellationToken); + + public void Update(TEntity entity) + { + DbSet.Update(entity); + + // Shadow properties? + //var property = _dbContext.Entry(entity).Property("LastUpdated"); + //if(property != null) { + //property.CurrentValue = DateTime.Now; + //} + } + + public void Update(params TEntity[] entities) => DbSet.UpdateRange(entities); + + public void Update(IEnumerable entities) => DbSet.UpdateRange(entities); + + public void Delete(TEntity entity) => DbSet.Remove(entity); + + public void Delete(object id) + { + // using a stub entity to mark for deletion + var typeInfo = typeof(TEntity).GetTypeInfo(); + var key = DbContext.Model.FindEntityType(typeInfo.Name).FindPrimaryKey().Properties.FirstOrDefault(); + var property = typeInfo.GetProperty(key?.Name); + if (property != null) + { + var entity = Activator.CreateInstance(); + property.SetValue(entity, id); + DbContext.Entry(entity).State = EntityState.Deleted; + } + else + { + var entity = DbSet.Find(id); + if (entity != null) + { + Delete(entity); + } + } + } + + public void Delete(params TEntity[] entities) => DbSet.RemoveRange(entities); + + public void Delete(IEnumerable entities) => DbSet.RemoveRange(entities); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs new file mode 100644 index 0000000..8658fa8 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs @@ -0,0 +1,135 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Repository +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Storage; + using Abstractions.Repository; + using Abstractions.UnitOfWork; + + public class UnitOfWork : IRepositoryFactory, IUnitOfWork where TContext : DbContext + { + private bool _disposed; + private Dictionary _commandRepositories; + private Dictionary _queryableRepositories; + + public TContext DbContext { get; } + + public UnitOfWork(TContext context) + { + DbContext = context ?? throw new ArgumentNullException(nameof(context)); + } + + public IRepository GetRepository() where TEntity : class + { + if (_commandRepositories == null) + { + _commandRepositories = new Dictionary(); + } + + var type = typeof(TEntity); + if (!_commandRepositories.ContainsKey(type)) + { + _commandRepositories[type] = new Repository(DbContext); + } + + return (IRepository) _commandRepositories[type]; + } + + public IQueryableRepository GetQueryableRepository() where TEntity : class + { + if (_queryableRepositories == null) + { + _queryableRepositories = new Dictionary(); + } + + var type = typeof(TEntity); + if (!_queryableRepositories.ContainsKey(type)) + { + _queryableRepositories[type] = new QueryableRepository(DbContext); + } + + return (IQueryableRepository) _queryableRepositories[type]; + } + + public int ExecuteSqlCommand(string sql, params object[] parameters) => + DbContext.Database.ExecuteSqlCommand(sql, parameters); + + public IQueryable FromSql(string sql, params object[] parameters) where TEntity : class => + DbContext.Set().FromSql(sql, parameters); + + public int SaveChanges(bool ensureAutoHistory = false) + { + if (ensureAutoHistory) + { + DbContext.EnsureAutoHistory(); + } + + return DbContext.SaveChanges(); + } + + public async Task SaveChangesAsync(bool ensureAutoHistory = false) + { + if (ensureAutoHistory) + { + DbContext.EnsureAutoHistory(); + } + + return await DbContext.SaveChangesAsync(); + } + + public async Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks) + { + using (var transaction = DbContext.Database.BeginTransaction()) + { + try + { + var count = 0; + foreach (var unitOfWork in unitOfWorks) + { + var uow = unitOfWork as UnitOfWork; + uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); + count += await uow.SaveChangesAsync(ensureAutoHistory); + } + + count += await SaveChangesAsync(ensureAutoHistory); + + transaction.Commit(); + + return count; + } + catch (Exception ex) + { + transaction.Rollback(); + + throw ex; + } + } + } + + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _commandRepositories?.Clear(); + _queryableRepositories?.Clear(); + + DbContext.Dispose(); + } + } + + _disposed = true; + } + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs new file mode 100644 index 0000000..c54ea19 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs @@ -0,0 +1,58 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Repository +{ + using Microsoft.EntityFrameworkCore; + using Microsoft.Extensions.DependencyInjection; + using Abstractions.Repository; + using Abstractions.UnitOfWork; + + internal static class UnitOfWorkServiceCollectionExtensions + { + public static IServiceCollection AddUnitOfWork(this IServiceCollection services) + where TContext : DbContext + { + services.AddScoped>(); + services.AddScoped>(); + services.AddScoped, UnitOfWork>(); + + return services; + } + + public static IServiceCollection AddUnitOfWork(this IServiceCollection services) + where TContext1 : DbContext + where TContext2 : DbContext + { + services.AddScoped, UnitOfWork>(); + services.AddScoped, UnitOfWork>(); + + return services; + } + + public static IServiceCollection AddUnitOfWork( + this IServiceCollection services) + where TContext1 : DbContext + where TContext2 : DbContext + where TContext3 : DbContext + { + services.AddScoped, UnitOfWork>(); + services.AddScoped, UnitOfWork>(); + services.AddScoped, UnitOfWork>(); + + return services; + } + + public static IServiceCollection AddUnitOfWork( + this IServiceCollection services) + where TContext1 : DbContext + where TContext2 : DbContext + where TContext3 : DbContext + where TContext4 : DbContext + { + services.AddScoped, UnitOfWork>(); + services.AddScoped, UnitOfWork>(); + services.AddScoped, UnitOfWork>(); + services.AddScoped, UnitOfWork>(); + + return services; + } + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj new file mode 100644 index 0000000..5f730dc --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + + + + diff --git a/Yuxi.SharedKernel.Storage/Contracts/IStorableEntityMapper.cs b/Yuxi.SharedKernel.Storage/Contracts/IStorableEntityMapper.cs index c1e6b0d..00e2b16 100644 --- a/Yuxi.SharedKernel.Storage/Contracts/IStorableEntityMapper.cs +++ b/Yuxi.SharedKernel.Storage/Contracts/IStorableEntityMapper.cs @@ -1,9 +1,6 @@ namespace Yuxi.SharedKernel.Storage.Contracts { - using TrackableEntities; - - public interface IStorableEntityMapper where T : ITrackable - where TI : IStorableItem + public interface IStorableEntityMapper where TI : IStorableItem { #region Internal Properties diff --git a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs index b585106..debcd30 100644 --- a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs +++ b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs @@ -1,22 +1,18 @@ namespace Yuxi.SharedKernel.Storage.Implementations { using System; - using System.Linq; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; - using Data.Contracts.Query; - using Data.Implementations.Entity; - using Data.Contracts.Repository; - using Specification.Base; using Specification.Contracts; using Specification.Implementations; using Contracts; using Microsoft.ServiceFabric.Data; using Microsoft.ServiceFabric.Data.Collections; - using TrackableEntities; + using Data.Abstractions.Entities; + using Data.Abstractions.Repository; - public class AggregateGenericRepositoryReliableStateManager : IRepositoryAsync where T : Entity + public class AggregateGenericRepositoryReliableStateManager : IRepository where T : Entity where TI : IStorableItem { #region Read Only Properties @@ -48,37 +44,51 @@ public async Task Find(string id) return coreEntity; } - public T Find(params object[] keyValues) + #endregion + + #region Adds + + private async Task Add(T entity) { - throw new NotImplementedException(); + var storableEntity = _mapper.MapToStorable(entity); + var succeed = await _aggreateStorage.TryAddAsync(_transaction, entity.Id, storableEntity); + + if (!succeed) throw new Exception($"Something went wrong when trying to update the entity {entity.Id}"); + return entity; } - public void UpsertGraph(T entity) + public async Task InsertAsync(T entity, CancellationToken cancellationToken = default(CancellationToken)) { - throw new NotImplementedException(); + await Add(entity); } - public IQueryFluent Query(ExpressionSpecification specification) + public async Task InsertAsync(params T[] entities) { - throw new NotImplementedException(); + foreach (var entity in entities) + { + await InsertAsync(entity); + } } - public IQueryFluent Query() + public async Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) { - throw new NotImplementedException(); + foreach (var entity in entities) + { + await InsertAsync(entity, cancellationToken); + } } - public IQueryable Queryable() + public void Insert(T entity) { throw new NotImplementedException(); } - public Task FindAsync(params object[] keyValues) + public void Insert(params T[] entities) { throw new NotImplementedException(); } - public Task FindAsync(CancellationToken cancellationToken, params object[] keyValues) + public void Insert(IEnumerable entities) { throw new NotImplementedException(); } @@ -99,34 +109,15 @@ public async Task Update(string id, T entity) public void Update(T entity) { - var id = entity.Id; - - Task.Run(() => Update(id, entity)); - } - - #endregion - - #region Adds - - public async Task Add(T entity) - { - var storableEntity = _mapper.MapToStorable(entity); - var succeed = await _aggreateStorage.TryAddAsync(_transaction, entity.Id, storableEntity); - - if (!succeed) throw new Exception("Something went wrong when trying to add the turn"); - return entity; + throw new NotImplementedException(); } - void IRepository.Add(T entity) + public void Update(params T[] entities) { - Task.Run(() => Add(entity)); + throw new NotImplementedException(); } - #endregion - - #region Repositories - - public IRepository GetRepository() where T1 : class, ITrackable + public void Update(IEnumerable entities) { throw new NotImplementedException(); } @@ -143,20 +134,20 @@ public Task Delete(T entity) void IRepository.Delete(T entity) { - Task.Run(() => Delete(entity)); + throw new NotImplementedException(); } - public void Delete(params object[] keyValues) + public void Delete(params T[] entities) { throw new NotImplementedException(); } - public Task DeleteAsync(params object[] keyValues) + public void Delete(IEnumerable entities) { throw new NotImplementedException(); } - public Task DeleteAsync(CancellationToken cancellationToken, params object[] keyValues) + public void Delete(object id) { throw new NotImplementedException(); } @@ -192,6 +183,6 @@ public async Task> ListBySpecification(ISpecification specific return result; } - #endregion + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj b/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj index 09ef2e0..ea96e24 100644 --- a/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj +++ b/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj @@ -1,16 +1,15 @@ - net462 + netstandard2.0;net462 - - + diff --git a/Yuxi.SharedKernel.sln b/Yuxi.SharedKernel.sln index 6bfbcca..268c106 100644 --- a/Yuxi.SharedKernel.sln +++ b/Yuxi.SharedKernel.sln @@ -5,9 +5,11 @@ VisualStudioVersion = 15.0.27004.2005 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yuxi.SharedKernel.Specification", "Yuxi.SharedKernel.Specification\Yuxi.SharedKernel.Specification.csproj", "{57D38CFE-F0EB-4F5A-BA78-BD3E4A84CC5F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yuxi.SharedKernel.Data", "Yuxi.SharedKernel.Data\Yuxi.SharedKernel.Data.csproj", "{F83E7A95-05AC-4EC0-9AAD-BD19A5785E76}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yuxi.SharedKernel.Storage", "Yuxi.SharedKernel.Storage\Yuxi.SharedKernel.Storage.csproj", "{642718DA-8548-40C7-9177-4E4C7AB06BE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yuxi.SharedKernel.Storage", "Yuxi.SharedKernel.Storage\Yuxi.SharedKernel.Storage.csproj", "{642718DA-8548-40C7-9177-4E4C7AB06BE6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yuxi.SharedKernel.Data.Abstractions", "Yuxi.SharedKernel.Data.Abstractions\Yuxi.SharedKernel.Data.Abstractions.csproj", "{5456188F-C491-4F20-A1C5-F20DD1000185}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yuxi.SharedKernel.Data.EntityFrameworkCore", "Yuxi.SharedKernel.Data.EntityFrameworkCore\Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj", "{308069E1-20D4-47DF-B444-0654F0A6E32C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,14 +21,18 @@ Global {57D38CFE-F0EB-4F5A-BA78-BD3E4A84CC5F}.Debug|Any CPU.Build.0 = Debug|Any CPU {57D38CFE-F0EB-4F5A-BA78-BD3E4A84CC5F}.Release|Any CPU.ActiveCfg = Release|Any CPU {57D38CFE-F0EB-4F5A-BA78-BD3E4A84CC5F}.Release|Any CPU.Build.0 = Release|Any CPU - {F83E7A95-05AC-4EC0-9AAD-BD19A5785E76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F83E7A95-05AC-4EC0-9AAD-BD19A5785E76}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F83E7A95-05AC-4EC0-9AAD-BD19A5785E76}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F83E7A95-05AC-4EC0-9AAD-BD19A5785E76}.Release|Any CPU.Build.0 = Release|Any CPU {642718DA-8548-40C7-9177-4E4C7AB06BE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {642718DA-8548-40C7-9177-4E4C7AB06BE6}.Debug|Any CPU.Build.0 = Debug|Any CPU {642718DA-8548-40C7-9177-4E4C7AB06BE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {642718DA-8548-40C7-9177-4E4C7AB06BE6}.Release|Any CPU.Build.0 = Release|Any CPU + {5456188F-C491-4F20-A1C5-F20DD1000185}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5456188F-C491-4F20-A1C5-F20DD1000185}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5456188F-C491-4F20-A1C5-F20DD1000185}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5456188F-C491-4F20-A1C5-F20DD1000185}.Release|Any CPU.Build.0 = Release|Any CPU + {308069E1-20D4-47DF-B444-0654F0A6E32C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {308069E1-20D4-47DF-B444-0654F0A6E32C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {308069E1-20D4-47DF-B444-0654F0A6E32C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {308069E1-20D4-47DF-B444-0654F0A6E32C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From e5124e342693e77158847adff3c7a7cce24408e7 Mon Sep 17 00:00:00 2001 From: Carlos Carrero Date: Wed, 8 Nov 2017 14:00:41 -0500 Subject: [PATCH 2/6] Implementing IRepository into the storage reliable generic repository --- .../Repository/IRepository.cs | 15 +++ .../Repository/Repository.cs | 35 +++++++ ...teGenericRepositoryReliableStateManager.cs | 95 ++++++++++++++----- 3 files changed, 121 insertions(+), 24 deletions(-) diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs index fdbbbe9..16c3fde 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs @@ -25,6 +25,13 @@ Task InsertAsync(IEnumerable entities, void Update(IEnumerable entities); + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)); + + Task UpdateAsync(params TEntity[] entities); + + Task UpdateAsync(IEnumerable entities, + CancellationToken cancellationToken = default(CancellationToken)); + void Delete(object id); void Delete(TEntity entity); @@ -32,5 +39,13 @@ Task InsertAsync(IEnumerable entities, void Delete(params TEntity[] entities); void Delete(IEnumerable entities); + + Task DeleteAsync(object id); + + Task DeleteAsync(TEntity entity); + + Task DeleteAsync(params TEntity[] entities); + + Task DeleteAsync(IEnumerable entities); } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs index 2a8674a..137319e 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs @@ -61,6 +61,21 @@ public void Update(TEntity entity) public void Update(IEnumerable entities) => DbSet.UpdateRange(entities); + public Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(params TEntity[] entities) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) + { + throw new NotImplementedException(); + } + public void Delete(TEntity entity) => DbSet.Remove(entity); public void Delete(object id) @@ -88,5 +103,25 @@ public void Delete(object id) public void Delete(params TEntity[] entities) => DbSet.RemoveRange(entities); public void Delete(IEnumerable entities) => DbSet.RemoveRange(entities); + + public Task DeleteAsync(object id) + { + throw new NotImplementedException(); + } + + public Task DeleteAsync(TEntity entity) + { + throw new NotImplementedException(); + } + + public Task DeleteAsync(params TEntity[] entities) + { + throw new NotImplementedException(); + } + + public Task DeleteAsync(IEnumerable entities) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs index debcd30..ab05181 100644 --- a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs +++ b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs @@ -35,31 +35,17 @@ public AggregateGenericRepositoryReliableStateManager(IReliableDictionary Find(string id) - { - var entity = (await _aggreateStorage.TryGetValueAsync(_transaction, id)).Value; - var coreEntity = _mapper.MapToCore(entity); - return coreEntity; - } - - #endregion - #region Adds - private async Task Add(T entity) + public async Task InsertAsync(T entity, CancellationToken cancellationToken = default(CancellationToken)) { var storableEntity = _mapper.MapToStorable(entity); var succeed = await _aggreateStorage.TryAddAsync(_transaction, entity.Id, storableEntity); - if (!succeed) throw new Exception($"Something went wrong when trying to update the entity {entity.Id}"); - return entity; - } - - public async Task InsertAsync(T entity, CancellationToken cancellationToken = default(CancellationToken)) - { - await Add(entity); + if (!succeed) + { + throw new Exception($"Something went wrong when trying to update the entity {entity.Id}"); + } } public async Task InsertAsync(params T[] entities) @@ -97,14 +83,38 @@ public void Insert(IEnumerable entities) #region Updates - public async Task Update(string id, T entity) + public async Task UpdateAsync(T entity, CancellationToken cancellationToken = default(CancellationToken)) { - var oldEntityt = (await _aggreateStorage.TryGetValueAsync(_transaction, id)).Value; + var oldEntityt = (await _aggreateStorage.TryGetValueAsync(_transaction, entity.Id)).Value; var storableEntity = _mapper.MapToStorable(entity); var succeed = await _aggreateStorage.TryUpdateAsync(_transaction, entity.Id, storableEntity, oldEntityt); - if (!succeed) throw new Exception($"Something went wrong when trying to update the entity {id}"); + if (!succeed) throw new Exception($"Something went wrong when trying to update the entity {entity.Id}"); + } + + public async Task UpdateAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) + { + foreach (var entity in entities) + { + await UpdateAsync(entity, cancellationToken); + } + } + + public async Task UpdateAsync(params T[] entities) + { + foreach (var entity in entities) + { + await UpdateAsync(entity); + } + } + + public async Task UpdateAsync(IEnumerable entities) + { + foreach (var entity in entities) + { + await UpdateAsync(entity); + } } public void Update(T entity) @@ -132,6 +142,37 @@ public Task Delete(T entity) return _aggreateStorage.TryRemoveAsync(_transaction, id); } + public async Task DeleteAsync(T entity) + { + await Delete(entity); + } + + public async Task DeleteAsync(params T[] entities) + { + foreach (var entity in entities) + { + await Delete(entity); + } + } + + public async Task DeleteAsync(IEnumerable entities) + { + foreach (var entity in entities) + { + await Delete(entity); + } + } + + public Task DeleteAsync(object id) + { + throw new NotImplementedException(); + } + + public void Delete(object id) + { + throw new NotImplementedException(); + } + void IRepository.Delete(T entity) { throw new NotImplementedException(); @@ -147,9 +188,15 @@ public void Delete(IEnumerable entities) throw new NotImplementedException(); } - public void Delete(object id) + #endregion + + #region Querys + + public async Task Find(string id) { - throw new NotImplementedException(); + var entity = (await _aggreateStorage.TryGetValueAsync(_transaction, id)).Value; + var coreEntity = _mapper.MapToCore(entity); + return coreEntity; } #endregion From 3af5e387aa1ec3888abcb1205f522e30b412b718 Mon Sep 17 00:00:00 2001 From: Carlos Carrero Date: Wed, 8 Nov 2017 14:04:27 -0500 Subject: [PATCH 3/6] Reformatting code --- .../Repository/Repository.cs | 3 ++- .../AggregateGenericRepositoryReliableStateManager.cs | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs index 137319e..838f7ef 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs @@ -71,7 +71,8 @@ public Task UpdateAsync(params TEntity[] entities) throw new NotImplementedException(); } - public Task UpdateAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) + public Task UpdateAsync(IEnumerable entities, + CancellationToken cancellationToken = default(CancellationToken)) { throw new NotImplementedException(); } diff --git a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs index ab05181..855a870 100644 --- a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs +++ b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs @@ -56,7 +56,8 @@ public async Task InsertAsync(params T[] entities) } } - public async Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) + public async Task InsertAsync(IEnumerable entities, + CancellationToken cancellationToken = default(CancellationToken)) { foreach (var entity in entities) { @@ -93,7 +94,8 @@ public void Insert(IEnumerable entities) if (!succeed) throw new Exception($"Something went wrong when trying to update the entity {entity.Id}"); } - public async Task UpdateAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) + public async Task UpdateAsync(IEnumerable entities, + CancellationToken cancellationToken = default(CancellationToken)) { foreach (var entity in entities) { @@ -230,6 +232,6 @@ public async Task> ListBySpecification(ISpecification specific return result; } - #endregion + #endregion } } \ No newline at end of file From 1e897ee28b93987d0c459e40d334c63f8fc617fd Mon Sep 17 00:00:00 2001 From: Carlos Carrero Date: Thu, 9 Nov 2017 11:05:12 -0500 Subject: [PATCH 4/6] Abstractions redefinition --- .../IEnumerablePagedListExtensions.cs | 5 +- .../PagedList/PagedList.cs | 13 +- .../Repository/IQueryableRepository.cs | 46 ---- .../UnitOfWork/IUnitOfWork.cs | 5 - .../UnitOfWork/IUnitOfWorkOfT.cs | 12 - ...Yuxi.SharedKernel.Data.Abstractions.csproj | 4 - .../Abstractions/IQueryableRepository.cs | 57 +++++ .../Abstractions}/IRepositoryFactory.cs | 4 +- .../Abstractions/IUnitOfWork.cs | 18 ++ .../PagedList/IQueryablePageListExtensions.cs | 4 +- .../Repository/QueryableRepository.cs | 40 ++-- .../Repository/Repository.cs | 14 +- .../Repository/UnitOfWork.cs | 19 +- .../UnitOfWorkServiceCollectionExtensions.cs | 4 +- ...aredKernel.Data.EntityFrameworkCore.csproj | 1 + .../Contracts/DataContext/IDataContext.cs | 18 -- .../DataContext/IDataContextAsync.cs | 16 -- .../Contracts/DataContext/IFakeDbContext.cs | 19 -- .../Contracts/Query/IQueryFluent.cs | 28 --- .../Contracts/Repository/IRepository.cs | 49 ----- .../Contracts/Repository/IRepositoryAsync.cs | 21 -- .../Contracts/UnitOfWork/IUnitOfWork.cs | 24 -- .../Contracts/UnitOfWork/IUnitOfWorkAsync.cs | 20 -- .../DataContext/DataContext.cs | 85 -------- .../DataContext/FakeDbContext.cs | 73 ------- .../Implementations/DataContext/FakeDbSet.cs | 107 --------- .../Implementations/Entity/Entity.cs | 21 -- .../Implementations/Query/QueryFluent.cs | 86 -------- .../Implementations/Repository/Repository.cs | 206 ------------------ .../Implementations/UnitOfWork/UnitOfWork.cs | 171 --------------- .../Utils/AllExpressionSpecification.cs | 18 -- .../Implementations/Utils/StateHelper.cs | 55 ----- .../Yuxi.SharedKernel.Data.csproj | 19 -- ...teGenericRepositoryReliableStateManager.cs | 12 +- 34 files changed, 129 insertions(+), 1165 deletions(-) rename {Yuxi.SharedKernel.Data.EntityFrameworkCore => Yuxi.SharedKernel.Data.Abstractions}/PagedList/IEnumerablePagedListExtensions.cs (79%) rename {Yuxi.SharedKernel.Data.EntityFrameworkCore => Yuxi.SharedKernel.Data.Abstractions}/PagedList/PagedList.cs (88%) delete mode 100644 Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs rename {Yuxi.SharedKernel.Data.Abstractions/Repository => Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions}/IRepositoryFactory.cs (67%) create mode 100644 Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContext.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContextAsync.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/DataContext/IFakeDbContext.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/Query/IQueryFluent.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/Repository/IRepository.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/Repository/IRepositoryAsync.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWork.cs delete mode 100644 Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWorkAsync.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/DataContext/DataContext.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbContext.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbSet.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/Entity/Entity.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/Query/QueryFluent.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/Repository/Repository.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/UnitOfWork/UnitOfWork.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/Utils/AllExpressionSpecification.cs delete mode 100644 Yuxi.SharedKernel.Data/Implementations/Utils/StateHelper.cs delete mode 100644 Yuxi.SharedKernel.Data/Yuxi.SharedKernel.Data.csproj diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IEnumerablePagedListExtensions.cs b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs similarity index 79% rename from Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IEnumerablePagedListExtensions.cs rename to Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs index b9cff76..5671285 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IEnumerablePagedListExtensions.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs @@ -1,10 +1,9 @@ -namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.PagedList +namespace Yuxi.SharedKernel.Data.Abstractions.PagedList { using System; using System.Collections.Generic; - using Abstractions.PagedList; - internal static class IEnumerablePagedListExtensions + public static class IEnumerablePagedListExtensions { public static IPagedList ToPagedList(this IEnumerable source, int pageIndex, int pageSize, int indexFrom = 0) => new PagedList(source, pageIndex, pageSize, indexFrom); diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/PagedList.cs b/Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs similarity index 88% rename from Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/PagedList.cs rename to Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs index 1ed2e2a..e24639e 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/PagedList.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs @@ -1,11 +1,10 @@ -namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.PagedList +namespace Yuxi.SharedKernel.Data.Abstractions.PagedList { using System; using System.Collections.Generic; using System.Linq; - using Abstractions.PagedList; - internal class PagedList : IPagedList + public class PagedList : IPagedList { public int PageIndex { get; set; } @@ -24,7 +23,7 @@ internal class PagedList : IPagedList public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages; - internal PagedList(IEnumerable source, int pageIndex, int pageSize, int indexFrom) + public PagedList(IEnumerable source, int pageIndex, int pageSize, int indexFrom) { if (indexFrom > pageIndex) { @@ -41,10 +40,10 @@ internal PagedList(IEnumerable source, int pageIndex, int pageSize, int index Items = source.Skip((PageIndex - IndexFrom) * PageSize).Take(PageSize).ToList(); } - internal PagedList() => Items = new T[0]; + public PagedList() => Items = new T[0]; } - internal class PagedList : IPagedList + public class PagedList : IPagedList { public int PageIndex { get; } @@ -94,7 +93,7 @@ public PagedList(IPagedList source, Func, IEnumera } } - internal static class PagedList + public static class PagedList { public static IPagedList Empty() => new PagedList(); diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs b/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs index e748502..fa3ce02 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs @@ -5,55 +5,9 @@ using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; - using Microsoft.EntityFrameworkCore.Query; - using PagedList; - using Specification.Contracts; public interface IQueryableRepository { - IPagedList GetPagedList(ISpecification predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true); - - Task> GetPagedListAsync(ISpecification predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - CancellationToken cancellationToken = default(CancellationToken)); - - IPagedList GetPagedList(Expression> selector, - ISpecification predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true) where TResult : class; - - Task> GetPagedListAsync(Expression> selector, - ISpecification predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - int pageIndex = 0, - int pageSize = 20, - bool disableTracking = true, - CancellationToken cancellationToken = default(CancellationToken)) where TResult : class; - - TEntity GetFirstOrDefault(ISpecification predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true); - - TResult GetFirstOrDefault(Expression> selector, - ISpecification predicate = null, - Func, IOrderedQueryable> orderBy = null, - Func, IIncludableQueryable> include = null, - bool disableTracking = true); - IQueryable FromSql(string sql, params object[] parameters); TEntity Find(params object[] keyValues); diff --git a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs index 25eb5ef..58aeac0 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs @@ -3,14 +3,9 @@ using System; using System.Linq; using System.Threading.Tasks; - using Repository; public interface IUnitOfWork : IDisposable { - IRepository GetRepository() where TEntity : class; - - IQueryableRepository GetQueryableRepository() where TEntity : class; - int SaveChanges(bool ensureAutoHistory = false); Task SaveChangesAsync(bool ensureAutoHistory = false); diff --git a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs deleted file mode 100644 index c5d3818..0000000 --- a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWorkOfT.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Abstractions.UnitOfWork -{ - using System.Threading.Tasks; - using Microsoft.EntityFrameworkCore; - - public interface IUnitOfWork : IUnitOfWork where TContext : DbContext - { - TContext DbContext { get; } - - Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj b/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj index da84d8b..af3c7ed 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj +++ b/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj @@ -4,10 +4,6 @@ netstandard2.0 - - - - diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs new file mode 100644 index 0000000..5306008 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs @@ -0,0 +1,57 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Abstractions +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore.Query; + using Data.Abstractions.PagedList; + using Specification.Contracts; + + public interface IQueryableRepository : Data.Abstractions.Repository.IQueryableRepository + { + IPagedList GetPagedList(ISpecification businessSpecification = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true); + + Task> GetPagedListAsync(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)); + + IPagedList GetPagedList(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true) where TResult : class; + + Task> GetPagedListAsync(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true, + CancellationToken cancellationToken = default(CancellationToken)) where TResult : class; + + TEntity GetFirstOrDefault(ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true); + + TResult GetFirstOrDefault(Expression> selector, + ISpecification predicate = null, + Func, IOrderedQueryable> orderBy = null, + Func, IIncludableQueryable> include = null, + bool disableTracking = true); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepositoryFactory.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs similarity index 67% rename from Yuxi.SharedKernel.Data.Abstractions/Repository/IRepositoryFactory.cs rename to Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs index 75456b5..4828828 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepositoryFactory.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs @@ -1,5 +1,7 @@ -namespace Yuxi.SharedKernel.Data.Abstractions.Repository +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Abstractions { + using Data.Abstractions.Repository; + public interface IRepositoryFactory { IRepository GetRepository() where TEntity : class; diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs new file mode 100644 index 0000000..8e6f189 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs @@ -0,0 +1,18 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Abstractions +{ + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + using Data.Abstractions.Repository; + using Data.Abstractions.UnitOfWork; + + public interface IUnitOfWork : IUnitOfWork where TContext : DbContext + { + IRepository GetRepository() where TEntity : class; + + IQueryableRepository GetQueryableRepository() where TEntity : class; + + TContext DbContext { get; } + + Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs index 3187996..30d5248 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs @@ -5,9 +5,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; - using Abstractions.PagedList; + using Data.Abstractions.PagedList; - internal static class IQueryablePageListExtensions + public static class IQueryablePageListExtensions { public static async Task> ToPagedListAsync(this IQueryable source, int pageIndex, int pageSize, int indexFrom = 0, CancellationToken cancellationToken = default(CancellationToken)) diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs index a845c9e..e606369 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs @@ -7,8 +7,8 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; - using Abstractions.PagedList; - using Abstractions.Repository; + using Data.Abstractions.PagedList; + using Abstractions; using PagedList; using Specification.Contracts; @@ -23,7 +23,7 @@ public QueryableRepository(DbContext dbContext) DbSet = DbContext.Set(); } - public IPagedList GetPagedList(ISpecification predicate = null, + public IPagedList GetPagedList(ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -41,9 +41,9 @@ public IPagedList GetPagedList(ISpecification predicate = null query = include(query); } - if (predicate != null) + if (businessSpecification != null) { - query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); } return orderBy != null @@ -51,7 +51,7 @@ public IPagedList GetPagedList(ISpecification predicate = null : query.ToPagedList(pageIndex, pageSize); } - public Task> GetPagedListAsync(ISpecification predicate = null, + public Task> GetPagedListAsync(ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -70,9 +70,9 @@ public Task> GetPagedListAsync(ISpecification predi query = include(query); } - if (predicate != null) + if (businessSpecification != null) { - query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); } return orderBy?.Invoke(query).ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken) ?? @@ -80,7 +80,7 @@ public Task> GetPagedListAsync(ISpecification predi } public IPagedList GetPagedList(Expression> selector, - ISpecification predicate = null, + ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -99,9 +99,9 @@ public IPagedList GetPagedList(Expression predicate.IsSatisfiedBy(entityToTest)); + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); } return orderBy != null @@ -110,7 +110,7 @@ public IPagedList GetPagedList(Expression> GetPagedListAsync(Expression> selector, - ISpecification predicate = null, + ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, int pageIndex = 0, @@ -131,9 +131,9 @@ public Task> GetPagedListAsync(Expression predicate.IsSatisfiedBy(entityToTest)); + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); } return orderBy?.Invoke(query).Select(selector) @@ -141,7 +141,7 @@ public Task> GetPagedListAsync(Expression predicate = null, + public TEntity GetFirstOrDefault(ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true) @@ -158,16 +158,16 @@ public TEntity GetFirstOrDefault(ISpecification predicate = null, query = include(query); } - if (predicate != null) + if (businessSpecification != null) { - query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); } return orderBy != null ? orderBy(query).FirstOrDefault() : query.FirstOrDefault(); } public TResult GetFirstOrDefault(Expression> selector, - ISpecification predicate = null, + ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true) @@ -184,9 +184,9 @@ public TResult GetFirstOrDefault(Expression> sel query = include(query); } - if (predicate != null) + if (businessSpecification != null) { - query = query.Where(entityToTest => predicate.IsSatisfiedBy(entityToTest)); + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); } return orderBy != null diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs index 838f7ef..9ba521d 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; - using Abstractions.Repository; + using Data.Abstractions.Repository; internal class Repository : IRepository where TEntity : class { @@ -32,12 +32,6 @@ public void Insert(TEntity entity) public Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken)) { return DbSet.AddAsync(entity, cancellationToken); - - // Shadow properties? - //var property = _dbContext.Entry(entity).Property("Created"); - //if (property != null) { - //property.CurrentValue = DateTime.Now; - //} } public Task InsertAsync(params TEntity[] entities) => DbSet.AddRangeAsync(entities); @@ -49,12 +43,6 @@ public Task InsertAsync(IEnumerable entities, public void Update(TEntity entity) { DbSet.Update(entity); - - // Shadow properties? - //var property = _dbContext.Entry(entity).Property("LastUpdated"); - //if(property != null) { - //property.CurrentValue = DateTime.Now; - //} } public void Update(params TEntity[] entities) => DbSet.UpdateRange(entities); diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs index 8658fa8..f5af47c 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs @@ -6,8 +6,9 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; - using Abstractions.Repository; - using Abstractions.UnitOfWork; + using Data.Abstractions.Repository; + using Data.Abstractions.UnitOfWork; + using Abstractions; public class UnitOfWork : IRepositoryFactory, IUnitOfWork where TContext : DbContext { @@ -38,7 +39,7 @@ public IRepository GetRepository() where TEntity : class return (IRepository) _commandRepositories[type]; } - public IQueryableRepository GetQueryableRepository() where TEntity : class + public Abstractions.IQueryableRepository GetQueryableRepository() where TEntity : class { if (_queryableRepositories == null) { @@ -51,7 +52,7 @@ public IQueryableRepository GetQueryableRepository() where TEn _queryableRepositories[type] = new QueryableRepository(DbContext); } - return (IQueryableRepository) _queryableRepositories[type]; + return (Abstractions.IQueryableRepository) _queryableRepositories[type]; } public int ExecuteSqlCommand(string sql, params object[] parameters) => @@ -90,6 +91,12 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false, params I foreach (var unitOfWork in unitOfWorks) { var uow = unitOfWork as UnitOfWork; + + if (uow == null) + { + continue; + } + uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); count += await uow.SaveChangesAsync(ensureAutoHistory); } @@ -100,11 +107,11 @@ public async Task SaveChangesAsync(bool ensureAutoHistory = false, params I return count; } - catch (Exception ex) + catch (Exception) { transaction.Rollback(); - throw ex; + throw; } } } diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs index c54ea19..07fd217 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs @@ -2,8 +2,8 @@ { using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; - using Abstractions.Repository; - using Abstractions.UnitOfWork; + using Data.Abstractions.UnitOfWork; + using Abstractions; internal static class UnitOfWorkServiceCollectionExtensions { diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj index 5f730dc..bd3401a 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Yuxi.SharedKernel.Data.EntityFrameworkCore.csproj @@ -15,6 +15,7 @@ + diff --git a/Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContext.cs b/Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContext.cs deleted file mode 100644 index 177e081..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.DataContext -{ - using System; - using TrackableEntities; - - public interface IDataContext : IDisposable - { - #region Public Methods - - int SaveChanges(); - - void SyncObjectState(TAgreggate entity) where TAgreggate : class, ITrackable; - - void SyncObjectsStatePostCommit(); - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContextAsync.cs b/Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContextAsync.cs deleted file mode 100644 index c1c9bb5..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/DataContext/IDataContextAsync.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.DataContext -{ - using System.Threading; - using System.Threading.Tasks; - - public interface IDataContextAsync : IDataContext - { - #region Public Methods - - Task SaveChangesAsync(CancellationToken cancellationToken); - - Task SaveChangesAsync(); - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Contracts/DataContext/IFakeDbContext.cs b/Yuxi.SharedKernel.Data/Contracts/DataContext/IFakeDbContext.cs deleted file mode 100644 index 089c3ed..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/DataContext/IFakeDbContext.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.DataContext -{ - using System.Data.Entity; - using Implementations.DataContext; - using Implementations.Entity; - - public interface IFakeDbContext : IDataContextAsync - { - #region Public Methods - - DbSet Set() where TEntity : class; - - void AddFakeDbSet() - where TEntity : Entity, new() - where TFakeDbSet : FakeDbSet, IDbSet, new(); - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Contracts/Query/IQueryFluent.cs b/Yuxi.SharedKernel.Data/Contracts/Query/IQueryFluent.cs deleted file mode 100644 index 1fb82ea..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/Query/IQueryFluent.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.Query -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Threading.Tasks; - using TrackableEntities; - - public interface IQueryFluent where TEntity : ITrackable - { - #region Public Methods - - IQueryFluent OrderBy(Func, IOrderedQueryable> orderBy); - - IQueryFluent Include(Expression> expression); - - IEnumerable SelectPage(int page, int pageSize, out int totalCount); - - IEnumerable Select(Expression> selector = null); - - IEnumerable Select(); - - Task> SelectAsync(); - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Contracts/Repository/IRepository.cs b/Yuxi.SharedKernel.Data/Contracts/Repository/IRepository.cs deleted file mode 100644 index 261bdd2..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/Repository/IRepository.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.Repository -{ - using System.Linq; - using System.Collections.Generic; - using System.Threading.Tasks; - using Specification.Contracts; - using Specification.Base; - using Query; - using TrackableEntities; - - public interface IRepository where TAgreggate : class, ITrackable - { - #region Public Methods - - IRepository GetRepository() where T : class, ITrackable; - - #region Commands - - void Add(TAgreggate entity); - - void Update(TAgreggate entity); - - void Delete(TAgreggate entity); - - void Delete(params object[] keyValues); - - TAgreggate Find(params object[] keyValues); - - void UpsertGraph(TAgreggate entity); - - #endregion - - #region Querys - - IQueryFluent Query(ExpressionSpecification specification); - - IQueryFluent Query(); - - IQueryable Queryable(); - - Task> ListAll(); - - Task> ListBySpecification(ISpecification specification); - - #endregion - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Contracts/Repository/IRepositoryAsync.cs b/Yuxi.SharedKernel.Data/Contracts/Repository/IRepositoryAsync.cs deleted file mode 100644 index 0f53eaa..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/Repository/IRepositoryAsync.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.Repository -{ - using System.Threading; - using System.Threading.Tasks; - using TrackableEntities; - - public interface IRepositoryAsync : IRepository where TAgreggate : class, ITrackable - { - #region Public Methods - - Task FindAsync(params object[] keyValues); - - Task FindAsync(CancellationToken cancellationToken, params object[] keyValues); - - Task DeleteAsync(params object[] keyValues); - - Task DeleteAsync(CancellationToken cancellationToken, params object[] keyValues); - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWork.cs b/Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWork.cs deleted file mode 100644 index 62a6fce..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWork.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.UnitOfWork -{ - using System; - using System.Data; - using Repository; - using TrackableEntities; - - public interface IUnitOfWork : IDisposable - { - #region Public Methods - - int SaveChanges(); - - void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); - - bool Commit(); - - void Rollback(); - - IRepository Repository() where TAgreggate : class, ITrackable; - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWorkAsync.cs b/Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWorkAsync.cs deleted file mode 100644 index 940dceb..0000000 --- a/Yuxi.SharedKernel.Data/Contracts/UnitOfWork/IUnitOfWorkAsync.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Contracts.UnitOfWork -{ - using System.Threading; - using System.Threading.Tasks; - using Repository; - using TrackableEntities; - - public interface IUnitOfWorkAsync : IUnitOfWork - { - #region Public Methods - - Task SaveChangesAsync(); - - Task SaveChangesAsync(CancellationToken cancellationToken); - - IRepositoryAsync RepositoryAsync() where TEntity : class, ITrackable; - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/DataContext/DataContext.cs b/Yuxi.SharedKernel.Data/Implementations/DataContext/DataContext.cs deleted file mode 100644 index 5d9e704..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/DataContext/DataContext.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.DataContext -{ - using System; - using System.Threading; - using System.Threading.Tasks; - using System.Data.Entity; - using System.Linq; - using Utils; - using Contracts.DataContext; - using TrackableEntities; - using TrackableEntities.EF6; - - public class DataContext : DbContext, IDataContextAsync - { - #region Constructors - - public DataContext(string nameOrConnectionString) : base(nameOrConnectionString) - { - InstanceId = Guid.NewGuid(); - - Configuration.LazyLoadingEnabled = false; - Configuration.ProxyCreationEnabled = false; - } - - #endregion - - #region Public Properties - - public Guid InstanceId { get; } - - #endregion - - #region Public Overriden Methods - - public override int SaveChanges() - { - SyncObjectsStatePreCommit(); - var changes = base.SaveChanges(); - SyncObjectsStatePostCommit(); - return changes; - } - - public override async Task SaveChangesAsync() - { - return await SaveChangesAsync(CancellationToken.None); - } - - public override async Task SaveChangesAsync(CancellationToken cancellationToken) - { - SyncObjectsStatePreCommit(); - var changesAsync = await base.SaveChangesAsync(cancellationToken); - SyncObjectsStatePostCommit(); - return changesAsync; - } - - #endregion - - #region Public Methods - - public void SyncObjectState(TEntity entity) where TEntity : class, ITrackable - { - this.ApplyChanges(entity); - } - - public void SyncObjectsStatePostCommit() - { - foreach (var dbEntityEntry in ChangeTracker.Entries()) - { - ((ITrackable) dbEntityEntry.Entity).TrackingState = StateHelper.ConvertState(dbEntityEntry.State); - } - } - - #endregion - - #region Private Methods - - private void SyncObjectsStatePreCommit() - { - var entities = ChangeTracker.Entries().Select(x => x.Entity).OfType(); - this.ApplyChanges(entities); - } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbContext.cs b/Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbContext.cs deleted file mode 100644 index d69677c..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbContext.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.DataContext -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using System.Data.Entity; - using Contracts.DataContext; - using TrackableEntities; - - public abstract class FakeDbContext : IFakeDbContext - { - #region Read Only Members - - private readonly Dictionary _fakeDbSets; - - #endregion - - #region Constructors - - protected FakeDbContext() - { - _fakeDbSets = new Dictionary(); - } - - #endregion - - #region Public Methods - - public int SaveChanges() - { - return default(int); - } - - public void SyncObjectState(TEntity entity) where TEntity : class, ITrackable - { - // no implentation needed, unit tests which uses FakeDbContext since there is no actual database for unit tests - } - - public Task SaveChangesAsync(CancellationToken cancellationToken) - { - return new Task(() => default(int)); - } - - public Task SaveChangesAsync() - { - return new Task(() => default(int)); - } - - public void Dispose() - { - } - - public DbSet Set() where T : class - { - return (DbSet) _fakeDbSets[typeof(T)]; - } - - public void AddFakeDbSet() - where TEntity : Entity.Entity, new() - where TFakeDbSet : FakeDbSet, IDbSet, new() - { - var fakeDbSet = Activator.CreateInstance(); - _fakeDbSets.Add(typeof(TEntity), fakeDbSet); - } - - public void SyncObjectsStatePostCommit() - { - } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbSet.cs b/Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbSet.cs deleted file mode 100644 index 470c5ea..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/DataContext/FakeDbSet.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.DataContext -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Collections.ObjectModel; - using System.Data.Entity; - using TrackableEntities; - - public abstract class FakeDbSet : DbSet, IDbSet where TEntity : Entity.Entity, new() - { - #region Read Only Members - - private readonly ObservableCollection _items; - private readonly IQueryable _query; - - #endregion - - #region Constructors - - protected FakeDbSet() - { - _items = new ObservableCollection(); - _query = _items.AsQueryable(); - } - - #endregion - - #region Explicit Implementations - - IEnumerator IEnumerable.GetEnumerator() - { - return _items.GetEnumerator(); - } - - #endregion - - #region Public Properties - - public IEnumerator GetEnumerator() - { - return _items.GetEnumerator(); - } - - public Expression Expression => _query.Expression; - - public Type ElementType => _query.ElementType; - - public IQueryProvider Provider => _query.Provider; - - #endregion - - #region Public Overriden Methods - - public override TEntity Add(TEntity entity) - { - _items.Add(entity); - return entity; - } - - public override TEntity Remove(TEntity entity) - { - _items.Remove(entity); - return entity; - } - - public override TEntity Attach(TEntity entity) - { - switch (entity.TrackingState) - { - case TrackingState.Modified: - _items.Remove(entity); - _items.Add(entity); - break; - - case TrackingState.Deleted: - _items.Remove(entity); - break; - - case TrackingState.Unchanged: - case TrackingState.Added: - _items.Add(entity); - break; - - default: - throw new ArgumentOutOfRangeException(); - } - return entity; - } - - public override TEntity Create() - { - return new TEntity(); - } - - public override TDerivedEntity Create() - { - return Activator.CreateInstance(); - } - - public override ObservableCollection Local => _items; - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/Entity/Entity.cs b/Yuxi.SharedKernel.Data/Implementations/Entity/Entity.cs deleted file mode 100644 index 88b9a6d..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/Entity/Entity.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.Entity -{ - using System.Collections.Generic; - using System.ComponentModel.DataAnnotations.Schema; - using TrackableEntities; - - public abstract class Entity : ITrackable - { - #region Public Properties - - public string Id { get; set; } - - [NotMapped] - public TrackingState TrackingState { get; set; } - - [NotMapped] - public ICollection ModifiedProperties { get; set; } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/Query/QueryFluent.cs b/Yuxi.SharedKernel.Data/Implementations/Query/QueryFluent.cs deleted file mode 100644 index e0ab48e..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/Query/QueryFluent.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.Query -{ - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using System.Linq; - using System.Linq.Expressions; - using Contracts.Query; - using Repository; - using Specification.Base; - using TrackableEntities; - - public sealed class QueryFluent : IQueryFluent where TEntity : class, ITrackable - { - #region Read Only Members - - private readonly ExpressionSpecification _specification; - - private readonly List>> _includes; - - private readonly Repository _repository; - - #endregion - - #region Members - - private Func, IOrderedQueryable> _orderBy; - - #endregion - - #region Constructors - - public QueryFluent(Repository repository) - { - _repository = repository; - - _includes = new List>>(); - } - - public QueryFluent(Repository repository, ExpressionSpecification specification) - : this(repository) - { - _specification = specification; - } - - #endregion - - #region Public Methods - - public IQueryFluent OrderBy(Func, IOrderedQueryable> orderBy) - { - _orderBy = orderBy; - return this; - } - - public IQueryFluent Include(Expression> expression) - { - _includes.Add(expression); - return this; - } - - public IEnumerable SelectPage(int page, int pageSize, out int totalCount) - { - totalCount = _repository.Select(_specification.ToExpression()).Count(); - - return _repository.Select(_specification.ToExpression(), _orderBy, _includes, page, pageSize); - } - - public IEnumerable Select() - { - return _repository.Select(_specification.ToExpression(), _orderBy, _includes); - } - - public IEnumerable Select(Expression> selector) - { - return _repository.Select(_specification.ToExpression(), _orderBy, _includes).Select(selector); - } - - public async Task> SelectAsync() - { - return await _repository.SelectAsync(_specification.ToExpression(), _orderBy, _includes); - } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/Repository/Repository.cs b/Yuxi.SharedKernel.Data/Implementations/Repository/Repository.cs deleted file mode 100644 index a6aa865..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/Repository/Repository.cs +++ /dev/null @@ -1,206 +0,0 @@ -using Yuxi.SharedKernel.Specification.Contracts; - -namespace Yuxi.SharedKernel.Data.Implementations.Repository -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using System.Linq; - using System.Linq.Expressions; - using System.Data.Entity; - using Contracts.DataContext; - using Contracts.Query; - using Contracts.Repository; - using Contracts.UnitOfWork; - using DataContext; - using Query; - using Utils; - using Specification.Base; - using TrackableEntities; - - public class Repository : IRepositoryAsync where TAgreggate : class, ITrackable - { - #region Read Only Members - - private readonly IDataContextAsync _context; - - private readonly DbSet _dbSet; - - private readonly IUnitOfWorkAsync _unitOfWork; - - #endregion - - #region Constructors - - public Repository(IDataContextAsync context, IUnitOfWorkAsync unitOfWork) - { - _context = context; - _unitOfWork = unitOfWork; - - switch (context) - { - case DbContext dbContext: - _dbSet = dbContext.Set(); - break; - case FakeDbContext fakeContext: - _dbSet = fakeContext.Set(); - break; - } - } - - #endregion - - #region Public Methods - - public IRepository GetRepository() where T : class, ITrackable - { - return _unitOfWork.Repository(); - } - - #region Commands - - public virtual void Add(TAgreggate entity) - { - entity.TrackingState = TrackingState.Added; - _dbSet.Attach(entity); - _context.SyncObjectState(entity); - } - - public virtual void Update(TAgreggate entity) - { - entity.TrackingState = TrackingState.Modified; - _dbSet.Attach(entity); - _context.SyncObjectState(entity); - } - - public virtual void Delete(TAgreggate entity) - { - entity.TrackingState = TrackingState.Deleted; - _dbSet.Attach(entity); - _context.SyncObjectState(entity); - } - - public virtual void Delete(params object[] keyValues) - { - var entity = _dbSet.Find(keyValues); - - Delete(entity); - } - - public virtual async Task DeleteAsync(params object[] keyValues) - { - return await DeleteAsync(CancellationToken.None, keyValues); - } - - public virtual async Task DeleteAsync(CancellationToken cancellationToken, params object[] keyValues) - { - var entity = await FindAsync(cancellationToken, keyValues); - - if (entity == null) - { - return false; - } - - entity.TrackingState = TrackingState.Deleted; - _dbSet.Attach(entity); - - return true; - } - - public virtual TAgreggate Find(params object[] keyValues) - { - return _dbSet.Find(keyValues); - } - - public virtual async Task FindAsync(params object[] keyValues) - { - return await _dbSet.FindAsync(keyValues); - } - - public virtual async Task FindAsync(CancellationToken cancellationToken, params object[] keyValues) - { - return await _dbSet.FindAsync(cancellationToken, keyValues); - } - - public void UpsertGraph(TAgreggate entity) - { - _context.SyncObjectState(entity); - } - - #endregion - - #region Querys - - public IQueryFluent Query() - { - return new QueryFluent(this, new AllExpressionSpecification()); - } - - public virtual IQueryFluent Query(ExpressionSpecification specification) - { - return new QueryFluent(this, specification); - } - - public IQueryable Queryable() - { - return _dbSet; - } - - public Task> ListAll() - { - throw new NotImplementedException(); - } - - public Task> ListBySpecification(ISpecification specification) - { - throw new NotImplementedException(); - } - - #endregion - - #endregion - - #region Internal Methods - - internal IQueryable Select( - Expression> filter = null, - Func, IOrderedQueryable> orderBy = null, - List>> includes = null, - int? page = null, - int? pageSize = null) - { - IQueryable query = _dbSet; - - if (includes != null) - { - query = includes.Aggregate(query, (current, include) => current.Include(include)); - } - if (orderBy != null) - { - query = orderBy(query); - } - if (filter != null) - { - query = query.Where(filter); - } - if (page != null && pageSize != null) - { - query = query.Skip((page.Value - 1) * pageSize.Value).Take(pageSize.Value); - } - return query; - } - - internal async Task> SelectAsync( - Expression> filter = null, - Func, IOrderedQueryable> orderBy = null, - List>> includes = null, - int? page = null, - int? pageSize = null) - { - return await Select(filter, orderBy, includes, page, pageSize).ToListAsync(); - } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/UnitOfWork/UnitOfWork.cs b/Yuxi.SharedKernel.Data/Implementations/UnitOfWork/UnitOfWork.cs deleted file mode 100644 index 9010b82..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/UnitOfWork/UnitOfWork.cs +++ /dev/null @@ -1,171 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.UnitOfWork -{ - using System; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - using System.Data; - using System.Data.Common; - using System.Data.Entity.Core.Objects; - using System.Data.Entity.Infrastructure; - using Contracts.DataContext; - using Contracts.Repository; - using Contracts.UnitOfWork; - using Repository; - using CommonServiceLocator; - using TrackableEntities; - - public class UnitOfWork : IUnitOfWorkAsync - { - #region Members - - private IDataContextAsync _dataContext; - - private bool _disposed; - - private ObjectContext _objectContext; - - private DbTransaction _transaction; - - private Dictionary _commandRepositories; - - #endregion - - #region Constructors - - public UnitOfWork(IDataContextAsync dataContext) - { - _dataContext = dataContext; - _commandRepositories = new Dictionary(); - } - - #endregion - - #region Clean Up - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~UnitOfWork() - { - // Finalizer calls Dispose(false) - Dispose(false); - } - - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - try - { - if (_objectContext != null) - { - if (_objectContext.Connection.State == ConnectionState.Open) - _objectContext.Connection.Close(); - - _objectContext.Dispose(); - _objectContext = null; - } - if (_dataContext != null) - { - _dataContext.Dispose(); - _dataContext = null; - } - } - catch (ObjectDisposedException) - { - // do nothing, the objectContext has already been disposed - } - _commandRepositories = null; - } - - _disposed = true; - } - - #endregion - - #region Public Methods - - public int SaveChanges() - { - return _dataContext.SaveChanges(); - } - - public IRepository Repository() where TEntity : class, ITrackable - { - return ServiceLocator.IsLocationProviderSet - ? ServiceLocator.Current.GetInstance>() - : RepositoryAsync(); - } - - public Task SaveChangesAsync() - { - return _dataContext.SaveChangesAsync(); - } - - public Task SaveChangesAsync(CancellationToken cancellationToken) - { - return _dataContext.SaveChangesAsync(cancellationToken); - } - - public IRepositoryAsync RepositoryAsync() where TEntity : class, ITrackable - { - if (ServiceLocator.IsLocationProviderSet) - { - return ServiceLocator.Current.GetInstance>(); - } - - if (_commandRepositories == null) - { - _commandRepositories = new Dictionary(); - } - - var type = typeof(TEntity).Name; - - if (_commandRepositories.ContainsKey(type)) - { - return (IRepositoryAsync) _commandRepositories[type]; - } - - var repositoryType = typeof(Repository<>); - - _commandRepositories.Add(type, - Activator.CreateInstance(repositoryType.MakeGenericType(typeof(TEntity)), _dataContext, this)); - - return _commandRepositories[type]; - } - - public void BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) - { - _objectContext = ((IObjectContextAdapter) _dataContext).ObjectContext; - if (_objectContext.Connection.State != ConnectionState.Open) - { - _objectContext.Connection.Open(); - } - - _transaction = _objectContext.Connection.BeginTransaction(isolationLevel); - } - - public bool Commit() - { - _transaction.Commit(); - return true; - } - - public void Rollback() - { - _transaction.Rollback(); - _dataContext.SyncObjectsStatePostCommit(); - } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/Utils/AllExpressionSpecification.cs b/Yuxi.SharedKernel.Data/Implementations/Utils/AllExpressionSpecification.cs deleted file mode 100644 index 3b1a1e4..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/Utils/AllExpressionSpecification.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.Utils -{ - using System; - using System.Linq.Expressions; - using Specification.Base; - - internal sealed class AllExpressionSpecification : ExpressionSpecification - { - #region Overriden Methods - - public override Expression> ToExpression() - { - return entity => true; - } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Implementations/Utils/StateHelper.cs b/Yuxi.SharedKernel.Data/Implementations/Utils/StateHelper.cs deleted file mode 100644 index 7691ea9..0000000 --- a/Yuxi.SharedKernel.Data/Implementations/Utils/StateHelper.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Yuxi.SharedKernel.Data.Implementations.Utils -{ - using System; - using System.Data.Entity; - using TrackableEntities; - - public class StateHelper - { - #region Public Methods - - public static EntityState ConvertState(TrackingState state) - { - switch (state) - { - case TrackingState.Added: - return EntityState.Added; - - case TrackingState.Modified: - return EntityState.Modified; - - case TrackingState.Deleted: - return EntityState.Deleted; - - default: - return EntityState.Unchanged; - } - } - - public static TrackingState ConvertState(EntityState state) - { - switch (state) - { - case EntityState.Detached: - return TrackingState.Unchanged; - - case EntityState.Unchanged: - return TrackingState.Unchanged; - - case EntityState.Added: - return TrackingState.Added; - - case EntityState.Deleted: - return TrackingState.Deleted; - - case EntityState.Modified: - return TrackingState.Modified; - - default: - throw new ArgumentOutOfRangeException(nameof(state)); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data/Yuxi.SharedKernel.Data.csproj b/Yuxi.SharedKernel.Data/Yuxi.SharedKernel.Data.csproj deleted file mode 100644 index ca38902..0000000 --- a/Yuxi.SharedKernel.Data/Yuxi.SharedKernel.Data.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - net462 - - - - - - - - - - - - - - - diff --git a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs index 855a870..ecf699d 100644 --- a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs +++ b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs @@ -94,8 +94,7 @@ public void Insert(IEnumerable entities) if (!succeed) throw new Exception($"Something went wrong when trying to update the entity {entity.Id}"); } - public async Task UpdateAsync(IEnumerable entities, - CancellationToken cancellationToken = default(CancellationToken)) + public async Task UpdateAsync(IEnumerable entities, CancellationToken cancellationToken) { foreach (var entity in entities) { @@ -103,15 +102,12 @@ public async Task UpdateAsync(IEnumerable entities, } } - public async Task UpdateAsync(params T[] entities) + public async Task UpdateAsync(IEnumerable entities) { - foreach (var entity in entities) - { - await UpdateAsync(entity); - } + await UpdateAsync(entities, new CancellationToken()); } - public async Task UpdateAsync(IEnumerable entities) + public async Task UpdateAsync(params T[] entities) { foreach (var entity in entities) { From a73090cbe9924a17111ff3c743d31dd4366cce41 Mon Sep 17 00:00:00 2001 From: Carlos Carrero Date: Thu, 9 Nov 2017 13:25:55 -0500 Subject: [PATCH 5/6] Refactoring namespaces --- .../Yuxi.SharedKernel.Data.Abstractions.csproj | 2 +- .../Abstractions/IQueryableRepository.cs | 2 +- .../Repository/QueryableRepository.cs | 2 +- .../Base/CompositeSpecification.cs | 2 +- .../Base/ExpressionSpecification.cs | 2 +- Yuxi.SharedKernel.Specification/Contracts/ISpecification.cs | 2 +- .../Implementations/AllSpecification.cs | 2 +- .../Implementations/AndSpecification.cs | 2 +- .../Implementations/NotSpecification.cs | 2 +- .../Implementations/OrSpecification.cs | 2 +- ...csproj => Yuxi.SharedKernel.Patterns.Specification.csproj} | 0 .../AggregateGenericRepositoryReliableStateManager.cs | 4 ++-- Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj | 2 +- Yuxi.SharedKernel.sln | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) rename Yuxi.SharedKernel.Specification/{Yuxi.SharedKernel.Specification.csproj => Yuxi.SharedKernel.Patterns.Specification.csproj} (100%) diff --git a/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj b/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj index af3c7ed..1029861 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj +++ b/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj @@ -9,7 +9,7 @@ - + diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs index 5306008..847793c 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Query; using Data.Abstractions.PagedList; - using Specification.Contracts; + using Patterns.Specification.Contracts; public interface IQueryableRepository : Data.Abstractions.Repository.IQueryableRepository { diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs index e606369..307f3a9 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs @@ -10,7 +10,7 @@ using Data.Abstractions.PagedList; using Abstractions; using PagedList; - using Specification.Contracts; + using Patterns.Specification.Contracts; internal class QueryableRepository : IQueryableRepository where TEntity : class { diff --git a/Yuxi.SharedKernel.Specification/Base/CompositeSpecification.cs b/Yuxi.SharedKernel.Specification/Base/CompositeSpecification.cs index 48cbc3a..285ed9f 100644 --- a/Yuxi.SharedKernel.Specification/Base/CompositeSpecification.cs +++ b/Yuxi.SharedKernel.Specification/Base/CompositeSpecification.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Specification.Base +namespace Yuxi.SharedKernel.Patterns.Specification.Base { using Contracts; using Implementations; diff --git a/Yuxi.SharedKernel.Specification/Base/ExpressionSpecification.cs b/Yuxi.SharedKernel.Specification/Base/ExpressionSpecification.cs index eedbc74..cd32fba 100644 --- a/Yuxi.SharedKernel.Specification/Base/ExpressionSpecification.cs +++ b/Yuxi.SharedKernel.Specification/Base/ExpressionSpecification.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Specification.Base +namespace Yuxi.SharedKernel.Patterns.Specification.Base { using System; using System.Linq.Expressions; diff --git a/Yuxi.SharedKernel.Specification/Contracts/ISpecification.cs b/Yuxi.SharedKernel.Specification/Contracts/ISpecification.cs index ffa658f..bd64cde 100644 --- a/Yuxi.SharedKernel.Specification/Contracts/ISpecification.cs +++ b/Yuxi.SharedKernel.Specification/Contracts/ISpecification.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Specification.Contracts +namespace Yuxi.SharedKernel.Patterns.Specification.Contracts { public interface ISpecification { diff --git a/Yuxi.SharedKernel.Specification/Implementations/AllSpecification.cs b/Yuxi.SharedKernel.Specification/Implementations/AllSpecification.cs index 1397134..0ab4a61 100644 --- a/Yuxi.SharedKernel.Specification/Implementations/AllSpecification.cs +++ b/Yuxi.SharedKernel.Specification/Implementations/AllSpecification.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Specification.Implementations +namespace Yuxi.SharedKernel.Patterns.Specification.Implementations { using Base; diff --git a/Yuxi.SharedKernel.Specification/Implementations/AndSpecification.cs b/Yuxi.SharedKernel.Specification/Implementations/AndSpecification.cs index 44570a8..f63f7f3 100644 --- a/Yuxi.SharedKernel.Specification/Implementations/AndSpecification.cs +++ b/Yuxi.SharedKernel.Specification/Implementations/AndSpecification.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Specification.Implementations +namespace Yuxi.SharedKernel.Patterns.Specification.Implementations { using Base; using Contracts; diff --git a/Yuxi.SharedKernel.Specification/Implementations/NotSpecification.cs b/Yuxi.SharedKernel.Specification/Implementations/NotSpecification.cs index 9bd4269..70ef598 100644 --- a/Yuxi.SharedKernel.Specification/Implementations/NotSpecification.cs +++ b/Yuxi.SharedKernel.Specification/Implementations/NotSpecification.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Specification.Implementations +namespace Yuxi.SharedKernel.Patterns.Specification.Implementations { using Base; using Contracts; diff --git a/Yuxi.SharedKernel.Specification/Implementations/OrSpecification.cs b/Yuxi.SharedKernel.Specification/Implementations/OrSpecification.cs index 551e67d..aa2eb06 100644 --- a/Yuxi.SharedKernel.Specification/Implementations/OrSpecification.cs +++ b/Yuxi.SharedKernel.Specification/Implementations/OrSpecification.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Specification.Implementations +namespace Yuxi.SharedKernel.Patterns.Specification.Implementations { using Base; using Contracts; diff --git a/Yuxi.SharedKernel.Specification/Yuxi.SharedKernel.Specification.csproj b/Yuxi.SharedKernel.Specification/Yuxi.SharedKernel.Patterns.Specification.csproj similarity index 100% rename from Yuxi.SharedKernel.Specification/Yuxi.SharedKernel.Specification.csproj rename to Yuxi.SharedKernel.Specification/Yuxi.SharedKernel.Patterns.Specification.csproj diff --git a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs index ecf699d..ac8ba38 100644 --- a/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs +++ b/Yuxi.SharedKernel.Storage/Implementations/AggregateGenericRepositoryReliableStateManager.cs @@ -4,8 +4,8 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; - using Specification.Contracts; - using Specification.Implementations; + using Patterns.Specification.Contracts; + using Patterns.Specification.Implementations; using Contracts; using Microsoft.ServiceFabric.Data; using Microsoft.ServiceFabric.Data.Collections; diff --git a/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj b/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj index ea96e24..3370070 100644 --- a/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj +++ b/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj @@ -10,7 +10,7 @@ - + diff --git a/Yuxi.SharedKernel.sln b/Yuxi.SharedKernel.sln index 268c106..fa22e7a 100644 --- a/Yuxi.SharedKernel.sln +++ b/Yuxi.SharedKernel.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27004.2005 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yuxi.SharedKernel.Specification", "Yuxi.SharedKernel.Specification\Yuxi.SharedKernel.Specification.csproj", "{57D38CFE-F0EB-4F5A-BA78-BD3E4A84CC5F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yuxi.SharedKernel.Patterns.Specification", "Yuxi.SharedKernel.Specification\Yuxi.SharedKernel.Patterns.Specification.csproj", "{57D38CFE-F0EB-4F5A-BA78-BD3E4A84CC5F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yuxi.SharedKernel.Storage", "Yuxi.SharedKernel.Storage\Yuxi.SharedKernel.Storage.csproj", "{642718DA-8548-40C7-9177-4E4C7AB06BE6}" EndProject From 07b961ce80264250d7a9ca935cf1e090ceb0a2ac Mon Sep 17 00:00:00 2001 From: Carlos Carrero Date: Fri, 10 Nov 2017 10:32:33 -0500 Subject: [PATCH 6/6] Restructuring the solution --- .../Entities/Entity.cs | 4 +++ .../IEnumerablePagedListExtensions.cs | 4 +++ .../PagedList/IPagedList.cs | 4 +++ .../PagedList/PagedList.cs | 25 ++++++++++++++++++- .../Repository/IQueryableRepository.cs | 4 +++ .../Repository/IRepository.cs | 16 ++++++++++++ .../UnitOfWork/IUnitOfWork.cs | 4 +++ .../Abstractions/IQueryableRepository.cs | 4 +++ .../Abstractions/IRepositoryFactory.cs | 4 +++ .../Abstractions/IUnitOfWork.cs | 12 +++++++-- .../PagedList/IQueryablePageListExtensions.cs | 4 +++ .../Repository/QueryableRepository.cs | 13 ++++++++++ .../Repository/Repository.cs | 25 +++++++++++++++++++ .../{Repository => UnitOfWork}/UnitOfWork.cs | 21 +++++++++++++++- .../UnitOfWorkServiceCollectionExtensions.cs | 8 ++++-- ...aredKernel.Data.EntityFrameworkCore.csproj | 1 - 16 files changed, 146 insertions(+), 7 deletions(-) rename Yuxi.SharedKernel.Data.EntityFrameworkCore/{Repository => UnitOfWork}/UnitOfWork.cs (93%) rename Yuxi.SharedKernel.Data.EntityFrameworkCore/{Repository => UnitOfWork}/UnitOfWorkServiceCollectionExtensions.cs (92%) diff --git a/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs b/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs index c9d62c3..cdc48a9 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs @@ -2,6 +2,10 @@ { public abstract class Entity { + #region Public Properties + public string Id { get; set; } + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs index 5671285..10aee9f 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs @@ -5,11 +5,15 @@ public static class IEnumerablePagedListExtensions { + #region Public Static Methods + public static IPagedList ToPagedList(this IEnumerable source, int pageIndex, int pageSize, int indexFrom = 0) => new PagedList(source, pageIndex, pageSize, indexFrom); public static IPagedList ToPagedList(this IEnumerable source, Func, IEnumerable> converter, int pageIndex, int pageSize, int indexFrom = 0) => new PagedList(source, converter, pageIndex, pageSize, indexFrom); + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs index 35d3bec..b795983 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs @@ -4,6 +4,8 @@ public interface IPagedList { + #region Public Read Only Properties + int IndexFrom { get; } int PageIndex { get; } @@ -19,5 +21,7 @@ public interface IPagedList bool HasPreviousPage { get; } bool HasNextPage { get; } + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs b/Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs index e24639e..8e2651d 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs @@ -6,6 +6,8 @@ public class PagedList : IPagedList { + #region Public Properties + public int PageIndex { get; set; } public int PageSize { get; set; } @@ -16,13 +18,20 @@ public class PagedList : IPagedList public int IndexFrom { get; set; } - public IList Items { get; set; } + + #endregion + + #region Public Read Only Properties public bool HasPreviousPage => PageIndex - IndexFrom > 0; public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages; + #endregion + + #region Constructors + public PagedList(IEnumerable source, int pageIndex, int pageSize, int indexFrom) { if (indexFrom > pageIndex) @@ -41,10 +50,14 @@ public PagedList(IEnumerable source, int pageIndex, int pageSize, int indexFr } public PagedList() => Items = new T[0]; + + #endregion } public class PagedList : IPagedList { + #region Public Read Only Properties + public int PageIndex { get; } public int PageSize { get; } @@ -61,6 +74,10 @@ public class PagedList : IPagedList public bool HasNextPage => PageIndex - IndexFrom + 1 < TotalPages; + #endregion + + #region Constructors + public PagedList(IEnumerable source, Func, IEnumerable> converter, int pageIndex, int pageSize, int indexFrom) { @@ -91,14 +108,20 @@ public PagedList(IPagedList source, Func, IEnumera Items = new List(converter(source.Items)); } + + #endregion } public static class PagedList { + #region Public Static Methods + public static IPagedList Empty() => new PagedList(); public static IPagedList From(IPagedList source, Func, IEnumerable> converter) => new PagedList(source, converter); + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs b/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs index fa3ce02..b923337 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs @@ -8,6 +8,8 @@ public interface IQueryableRepository { + #region Public Methods + IQueryable FromSql(string sql, params object[] parameters); TEntity Find(params object[] keyValues); @@ -17,5 +19,7 @@ public interface IQueryableRepository Task FindAsync(object[] keyValues, CancellationToken cancellationToken); int Count(Expression> predicate = null); + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs index 16c3fde..b0d1509 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs @@ -6,6 +6,10 @@ public interface IRepository where TEntity : class { + #region Public Methods + + #region Inserts + void Insert(TEntity entity); void Insert(params TEntity[] entities); @@ -19,6 +23,10 @@ public interface IRepository where TEntity : class Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)); + #endregion + + #region Updates + void Update(TEntity entity); void Update(params TEntity[] entities); @@ -32,6 +40,10 @@ Task InsertAsync(IEnumerable entities, Task UpdateAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)); + #endregion + + #region Deletes + void Delete(object id); void Delete(TEntity entity); @@ -47,5 +59,9 @@ Task UpdateAsync(IEnumerable entities, Task DeleteAsync(params TEntity[] entities); Task DeleteAsync(IEnumerable entities); + + #endregion + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs index 58aeac0..c20efd6 100644 --- a/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs +++ b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs @@ -6,6 +6,8 @@ public interface IUnitOfWork : IDisposable { + #region Public Methods + int SaveChanges(bool ensureAutoHistory = false); Task SaveChangesAsync(bool ensureAutoHistory = false); @@ -13,5 +15,7 @@ public interface IUnitOfWork : IDisposable int ExecuteSqlCommand(string sql, params object[] parameters); IQueryable FromSql(string sql, params object[] parameters) where TEntity : class; + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs index 847793c..25909df 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs @@ -11,6 +11,8 @@ public interface IQueryableRepository : Data.Abstractions.Repository.IQueryableRepository { + #region Public Methods + IPagedList GetPagedList(ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -53,5 +55,7 @@ TResult GetFirstOrDefault(Expression> selector, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, bool disableTracking = true); + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs index 4828828..87b4f36 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs @@ -4,8 +4,12 @@ public interface IRepositoryFactory { + #region Public Methods + IRepository GetRepository() where TEntity : class; IQueryableRepository GetQueryableRepository() where TEntity : class; + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs index 8e6f189..3b46a40 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs @@ -7,12 +7,20 @@ public interface IUnitOfWork : IUnitOfWork where TContext : DbContext { + #region Read Only Properties + + TContext DbContext { get; } + + #endregion + + #region Public Methods + IRepository GetRepository() where TEntity : class; IQueryableRepository GetQueryableRepository() where TEntity : class; - TContext DbContext { get; } - Task SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks); + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs index 30d5248..aca3648 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs @@ -9,6 +9,8 @@ public static class IQueryablePageListExtensions { + #region Public Static Methods + public static async Task> ToPagedListAsync(this IQueryable source, int pageIndex, int pageSize, int indexFrom = 0, CancellationToken cancellationToken = default(CancellationToken)) { @@ -34,5 +36,7 @@ public static async Task> ToPagedListAsync(this IQueryable s return pagedList; } + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs index 307f3a9..f3e7acd 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs @@ -14,15 +14,26 @@ internal class QueryableRepository : IQueryableRepository where TEntity : class { + #region Protected Read Only Properties + protected readonly DbContext DbContext; + protected readonly DbSet DbSet; + #endregion + + #region Constructors + public QueryableRepository(DbContext dbContext) { DbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); DbSet = DbContext.Set(); } + #endregion + + #region Public Methods + public IPagedList GetPagedList(ISpecification businessSpecification = null, Func, IOrderedQueryable> orderBy = null, Func, IIncludableQueryable> include = null, @@ -205,5 +216,7 @@ public Task FindAsync(object[] keyValues, CancellationToken cancellatio public int Count(Expression> predicate = null) => predicate == null ? DbSet.Count() : DbSet.Count(predicate); + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs index 9ba521d..8233ed1 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs @@ -11,15 +11,28 @@ internal class Repository : IRepository where TEntity : class { + #region Protected Read Only Properties + protected readonly DbContext DbContext; + protected readonly DbSet DbSet; + #endregion + + #region Constructors + public Repository(DbContext dbContext) { DbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); DbSet = DbContext.Set(); } + #endregion + + #region Public Methods + + #region Inserts + public void Insert(TEntity entity) { DbSet.Add(entity); @@ -40,6 +53,10 @@ public Task InsertAsync(IEnumerable entities, CancellationToken cancellationToken = default(CancellationToken)) => DbSet.AddRangeAsync(entities, cancellationToken); + #endregion + + #region Updates + public void Update(TEntity entity) { DbSet.Update(entity); @@ -65,6 +82,10 @@ public Task UpdateAsync(IEnumerable entities, throw new NotImplementedException(); } + #endregion + + #region Deletes + public void Delete(TEntity entity) => DbSet.Remove(entity); public void Delete(object id) @@ -112,5 +133,9 @@ public Task DeleteAsync(IEnumerable entities) { throw new NotImplementedException(); } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWork.cs similarity index 93% rename from Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs rename to Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWork.cs index f5af47c..944f5d7 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWork.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWork.cs @@ -1,4 +1,4 @@ -namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Repository +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.UnitOfWork { using System; using System.Collections.Generic; @@ -8,21 +8,38 @@ using Microsoft.EntityFrameworkCore.Storage; using Data.Abstractions.Repository; using Data.Abstractions.UnitOfWork; + using Repository; using Abstractions; public class UnitOfWork : IRepositoryFactory, IUnitOfWork where TContext : DbContext { + #region Private Members + private bool _disposed; + private Dictionary _commandRepositories; + private Dictionary _queryableRepositories; + #endregion + + #region Public Read Only Properties + public TContext DbContext { get; } + #endregion + + #region Constructors + public UnitOfWork(TContext context) { DbContext = context ?? throw new ArgumentNullException(nameof(context)); } + #endregion + + #region Public Methods + public IRepository GetRepository() where TEntity : class { if (_commandRepositories == null) @@ -138,5 +155,7 @@ protected virtual void Dispose(bool disposing) _disposed = true; } + + #endregion } } \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs similarity index 92% rename from Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs rename to Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs index 07fd217..4fb0780 100644 --- a/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/UnitOfWorkServiceCollectionExtensions.cs +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs @@ -1,12 +1,14 @@ -namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Repository +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.UnitOfWork { using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Data.Abstractions.UnitOfWork; using Abstractions; - internal static class UnitOfWorkServiceCollectionExtensions + public static class UnitOfWorkServiceCollectionExtensions { + #region Public Static Methods + public static IServiceCollection AddUnitOfWork(this IServiceCollection services) where TContext : DbContext { @@ -54,5 +56,7 @@ public static IServiceCollection AddUnitOfWork -