diff --git a/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs b/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs new file mode 100644 index 0000000..cdc48a9 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Entities/Entity.cs @@ -0,0 +1,11 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.Entities +{ + 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 new file mode 100644 index 0000000..10aee9f --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IEnumerablePagedListExtensions.cs @@ -0,0 +1,19 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.PagedList +{ + using System; + using System.Collections.Generic; + + 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 new file mode 100644 index 0000000..b795983 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/IPagedList.cs @@ -0,0 +1,27 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.PagedList +{ + using System.Collections.Generic; + + public interface IPagedList + { + #region Public Read Only Properties + + int IndexFrom { get; } + + int PageIndex { get; } + + int PageSize { get; } + + int TotalCount { get; } + + int TotalPages { get; } + + IList Items { get; } + + 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 new file mode 100644 index 0000000..8e2651d --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/PagedList/PagedList.cs @@ -0,0 +1,127 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.PagedList +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public class PagedList : IPagedList + { + #region Public Properties + + 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; } + + #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) + { + 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(); + } + + public PagedList() => Items = new T[0]; + + #endregion + } + + public class PagedList : IPagedList + { + #region Public Read Only Properties + + 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; + + #endregion + + #region Constructors + + 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)); + } + + #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 new file mode 100644 index 0000000..b923337 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IQueryableRepository.cs @@ -0,0 +1,25 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.Repository +{ + using System; + using System.Linq; + using System.Linq.Expressions; + using System.Threading; + using System.Threading.Tasks; + + public interface IQueryableRepository + { + #region Public Methods + + 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); + + #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 new file mode 100644 index 0000000..b0d1509 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Repository/IRepository.cs @@ -0,0 +1,67 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.Repository +{ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + public interface IRepository where TEntity : class + { + #region Public Methods + + #region Inserts + + 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)); + + #endregion + + #region Updates + + void Update(TEntity entity); + + void Update(params TEntity[] 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)); + + #endregion + + #region Deletes + + void Delete(object id); + + void Delete(TEntity entity); + + 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); + + #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 new file mode 100644 index 0000000..c20efd6 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/UnitOfWork/IUnitOfWork.cs @@ -0,0 +1,21 @@ +namespace Yuxi.SharedKernel.Data.Abstractions.UnitOfWork +{ + using System; + using System.Linq; + using System.Threading.Tasks; + + public interface IUnitOfWork : IDisposable + { + #region Public Methods + + 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; + + #endregion + } +} \ 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..1029861 --- /dev/null +++ b/Yuxi.SharedKernel.Data.Abstractions/Yuxi.SharedKernel.Data.Abstractions.csproj @@ -0,0 +1,15 @@ + + + + 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..25909df --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IQueryableRepository.cs @@ -0,0 +1,61 @@ +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 Patterns.Specification.Contracts; + + public interface IQueryableRepository : Data.Abstractions.Repository.IQueryableRepository + { + #region Public Methods + + 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); + + #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 new file mode 100644 index 0000000..87b4f36 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IRepositoryFactory.cs @@ -0,0 +1,15 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.Abstractions +{ + using Data.Abstractions.Repository; + + 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 new file mode 100644 index 0000000..3b46a40 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Abstractions/IUnitOfWork.cs @@ -0,0 +1,26 @@ +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 + { + #region Read Only Properties + + TContext DbContext { get; } + + #endregion + + #region Public Methods + + IRepository GetRepository() where TEntity : class; + + IQueryableRepository GetQueryableRepository() where TEntity : class; + + 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 new file mode 100644 index 0000000..aca3648 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/PagedList/IQueryablePageListExtensions.cs @@ -0,0 +1,42 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.PagedList +{ + using System; + using System.Linq; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + using Data.Abstractions.PagedList; + + 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)) + { + 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; + } + + #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 new file mode 100644 index 0000000..f3e7acd --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/QueryableRepository.cs @@ -0,0 +1,222 @@ +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 Data.Abstractions.PagedList; + using Abstractions; + using PagedList; + using Patterns.Specification.Contracts; + + 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, + int pageIndex = 0, + int pageSize = 20, + bool disableTracking = true) + { + IQueryable query = DbSet; + if (disableTracking) + { + query = query.AsNoTracking(); + } + + if (include != null) + { + query = include(query); + } + + if (businessSpecification != null) + { + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); + } + + return orderBy != null + ? orderBy(query).ToPagedList(pageIndex, pageSize) + : query.ToPagedList(pageIndex, pageSize); + } + + public Task> GetPagedListAsync(ISpecification businessSpecification = 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 (businessSpecification != null) + { + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); + } + + return orderBy?.Invoke(query).ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken) ?? + query.ToPagedListAsync(pageIndex, pageSize, 0, cancellationToken); + } + + public IPagedList GetPagedList(Expression> selector, + ISpecification businessSpecification = 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 (businessSpecification != null) + { + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); + } + + return orderBy != null + ? orderBy(query).Select(selector).ToPagedList(pageIndex, pageSize) + : query.Select(selector).ToPagedList(pageIndex, pageSize); + } + + public Task> GetPagedListAsync(Expression> selector, + ISpecification businessSpecification = 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 (businessSpecification != null) + { + query = query.Where(entityToTest => businessSpecification.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 businessSpecification = 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 (businessSpecification != null) + { + query = query.Where(entityToTest => businessSpecification.IsSatisfiedBy(entityToTest)); + } + + return orderBy != null ? orderBy(query).FirstOrDefault() : query.FirstOrDefault(); + } + + public TResult GetFirstOrDefault(Expression> selector, + ISpecification businessSpecification = 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 (businessSpecification != null) + { + query = query.Where(entityToTest => businessSpecification.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); + + #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 new file mode 100644 index 0000000..8233ed1 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/Repository/Repository.cs @@ -0,0 +1,141 @@ +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 Data.Abstractions.Repository; + + 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); + } + + 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); + } + + public Task InsertAsync(params TEntity[] entities) => DbSet.AddRangeAsync(entities); + + public Task InsertAsync(IEnumerable entities, + CancellationToken cancellationToken = default(CancellationToken)) => + DbSet.AddRangeAsync(entities, cancellationToken); + + #endregion + + #region Updates + + public void Update(TEntity entity) + { + DbSet.Update(entity); + } + + public void Update(params TEntity[] entities) => DbSet.UpdateRange(entities); + + 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(); + } + + #endregion + + #region Deletes + + 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); + + 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(); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWork.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWork.cs new file mode 100644 index 0000000..944f5d7 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWork.cs @@ -0,0 +1,161 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.UnitOfWork +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.EntityFrameworkCore; + 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) + { + _commandRepositories = new Dictionary(); + } + + var type = typeof(TEntity); + if (!_commandRepositories.ContainsKey(type)) + { + _commandRepositories[type] = new Repository(DbContext); + } + + return (IRepository) _commandRepositories[type]; + } + + public Abstractions.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 (Abstractions.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; + + if (uow == null) + { + continue; + } + + uow.DbContext.Database.UseTransaction(transaction.GetDbTransaction()); + count += await uow.SaveChangesAsync(ensureAutoHistory); + } + + count += await SaveChangesAsync(ensureAutoHistory); + + transaction.Commit(); + + return count; + } + catch (Exception) + { + transaction.Rollback(); + + throw; + } + } + } + + 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; + } + + #endregion + } +} \ No newline at end of file diff --git a/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs new file mode 100644 index 0000000..4fb0780 --- /dev/null +++ b/Yuxi.SharedKernel.Data.EntityFrameworkCore/UnitOfWork/UnitOfWorkServiceCollectionExtensions.cs @@ -0,0 +1,62 @@ +namespace Yuxi.SharedKernel.Data.EntityFrameworkCore.UnitOfWork +{ + using Microsoft.EntityFrameworkCore; + using Microsoft.Extensions.DependencyInjection; + using Data.Abstractions.UnitOfWork; + using Abstractions; + + public static class UnitOfWorkServiceCollectionExtensions + { + #region Public Static Methods + + 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; + } + + #endregion + } +} \ 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.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.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/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..ac8ba38 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 Patterns.Specification.Contracts; + using Patterns.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 @@ -39,46 +35,47 @@ public AggregateGenericRepositoryReliableStateManager(IReliableDictionary Find(string id) - { - var entity = (await _aggreateStorage.TryGetValueAsync(_transaction, id)).Value; - var coreEntity = _mapper.MapToCore(entity); - return coreEntity; - } + #region Adds - public T Find(params object[] keyValues) + public async Task InsertAsync(T entity, CancellationToken cancellationToken = default(CancellationToken)) { - throw new NotImplementedException(); - } + var storableEntity = _mapper.MapToStorable(entity); + var succeed = await _aggreateStorage.TryAddAsync(_transaction, entity.Id, storableEntity); - public void UpsertGraph(T entity) - { - throw new NotImplementedException(); + if (!succeed) + { + throw new Exception($"Something went wrong when trying to update the entity {entity.Id}"); + } } - 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(); } @@ -87,46 +84,48 @@ public Task FindAsync(CancellationToken cancellationToken, params object[] ke #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 void Update(T entity) + public async Task UpdateAsync(IEnumerable entities, CancellationToken cancellationToken) { - var id = entity.Id; - - Task.Run(() => Update(id, entity)); + foreach (var entity in entities) + { + await UpdateAsync(entity, cancellationToken); + } } - #endregion - - #region Adds - - public async Task Add(T entity) + public async Task UpdateAsync(IEnumerable entities) { - 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; + await UpdateAsync(entities, new CancellationToken()); } - void IRepository.Add(T entity) + public async Task UpdateAsync(params T[] entities) { - Task.Run(() => Add(entity)); + foreach (var entity in entities) + { + await UpdateAsync(entity); + } } - #endregion + public void Update(T entity) + { + throw new NotImplementedException(); + } - #region Repositories + public void Update(params T[] entities) + { + throw new NotImplementedException(); + } - public IRepository GetRepository() where T1 : class, ITrackable + public void Update(IEnumerable entities) { throw new NotImplementedException(); } @@ -141,28 +140,65 @@ public Task Delete(T entity) return _aggreateStorage.TryRemoveAsync(_transaction, id); } - void IRepository.Delete(T entity) + 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) { - Task.Run(() => Delete(entity)); + throw new NotImplementedException(); } - public void Delete(params object[] keyValues) + void IRepository.Delete(T entity) { throw new NotImplementedException(); } - public Task DeleteAsync(params object[] keyValues) + public void Delete(params T[] entities) { throw new NotImplementedException(); } - public Task DeleteAsync(CancellationToken cancellationToken, params object[] keyValues) + public void Delete(IEnumerable entities) { throw new NotImplementedException(); } #endregion + #region Querys + + public async Task Find(string id) + { + var entity = (await _aggreateStorage.TryGetValueAsync(_transaction, id)).Value; + var coreEntity = _mapper.MapToCore(entity); + return coreEntity; + } + + #endregion + #region Lists public Task> ListAll() diff --git a/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj b/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj index 09ef2e0..3370070 100644 --- a/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj +++ b/Yuxi.SharedKernel.Storage/Yuxi.SharedKernel.Storage.csproj @@ -1,17 +1,16 @@ - net462 + netstandard2.0;net462 - - - + + diff --git a/Yuxi.SharedKernel.sln b/Yuxi.SharedKernel.sln index 6bfbcca..fa22e7a 100644 --- a/Yuxi.SharedKernel.sln +++ b/Yuxi.SharedKernel.sln @@ -3,11 +3,13 @@ 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.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