添加项目文件。

This commit is contained in:
SpecialX
2025-05-23 19:03:00 +08:00
parent 6fa7679fd3
commit d36fef2bbb
185 changed files with 13413 additions and 0 deletions

View 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);
}
}
}