添加项目文件。
This commit is contained in:
225
Entities/Context/UnitOfWork/UnitOfWork.cs
Normal file
225
Entities/Context/UnitOfWork/UnitOfWork.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
|
||||
namespace SharedDATA.Api
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Transactions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the default implementation of the <see cref="IUnitOfWork"/> and <see cref="IUnitOfWork{TContext}"/> interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The type of the db context.</typeparam>
|
||||
public class UnitOfWork<TContext> : IRepositoryFactory, IUnitOfWork<TContext>, IUnitOfWork where TContext : DbContext
|
||||
{
|
||||
private readonly TContext _context;
|
||||
private bool disposed = false;
|
||||
private Dictionary<Type, object> repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnitOfWork{TContext}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
public UnitOfWork(TContext context)
|
||||
{
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the db context.
|
||||
/// </summary>
|
||||
/// <returns>The instance of type <typeparamref name="TContext"/>.</returns>
|
||||
public TContext DbContext => _context;
|
||||
|
||||
/// <summary>
|
||||
/// Changes the database name. This require the databases in the same machine. NOTE: This only work for MySQL right now.
|
||||
/// </summary>
|
||||
/// <param name="database">The database name.</param>
|
||||
/// <remarks>
|
||||
/// This only been used for supporting multiple databases in the same model. This require the databases in the same machine.
|
||||
/// </remarks>
|
||||
public void ChangeDatabase(string database)
|
||||
{
|
||||
var connection = _context.Database.GetDbConnection();
|
||||
if (connection.State.HasFlag(ConnectionState.Open))
|
||||
{
|
||||
connection.ChangeDatabase(database);
|
||||
}
|
||||
else
|
||||
{
|
||||
var connectionString = Regex.Replace(connection.ConnectionString.Replace(" ", ""), @"(?<=[Dd]atabase=)\w+(?=;)", database, RegexOptions.Singleline);
|
||||
connection.ConnectionString = connectionString;
|
||||
}
|
||||
|
||||
// Following code only working for mysql.
|
||||
var items = _context.Model.GetEntityTypes();
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item is IConventionEntityType entityType)
|
||||
{
|
||||
entityType.SetSchema(database);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the db context.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public TDbContext GetDbContext<TDbContext>() where TDbContext : DbContext
|
||||
{
|
||||
if (_context == null)
|
||||
return null;
|
||||
return _context as TDbContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified repository for the <typeparamref name="TEntity"/>.
|
||||
/// </summary>
|
||||
/// <param name="hasCustomRepository"><c>True</c> if providing custom repositry</param>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <returns>An instance of type inherited from <see cref="IRepository{TEntity}"/> interface.</returns>
|
||||
public IRepository<TEntity> GetRepository<TEntity>(bool hasCustomRepository = false) where TEntity : class
|
||||
{
|
||||
if (repositories == null)
|
||||
{
|
||||
repositories = new Dictionary<Type, object>();
|
||||
}
|
||||
|
||||
// what's the best way to support custom reposity?
|
||||
if (hasCustomRepository)
|
||||
{
|
||||
var customRepo = _context.GetService<IRepository<TEntity>>();
|
||||
if (customRepo != null)
|
||||
{
|
||||
return customRepo;
|
||||
}
|
||||
}
|
||||
|
||||
var type = typeof(TEntity);
|
||||
if (!repositories.ContainsKey(type))
|
||||
{
|
||||
repositories[type] = new Repository<TEntity>(_context);
|
||||
}
|
||||
|
||||
return (IRepository<TEntity>)repositories[type];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified raw SQL command.
|
||||
/// </summary>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>The number of state entities written to database.</returns>
|
||||
public int ExecuteSqlCommand(string sql, params object[] parameters) => _context.Database.ExecuteSqlRaw(sql, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Uses raw SQL queries to fetch the specified <typeparamref name="TEntity" /> data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <param name="sql">The raw SQL.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns>An <see cref="IQueryable{T}" /> that contains elements that satisfy the condition specified by raw SQL.</returns>
|
||||
public IQueryable<TEntity> FromSql<TEntity>(string sql, params object[] parameters) where TEntity : class => _context.Set<TEntity>().FromSqlRaw(sql, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Saves all changes made in this context to the database.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <returns>The number of state entries written to the database.</returns>
|
||||
public int SaveChanges(bool ensureAutoHistory = false)
|
||||
{
|
||||
if (ensureAutoHistory)
|
||||
{
|
||||
_context.EnsureAutoHistory();
|
||||
}
|
||||
|
||||
return _context.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously saves all changes made in this unit of work to the database.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous save operation. The task result contains the number of state entities written to database.</returns>
|
||||
public async Task<int> SaveChangesAsync(bool ensureAutoHistory = false)
|
||||
{
|
||||
if (ensureAutoHistory)
|
||||
{
|
||||
_context.EnsureAutoHistory();
|
||||
}
|
||||
|
||||
return await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves all changes made in this context to the database with distributed transaction.
|
||||
/// </summary>
|
||||
/// <param name="ensureAutoHistory"><c>True</c> if save changes ensure auto record the change history.</param>
|
||||
/// <param name="unitOfWorks">An optional <see cref="IUnitOfWork"/> array.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous save operation. The task result contains the number of state entities written to database.</returns>
|
||||
public async Task<int> SaveChangesAsync(bool ensureAutoHistory = false, params IUnitOfWork[] unitOfWorks)
|
||||
{
|
||||
using (var ts = new TransactionScope())
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var unitOfWork in unitOfWorks)
|
||||
{
|
||||
count += await unitOfWork.SaveChangesAsync(ensureAutoHistory);
|
||||
}
|
||||
|
||||
count += await SaveChangesAsync(ensureAutoHistory);
|
||||
|
||||
ts.Complete();
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">The disposing.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// clear repositories
|
||||
if (repositories != null)
|
||||
{
|
||||
repositories.Clear();
|
||||
}
|
||||
|
||||
// dispose the db context.
|
||||
_context.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public void TrackGraph(object rootEntity, Action<EntityEntryGraphNode> callback)
|
||||
{
|
||||
_context.ChangeTracker.TrackGraph(rootEntity, callback);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user