initial commit

This commit is contained in:
tk
2024-11-13 18:18:28 +08:00
commit 013f35e296
1500 changed files with 443723 additions and 0 deletions

View File

@ -0,0 +1,96 @@
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql
{
internal class RepositoryDbContext : DbContext
{
protected IBaseRepository _repo;
public RepositoryDbContext(IFreeSql orm, IBaseRepository repo) : base()
{
_ormScoped = DbContextScopedFreeSql.Create(orm, () => this, () => repo.UnitOfWork);
_isUseUnitOfWork = false;
UnitOfWork = repo.UnitOfWork;
_repo = repo;
}
static ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>> _dicGetRepositoryDbField = new ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>>();
static FieldInfo GetRepositoryDbField(Type type, string fieldName) => _dicGetRepositoryDbField.GetOrAdd(type, tp => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(fieldName, fn =>
typeof(BaseRepository<,>).MakeGenericType(type, typeof(int)).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic));
public override IDbSet Set(Type entityType)
{
if (_dicSet.ContainsKey(entityType)) return _dicSet[entityType];
var tb = OrmOriginal.CodeFirst.GetTableByEntity(entityType);
if (tb == null) return null;
object repo = _repo;
if (entityType != _repo.EntityType)
{
repo = Activator.CreateInstance(typeof(DefaultRepository<,>).MakeGenericType(entityType, typeof(int)), _repo.Orm);
(repo as IBaseRepository).UnitOfWork = _repo.UnitOfWork;
GetRepositoryDbField(entityType, "_dbPriv").SetValue(repo, this);
GetRepositoryDbField(entityType, "_asTablePriv").SetValue(repo,
_repo.GetType().GetField("_asTablePriv", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_repo));
//GetRepositoryDbField(_repo.EntityType, "_asTablePriv").GetValue(_repo));
if (typeof(IBaseRepository<>).MakeGenericType(_repo.EntityType).IsAssignableFrom(_repo.GetType()))
typeof(RepositoryDbContext).GetMethod("SetRepositoryDataFilter").MakeGenericMethod(_repo.EntityType)
.Invoke(null, new object[] { repo, _repo });
}
var sd = Activator.CreateInstance(typeof(RepositoryDbSet<>).MakeGenericType(entityType), repo) as IDbSet;
_listSet.Add(sd);
if (entityType != typeof(object)) _dicSet.Add(entityType, sd);
return sd;
}
public static void SetRepositoryDataFilter<TEntity>(object repo, IBaseRepository<TEntity> baseRepo) where TEntity : class
{
var filter = baseRepo.DataFilter as DataFilter<TEntity>;
DataFilterUtil.SetRepositoryDataFilter(repo, fl =>
{
foreach (var f in filter._filters)
fl.Apply<TEntity>(f.Key, f.Value.Expression);
});
}
int SaveChangesSuccess()
{
int ret;
try
{
if (UnitOfWork?.EntityChangeReport != null)
{
UnitOfWork.EntityChangeReport.Report.AddRange(_entityChangeReport);
if (UnitOfWork.EntityChangeReport.OnChange == null) UnitOfWork.EntityChangeReport.OnChange = Options.OnEntityChange;
} else
EmitOnEntityChange(_entityChangeReport);
}
finally
{
_entityChangeReport.Clear();
ret = _affrows;
_affrows = 0;
}
return ret;
}
public override int SaveChanges()
{
FlushCommand();
return SaveChangesSuccess();
}
#if net40
#else
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
await FlushCommandAsync(cancellationToken);
return SaveChangesSuccess();
}
#endif
}
}

View File

@ -0,0 +1,93 @@
using FreeSql.Extensions.EntityUtil;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FreeSql
{
internal class RepositoryDbSet<TEntity> : DbSet<TEntity> where TEntity : class
{
protected BaseRepository<TEntity> _repo;
public RepositoryDbSet(BaseRepository<TEntity> repo)
{
_db = repo._db;
_uow = repo.UnitOfWork;
_repo = repo;
}
IUnitOfWork _uowPriv;
internal override IUnitOfWork _uow
{
get => _uowPriv;
set
{
_uowPriv = value;
foreach (var dbset in _dicDbSetObjects.Values) //配合 UnitOfWorkManager
dbset._uow = _uowPriv;
}
}
protected override ISelect<TEntity> OrmSelect(object dywhere)
{
var select = base.OrmSelect(dywhere);
if (_repo._asTablePriv != null) select.AsTable(_repo._asTablePriv);
var filters = (_repo.DataFilter as DataFilter<TEntity>)._filters;
foreach (var filter in filters.Where(a => a.Value.IsEnabled == true)) select.Where(filter.Value.Expression);
var disableFilter = filters.Where(a => a.Value.IsEnabled == false).Select(a => a.Key).ToList();
disableFilter.AddRange((_repo.DataFilter as DataFilter<TEntity>)._filtersByOrm.Where(a => a.Value.IsEnabled == false).Select(a => a.Key));
if (disableFilter.Any()) select.DisableGlobalFilter(disableFilter.ToArray());
return select;
}
internal ISelect<TEntity> OrmSelectInternal(object dywhere) => OrmSelect(dywhere);
protected override IUpdate<TEntity> OrmUpdate(IEnumerable<TEntity> entitys)
{
var update = base.OrmUpdate(entitys);
if (_repo._asTablePriv != null) update.AsTable(old => _repo._asTablePriv(_entityType, old));
var filters = (_repo.DataFilter as DataFilter<TEntity>)._filters;
foreach (var filter in filters.Where(a => a.Value.IsEnabled == true)) update.Where(filter.Value.Expression);
var disableFilter = filters.Where(a => a.Value.IsEnabled == false).Select(a => a.Key).ToList();
disableFilter.AddRange((_repo.DataFilter as DataFilter<TEntity>)._filtersByOrm.Where(a => a.Value.IsEnabled == false).Select(a => a.Key));
if (disableFilter.Any()) update.DisableGlobalFilter(disableFilter.ToArray());
return update;
}
internal IUpdate<TEntity> OrmUpdateInternal(IEnumerable<TEntity> entitys) => OrmUpdate(entitys);
protected override IDelete<TEntity> OrmDelete(object dywhere)
{
var delete = base.OrmDelete(dywhere);
if (_repo._asTablePriv != null) delete.AsTable(old => _repo._asTablePriv(_entityType, old));
var filters = (_repo.DataFilter as DataFilter<TEntity>)._filters;
foreach (var filter in filters.Where(a => a.Value.IsEnabled == true)) delete.Where(filter.Value.Expression);
var disableFilter = filters.Where(a => a.Value.IsEnabled == false).Select(a => a.Key).ToList();
disableFilter.AddRange((_repo.DataFilter as DataFilter<TEntity>)._filtersByOrm.Where(a => a.Value.IsEnabled == false).Select(a => a.Key));
if (disableFilter.Any()) delete.DisableGlobalFilter(disableFilter.ToArray());
return delete;
}
internal IDelete<TEntity> OrmDeleteInternal(object dywhere) => OrmDelete(dywhere);
protected override IDelete<object> OrmDeleteAsType(Type entityType)
{
var delete = base.OrmDeleteAsType(entityType);
if (_repo._asTablePriv != null) delete.AsTable(old => _repo._asTablePriv(_entityType, old));
return delete;
}
protected override IInsert<TEntity> OrmInsert(TEntity entity) => OrmInsert(new[] { entity });
protected override IInsert<TEntity> OrmInsert(IEnumerable<TEntity> entitys)
{
var insert = base.OrmInsert(entitys);
if (_repo._asTablePriv != null) insert.AsTable(old => _repo._asTablePriv(_entityType, old));
var filters = (_repo.DataFilter as DataFilter<TEntity>)._filters.Where(a => a.Value.IsEnabled == true);
foreach (var filter in filters)
{
if (entitys != null)
foreach (var entity in entitys)
if (filter.Value.ExpressionDelegate?.Invoke(entity) == false)
throw new Exception(DbContextStrings.InsertError_Filter(filter.Key, filter.Value.Expression, _db.OrmOriginal.GetEntityString(_entityType, entity)));
}
return insert;
}
internal IInsert<TEntity> OrmInsertInternal(TEntity entity) => OrmInsert(entity);
internal IInsert<TEntity> OrmInsertInternal(IEnumerable<TEntity> entitys) => OrmInsert(entitys);
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Linq.Expressions;
namespace FreeSql
{
public interface IRepositoryUnitOfWork : IUnitOfWork
{
/// <summary>
/// 在工作单元内创建默认仓库类,工作单元下的仓储操作具有事务特点
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TKey"></typeparam>
/// <param name="filter">数据过滤 + 验证</param>
/// <returns></returns>
IBaseRepository<TEntity, TKey> GetRepository<TEntity, TKey>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class;
/// <summary>
/// 在工作单元内创建联合主键的仓储类,工作单元下的仓储操作具有事务特点
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="filter">数据过滤 + 验证</param>
/// <returns></returns>
IBaseRepository<TEntity> GetRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class;
/// <summary>
/// 在工作单元内创建仓库类,工作单元下的仓储操作具有事务特点
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="filter">数据过滤 + 验证</param>
/// <param name="asTable">分表规则,参数:旧表名;返回:新表名 https://github.com/2881099/FreeSql/wiki/Repository</param>
/// <returns></returns>
IBaseRepository<TEntity, Guid> GetGuidRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null, Func<string, string> asTable = null) where TEntity : class;
}
class RepositoryUnitOfWork : UnitOfWork, IRepositoryUnitOfWork
{
public RepositoryUnitOfWork(IFreeSql fsql) : base(fsql)
{
}
public IBaseRepository<TEntity, Guid> GetGuidRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null, Func<string, string> asTable = null) where TEntity : class
{
var repo = new GuidRepository<TEntity>(_fsql, filter, asTable);
repo.UnitOfWork = this;
return repo;
}
public IBaseRepository<TEntity, TKey> GetRepository<TEntity, TKey>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class
{
var repo = new DefaultRepository<TEntity, TKey>(_fsql, filter);
repo.UnitOfWork = this;
return repo;
}
public IBaseRepository<TEntity> GetRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class
{
var repo = new DefaultRepository<TEntity, int>(_fsql, filter);
repo.UnitOfWork = this;
return repo;
}
}
}

View File

@ -0,0 +1,278 @@
using FreeSql.Internal;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace FreeSql
{
public interface IDataFilter<TEntity> : IDisposable where TEntity : class
{
IDataFilter<TEntity> Apply(string filterName, Expression<Func<TEntity, bool>> filterAndValidateExp);
/// <summary>
/// 开启过滤器,若使用 using 则使用完后,恢复为原有状态
/// </summary>
/// <param name="filterName">过滤器名称</param>
/// <returns></returns>
IDisposable Enable(params string[] filterName);
/// <summary>
/// 开启所有过滤器,若使用 using 则使用完后,恢复为原有状态
/// </summary>
/// <returns></returns>
IDisposable EnableAll();
/// <summary>
/// 禁用过滤器,若使用 using 则使用完后,恢复为原有状态
/// </summary>
/// <param name="filterName"></param>
/// <returns></returns>
IDisposable Disable(params string[] filterName);
/// <summary>
/// 禁用所有过滤器,若使用 using 则使用完后,恢复为原有状态
/// </summary>
/// <returns></returns>
IDisposable DisableAll();
bool IsEnabled(string filterName);
}
internal class DataFilter<TEntity> : IDataFilter<TEntity> where TEntity : class
{
internal class FilterItem
{
public Expression<Func<TEntity, bool>> Expression { get; set; }
Func<TEntity, bool> _expressionDelegate;
public Func<TEntity, bool> ExpressionDelegate => _expressionDelegate ?? (_expressionDelegate = Expression?.Compile());
public bool IsEnabled { get; set; }
}
internal class FilterItemByOrm
{
public GlobalFilter.Item Filter { get; set; }
public bool IsEnabled { get; set; }
}
internal ConcurrentDictionary<string, FilterItem> _filters = new ConcurrentDictionary<string, FilterItem>(StringComparer.CurrentCultureIgnoreCase);
internal ConcurrentDictionary<string, FilterItemByOrm> _filtersByOrm = new ConcurrentDictionary<string, FilterItemByOrm>(StringComparer.CurrentCultureIgnoreCase);
public IDataFilter<TEntity> Apply(string filterName, Expression<Func<TEntity, bool>> filterAndValidateExp)
{
if (filterName == null)
throw new ArgumentNullException(nameof(filterName));
if (filterAndValidateExp == null) return this;
var filterItem = new FilterItem { Expression = filterAndValidateExp, IsEnabled = true };
_filters.AddOrUpdate(filterName, filterItem, (k, v) => filterItem);
return this;
}
public IDisposable Disable(params string[] filterName)
{
if (filterName == null || filterName.Any() == false) return new UsingAny(() => { });
List<string> restore = new List<string>();
List<string> restoreByOrm = new List<string>();
foreach (var name in filterName)
{
if (_filters.TryGetValue(name, out var tryfi))
{
if (tryfi.IsEnabled)
{
restore.Add(name);
tryfi.IsEnabled = false;
}
}
if (_filtersByOrm.TryGetValue(name, out var tryfiByOrm))
{
if (tryfiByOrm.IsEnabled)
{
restoreByOrm.Add(name);
tryfiByOrm.IsEnabled = false;
}
}
}
return new UsingAny(() =>
{
restore.ForEach(name =>
{
if (_filters.TryGetValue(name, out var tryfi) && tryfi.IsEnabled == false)
tryfi.IsEnabled = true;
});
restoreByOrm.ForEach(name =>
{
if (_filtersByOrm.TryGetValue(name, out var tryfiByOrm) && tryfiByOrm.IsEnabled == false)
tryfiByOrm.IsEnabled = true;
});
});
}
public IDisposable DisableAll()
{
List<string> restore = new List<string>();
List<string> restoreByOrm = new List<string>();
foreach (var val in _filters)
{
if (val.Value.IsEnabled)
{
restore.Add(val.Key);
val.Value.IsEnabled = false;
}
}
foreach (var val in _filtersByOrm)
{
if (val.Value.IsEnabled)
{
restoreByOrm.Add(val.Key);
val.Value.IsEnabled = false;
}
}
return new UsingAny(() =>
{
restore.ForEach(name =>
{
if (_filters.TryGetValue(name, out var tryfi) && tryfi.IsEnabled == false)
tryfi.IsEnabled = true;
});
restoreByOrm.ForEach(name =>
{
if (_filtersByOrm.TryGetValue(name, out var tryfiByOrm) && tryfiByOrm.IsEnabled == false)
tryfiByOrm.IsEnabled = true;
});
});
}
class UsingAny : IDisposable
{
Action _ondis;
public UsingAny(Action ondis)
{
_ondis = ondis;
}
public void Dispose()
{
_ondis?.Invoke();
}
}
public IDisposable Enable(params string[] filterName)
{
if (filterName == null || filterName.Any() == false) return new UsingAny(() => { });
List<string> restore = new List<string>();
List<string> restoreByOrm = new List<string>();
foreach (var name in filterName)
{
if (_filters.TryGetValue(name, out var tryfi))
{
if (tryfi.IsEnabled == false)
{
restore.Add(name);
tryfi.IsEnabled = true;
}
}
if (_filtersByOrm.TryGetValue(name, out var tryfiByOrm))
{
if (tryfiByOrm.IsEnabled == false)
{
restoreByOrm.Add(name);
tryfiByOrm.IsEnabled = true;
}
}
}
return new UsingAny(() =>
{
restore.ForEach(name =>
{
if (_filters.TryGetValue(name, out var tryfi) && tryfi.IsEnabled == true)
tryfi.IsEnabled = false;
});
restoreByOrm.ForEach(name =>
{
if (_filtersByOrm.TryGetValue(name, out var tryfiByOrm) && tryfiByOrm.IsEnabled == true)
tryfiByOrm.IsEnabled = false;
});
});
}
public IDisposable EnableAll()
{
List<string> restore = new List<string>();
List<string> restoreByOrm = new List<string>();
foreach (var val in _filters)
{
if (val.Value.IsEnabled == false)
{
restore.Add(val.Key);
val.Value.IsEnabled = true;
}
}
foreach (var val in _filtersByOrm)
{
if (val.Value.IsEnabled == false)
{
restoreByOrm.Add(val.Key);
val.Value.IsEnabled = true;
}
}
return new UsingAny(() =>
{
restore.ForEach(name =>
{
if (_filters.TryGetValue(name, out var tryfi) && tryfi.IsEnabled == true)
tryfi.IsEnabled = false;
});
restoreByOrm.ForEach(name =>
{
if (_filtersByOrm.TryGetValue(name, out var tryfiByOrm) && tryfiByOrm.IsEnabled == true)
tryfiByOrm.IsEnabled = false;
});
});
}
public bool IsEnabled(string filterName)
{
if (filterName == null) return false;
return _filters.TryGetValue(filterName, out var tryfi) ? tryfi.IsEnabled :
_filtersByOrm.TryGetValue(filterName, out var tryfiByOrm) ? tryfiByOrm.IsEnabled : false;
}
~DataFilter() => this.Dispose();
public void Dispose()
{
_filters.Clear();
}
}
public class FluentDataFilter : IDisposable
{
internal class FilterInfo
{
public Type type { get; }
public string name { get; }
public LambdaExpression exp { get; }
public FilterInfo(Type type, string name, LambdaExpression exp)
{
this.type = type;
this.name = name;
this.exp = exp;
}
}
internal List<FilterInfo> _filters = new List<FilterInfo>();
public FluentDataFilter Apply<TEntity>(string filterName, Expression<Func<TEntity, bool>> filterAndValidateExp) where TEntity : class
{
if (filterName == null)
throw new ArgumentNullException(nameof(filterName));
if (filterAndValidateExp == null) return this;
_filters.Add(new FilterInfo(typeof(TEntity), filterName, filterAndValidateExp));
return this;
}
~FluentDataFilter() => this.Dispose();
public void Dispose()
{
_filters.Clear();
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace FreeSql
{
internal class DataFilterUtil
{
internal static Action<FluentDataFilter> _globalDataFilter;
static ConcurrentDictionary<Type, Delegate> _dicSetRepositoryDataFilterApplyDataFilterFunc = new ConcurrentDictionary<Type, Delegate>();
static ConcurrentDictionary<Type, ConcurrentDictionary<string, bool>> _dicSetRepositoryDataFilterConvertFilterNotExists = new ConcurrentDictionary<Type, ConcurrentDictionary<string, bool>>();
internal static void SetRepositoryDataFilter(object repos, Action<FluentDataFilter> scopedDataFilter)
{
if (scopedDataFilter != null)
{
SetRepositoryDataFilter(repos, null);
}
if (scopedDataFilter == null)
{
scopedDataFilter = _globalDataFilter;
}
if (scopedDataFilter == null) return;
using (var globalFilter = new FluentDataFilter())
{
scopedDataFilter(globalFilter);
var type = repos.GetType();
Type entityType = (repos as IBaseRepository).EntityType;
if (entityType == null) throw new Exception(DbContextStrings.FailedSetFilter_NotBelongIRpository);
var notExists = _dicSetRepositoryDataFilterConvertFilterNotExists.GetOrAdd(type, t => new ConcurrentDictionary<string, bool>());
var newFilter = new Dictionary<string, LambdaExpression>();
foreach (var gf in globalFilter._filters)
{
if (notExists.ContainsKey(gf.name)) continue;
LambdaExpression newExp = null;
var filterParameter1 = Expression.Parameter(entityType, gf.exp.Parameters[0].Name);
try
{
newExp = Expression.Lambda(
typeof(Func<,>).MakeGenericType(entityType, typeof(bool)),
new ReplaceVisitor().Modify(gf.exp.Body, filterParameter1),
filterParameter1
);
}
catch
{
notExists.TryAdd(gf.name, true); //防止第二次错误
continue;
}
newFilter.Add(gf.name, newExp);
}
if (newFilter.Any() == false) return;
var del = _dicSetRepositoryDataFilterApplyDataFilterFunc.GetOrAdd(type, t =>
{
var reposParameter = Expression.Parameter(type);
var nameParameter = Expression.Parameter(typeof(string));
var expressionParameter = Expression.Parameter(
typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(entityType, typeof(bool)))
);
return Expression.Lambda(
Expression.Block(
Expression.Call(reposParameter, type.GetMethod("ApplyDataFilter", BindingFlags.Instance | BindingFlags.NonPublic), nameParameter, expressionParameter)
),
new[] {
reposParameter, nameParameter, expressionParameter
}
).Compile();
});
foreach (var nf in newFilter)
{
del.DynamicInvoke(repos, nf.Key, nf.Value);
}
newFilter.Clear();
}
}
}
class ReplaceVisitor : ExpressionVisitor
{
private ParameterExpression parameter;
public Expression Modify(Expression expression, ParameterExpression parameter)
{
this.parameter = parameter;
return Visit(expression);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression?.NodeType == ExpressionType.Parameter)
return Expression.Property(parameter, node.Member.Name);
return base.VisitMember(node);
}
}
}

View File

@ -0,0 +1,43 @@
#if netcoreapp
using FreeSql;
using System;
using System.Linq;
using System.Reflection;
namespace Microsoft.Extensions.DependencyInjection
{
public static class FreeSqlRepositoryDependencyInjection
{
/// <summary>
/// 批量注入 Repository可以参考代码自行调整
/// </summary>
/// <param name="services"></param>
/// <param name="globalDataFilter"></param>
/// <param name="assemblies"></param>
/// <returns></returns>
public static IServiceCollection AddFreeRepository(this IServiceCollection services, Action<FluentDataFilter> globalDataFilter = null, params Assembly[] assemblies)
{
if (globalDataFilter != null)
{
DataFilterUtil._globalDataFilter = globalDataFilter;
//如果看到了这里的代码,想自己调整,但因为 _globalDataFilter 是内部属性,无法修改?
//请考虑改用 fsql.GlobalFilter.Apply
}
services.AddScoped(typeof(IBaseRepository<>), typeof(GuidRepository<>));
services.AddScoped(typeof(BaseRepository<>), typeof(GuidRepository<>));
services.AddScoped(typeof(IBaseRepository<,>), typeof(DefaultRepository<,>));
services.AddScoped(typeof(BaseRepository<,>), typeof(DefaultRepository<,>));
if (assemblies?.Any() == true)
foreach (var asse in assemblies)
foreach (var repo in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(IBaseRepository).IsAssignableFrom(a)))
services.AddScoped(repo);
return services;
}
}
}
#endif

View File

@ -0,0 +1,56 @@
using FreeSql;
using System;
using System.Linq;
using System.Linq.Expressions;
partial class FreeSqlDbContextExtensions
{
/// <summary>
/// 返回默认仓库类
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TKey"></typeparam>
/// <param name="that"></param>
/// <param name="filter">数据过滤 + 验证</param>
/// <returns></returns>
public static IBaseRepository<TEntity, TKey> GetRepository<TEntity, TKey>(this IFreeSql that, Expression<Func<TEntity, bool>> filter = null) where TEntity : class
{
return new DefaultRepository<TEntity, TKey>(that, filter);
}
/// <summary>
/// 返回默认仓库类,适用联合主键的仓储类
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="that"></param>
/// <param name="filter">数据过滤 + 验证</param>
/// <returns></returns>
public static IBaseRepository<TEntity> GetRepository<TEntity>(this IFreeSql that, Expression<Func<TEntity, bool>> filter = null) where TEntity : class
{
return new DefaultRepository<TEntity, int>(that, filter);
}
/// <summary>
/// 返回仓库类
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="that"></param>
/// <param name="filter">数据过滤 + 验证</param>
/// <param name="asTable">分表规则,参数:旧表名;返回:新表名 https://github.com/2881099/FreeSql/wiki/Repository</param>
/// <returns></returns>
public static IBaseRepository<TEntity, Guid> GetGuidRepository<TEntity>(this IFreeSql that, Expression<Func<TEntity, bool>> filter = null, Func<string, string> asTable = null) where TEntity : class
{
return new GuidRepository<TEntity>(that, filter, asTable);
}
/// <summary>
/// 创建基于仓储功能的工作单元,务必使用 using 包含使用
/// </summary>
/// <param name="that"></param>
/// <returns></returns>
public static IRepositoryUnitOfWork CreateUnitOfWork(this IFreeSql that)
{
return new RepositoryUnitOfWork(that);
}
}

View File

@ -0,0 +1,220 @@
using FreeSql.Extensions.EntityUtil;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql
{
public abstract partial class BaseRepository<TEntity> : IBaseRepository<TEntity>
where TEntity : class
{
internal RepositoryDbContext _dbPriv; //这个不能私有化,有地方需要反射获取它
internal RepositoryDbContext _db => _dbPriv ?? (_dbPriv = new RepositoryDbContext(OrmOriginal, this));
internal RepositoryDbSet<TEntity> _dbsetPriv;
internal RepositoryDbSet<TEntity> _dbset => _dbsetPriv ?? (_dbsetPriv = _db.Set<TEntity>() as RepositoryDbSet<TEntity>);
public IDataFilter<TEntity> DataFilter { get; } = new DataFilter<TEntity>();
internal Func<Type, string, string> _asTablePriv;
protected void ApplyDataFilter(string name, Expression<Func<TEntity, bool>> exp) => DataFilter.Apply(name, exp);
protected BaseRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter, Func<string, string> asTable = null)
{
_ormScoped = DbContextScopedFreeSql.Create(fsql, () => _db, () => UnitOfWork);
DataFilterUtil.SetRepositoryDataFilter(this, null);
DataFilter.Apply("", filter);
AsTable(asTable);
fsql?.GlobalFilter?.GetAllFilters().ForEach(gf =>
{
(DataFilter as DataFilter<TEntity>)._filtersByOrm.TryAdd(gf.Name, new DataFilter<TEntity>.FilterItemByOrm
{
Filter = gf,
IsEnabled = true
});
});
}
~BaseRepository() => this.Dispose();
int _disposeCounter;
public void Dispose()
{
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
try
{
_dbsetPriv?.Dispose();
_dbPriv?.Dispose();
this.DataFilter?.Dispose();
}
finally
{
GC.SuppressFinalize(this);
}
}
public Type EntityType => _dbsetPriv?.EntityType ?? typeof(TEntity);
public void AsType(Type entityType) => _dbset.AsType(entityType);
public void AsTable(Func<string, string> rule)
{
if (rule == null)
{
_asTablePriv = null;
return;
}
_asTablePriv = (a, b) => a == EntityType ? rule(b) : null;
}
public void AsTable(Func<Type, string, string> rule)
{
_asTablePriv = rule;
}
public DbContextOptions DbContextOptions { get => _db.Options; set => _db.Options = value; }
internal DbContextScopedFreeSql _ormScoped;
internal IFreeSql OrmOriginal => _ormScoped?._originalFsql;
public IFreeSql Orm => _ormScoped;
IUnitOfWork _unitOfWork;
public IUnitOfWork UnitOfWork
{
set
{
_unitOfWork = value;
if (_dbsetPriv != null) _dbsetPriv._uow = _unitOfWork; //防止 dbset 对象已经存在,再次设置 UnitOfWork 无法生效,所以作此判断重新设置
if (_dbPriv != null) _dbPriv.UnitOfWork = _unitOfWork;
}
get => _unitOfWork;
}
public IUpdate<TEntity> UpdateDiy => _dbset.OrmUpdateInternal(null);
public virtual ISelect<TEntity> Select => _dbset.OrmSelectInternal(null);
public ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => Select.Where(exp);
public ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => Select.WhereIf(condition, exp);
public virtual int Delete(Expression<Func<TEntity, bool>> predicate)
{
var delete = _dbset.OrmDeleteInternal(null).Where(predicate);
var sql = delete.ToSql();
var affrows = delete.ExecuteAffrows();
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { EntityType = EntityType, Object = sql, Type = DbContext.EntityChangeType.SqlRaw });
return affrows;
}
public virtual int Delete(TEntity entity)
{
_dbset.Remove(entity);
return _db.SaveChanges();
}
public virtual int Delete(IEnumerable<TEntity> entitys)
{
_dbset.RemoveRange(entitys);
return _db.SaveChanges();
}
public virtual List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate)
{
var list = _dbset.RemoveCascadeByDatabase(predicate);
var affrows = _db.SaveChanges();
return list;
}
public virtual TEntity Insert(TEntity entity)
{
_dbset.Add(entity);
_db.SaveChanges();
return entity;
}
public virtual List<TEntity> Insert(IEnumerable<TEntity> entitys)
{
_dbset.AddRange(entitys);
_db.SaveChanges();
return entitys.ToList();
}
public virtual int Update(TEntity entity)
{
_dbset.Update(entity);
return _db.SaveChanges();
}
public virtual int Update(IEnumerable<TEntity> entitys)
{
_dbset.UpdateRange(entitys);
return _db.SaveChanges();
}
public void Attach(TEntity data) => _dbset.Attach(data);
public void Attach(IEnumerable<TEntity> data) => _dbset.AttachRange(data);
public IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data)
{
_dbset.AttachOnlyPrimary(data);
return this;
}
public void FlushState() => _dbset.FlushState();
public Dictionary<string, object[]> CompareState(TEntity newdata) => _dbset.CompareState(newdata);
public virtual TEntity InsertOrUpdate(TEntity entity)
{
_dbset.AddOrUpdate(entity);
_db.SaveChanges();
return entity;
}
public virtual void SaveMany(TEntity entity, string propertyName)
{
_dbset.SaveMany(entity, propertyName);
_db.SaveChanges();
}
public virtual void BeginEdit(List<TEntity> data) => _dbset.BeginEdit(data);
public virtual int EndEdit(List<TEntity> data = null)
{
_db.FlushCommand();
if (UnitOfWork?.GetOrBeginTransaction(true) == null && _db.OrmOriginal.Ado.TransactionCurrentThread == null)
{
int affrows = 0;
IUnitOfWork olduow = UnitOfWork;
UnitOfWork = new UnitOfWork(_db.OrmOriginal);
try
{
affrows = _dbset.EndEdit(data);
UnitOfWork.Commit();
}
catch
{
UnitOfWork.Rollback();
throw;
}
finally
{
UnitOfWork.Dispose();
UnitOfWork = olduow;
}
_db.SaveChanges();
return affrows;
}
_db.SaveChanges();
return _dbset.EndEdit(data);
}
}
public abstract partial class BaseRepository<TEntity, TKey> : BaseRepository<TEntity>, IBaseRepository<TEntity, TKey>
where TEntity : class
{
public BaseRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter, Func<string, string> asTable = null) : base(fsql, filter, asTable) { }
TEntity CheckTKeyAndReturnIdEntity(TKey id)
{
var tb = _db.OrmOriginal.CodeFirst.GetTableByEntity(EntityType);
if (tb.Primarys.Length != 1) throw new Exception(DbContextStrings.EntityType_PrimaryKeyIsNotOne(EntityType.Name));
if (tb.Primarys[0].CsType.NullableTypeOrThis() != typeof(TKey).NullableTypeOrThis()) throw new Exception(DbContextStrings.EntityType_PrimaryKeyError(EntityType.Name, typeof(TKey).FullName));
var obj = tb.Type.CreateInstanceGetDefaultValue();
_db.OrmOriginal.SetEntityValueWithPropertyName(tb.Type, obj, tb.Primarys[0].CsName, id);
var ret = obj as TEntity;
if (ret == null) throw new Exception(DbContextStrings.EntityType_CannotConvert(EntityType.Name, typeof(TEntity).Name));
return ret;
}
public virtual int Delete(TKey id) => Delete(CheckTKeyAndReturnIdEntity(id));
public virtual TEntity Find(TKey id) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOne();
public virtual TEntity Get(TKey id) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOne();
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
#if net40
#else
namespace FreeSql
{
partial class BaseRepository<TEntity>
where TEntity : class
{
public virtual async Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
var delete = _dbset.OrmDeleteInternal(null).Where(predicate);
var sql = delete.ToSql();
var affrows = await delete.ExecuteAffrowsAsync(cancellationToken);
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { EntityType = EntityType, Object = sql, Type = DbContext.EntityChangeType.SqlRaw });
return affrows;
}
public virtual Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
{
_dbset.Remove(entity);
return _db.SaveChangesAsync(cancellationToken);
}
public virtual Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
{
_dbset.RemoveRange(entitys);
return _db.SaveChangesAsync(cancellationToken);
}
public virtual async Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
var list = await _dbset.RemoveCascadeByDatabaseAsync(predicate, cancellationToken);
var affrows = await _db.SaveChangesAsync(cancellationToken);
return list;
}
public virtual async Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
{
await _dbset.AddAsync(entity, cancellationToken);
await _db.SaveChangesAsync(cancellationToken);
return entity;
}
public virtual async Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
{
await _dbset.AddRangeAsync(entitys, cancellationToken);
await _db.SaveChangesAsync(cancellationToken);
return entitys.ToList();
}
public virtual Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
{
_dbset.Update(entity);
return _db.SaveChangesAsync(cancellationToken);
}
public virtual Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
{
_dbset.UpdateRange(entitys);
return _db.SaveChangesAsync(cancellationToken);
}
public virtual async Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
{
await _dbset.AddOrUpdateAsync(entity, cancellationToken);
await _db.SaveChangesAsync(cancellationToken);
return entity;
}
public virtual async Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default)
{
await _dbset.SaveManyAsync(entity, propertyName, cancellationToken);
await _db.SaveChangesAsync(cancellationToken);
}
}
partial class BaseRepository<TEntity, TKey>
{
public virtual Task<int> DeleteAsync(TKey id, CancellationToken cancellationToken = default) => DeleteAsync(CheckTKeyAndReturnIdEntity(id), cancellationToken);
public virtual Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOneAsync(cancellationToken);
public virtual Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOneAsync(cancellationToken);
}
}
#endif

View File

@ -0,0 +1,25 @@
using System;
using System.Linq.Expressions;
namespace FreeSql
{
public class DefaultRepository<TEntity, TKey> : BaseRepository<TEntity, TKey> where TEntity : class
{
public DefaultRepository(IFreeSql fsql) : base(fsql, null, null) { }
public DefaultRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter) : base(fsql, filter, null) { }
public DefaultRepository(IFreeSql fsql, UnitOfWorkManager uowManger) : base(uowManger?.Orm ?? fsql, null, null)
{
uowManger?.Binding(this);
}
}
public class GuidRepository<TEntity> : BaseRepository<TEntity, Guid> where TEntity : class
{
public GuidRepository(IFreeSql fsql) : this(fsql, null, null) { }
public GuidRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter, Func<string, string> asTable) : base(fsql, filter, asTable) { }
public GuidRepository(IFreeSql fsql, UnitOfWorkManager uowManger) : base(uowManger?.Orm ?? fsql, null, null)
{
uowManger?.Binding(this);
}
}
}

View File

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql
{
public interface IBaseRepository : IDisposable
{
Type EntityType { get; }
IUnitOfWork UnitOfWork { get; set; }
IFreeSql Orm { get; }
/// <summary>
/// 动态Type在使用 Repository&lt;object&gt; 后使用本方法,指定实体类型
/// </summary>
/// <param name="entityType"></param>
/// <returns></returns>
void AsType(Type entityType);
/// <summary>
/// 分表规则,参数:旧表名;返回:新表名 https://github.com/2881099/FreeSql/wiki/Repository
/// </summary>
/// <param name="rule"></param>
void AsTable(Func<string, string> rule);
/// <summary>
/// 分表规则,参数:实体类型、旧表名;返回:新表名 https://github.com/2881099/FreeSql/wiki/Repository
/// </summary>
/// <param name="rule"></param>
void AsTable(Func<Type, string, string> rule);
/// <summary>
/// 设置 DbContext 选项
/// </summary>
DbContextOptions DbContextOptions { get; set; }
}
public interface IBaseRepository<TEntity> : IBaseRepository
where TEntity : class
{
IDataFilter<TEntity> DataFilter { get; }
ISelect<TEntity> Select { get; }
ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp);
ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp);
TEntity Insert(TEntity entity);
List<TEntity> Insert(IEnumerable<TEntity> entitys);
/// <summary>
/// 清空状态数据
/// </summary>
void FlushState();
/// <summary>
/// 附加实体,可用于不查询就更新或删除
/// </summary>
/// <param name="entity"></param>
void Attach(TEntity entity);
void Attach(IEnumerable<TEntity> entity);
/// <summary>
/// 附加实体并且只附加主键值可用于不更新属性值为null或默认值的字段
/// </summary>
/// <param name="data"></param>
IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data);
/// <summary>
/// 比较实体,计算出值发生变化的属性,以及属性变化的前后值
/// </summary>
/// <param name="newdata">最新的实体对象,它将与附加实体的状态对比</param>
/// <returns>key: 属性名, value: [旧值, 新值]</returns>
Dictionary<string, object[]> CompareState(TEntity newdata);
int Update(TEntity entity);
int Update(IEnumerable<TEntity> entitys);
TEntity InsertOrUpdate(TEntity entity);
/// <summary>
/// 保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
/// 场景:在关闭级联保存功能之后,手工使用本方法<para></para>
/// 例子:保存商品的 OneToMany 集合属性SaveMany(goods, "Skus")<para></para>
/// 当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
/// 当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
/// </summary>
/// <param name="entity">实体对象</param>
/// <param name="propertyName">属性名</param>
void SaveMany(TEntity entity, string propertyName);
IUpdate<TEntity> UpdateDiy { get; }
int Delete(TEntity entity);
int Delete(IEnumerable<TEntity> entitys);
int Delete(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// 根据设置的 OneToOne/OneToMany/ManyToMany 导航属性,级联查询所有的数据库记录,删除并返回它们
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// 开始编辑数据,然后调用方法 EndEdit 分析出添加、修改、删除 SQL 语句进行执行<para></para>
/// 场景winform 加载表数据后,一顿添加、修改、删除操作之后,最后才点击【保存】<para></para><para></para>
/// 示例https://github.com/dotnetcore/FreeSql/issues/397<para></para>
/// 注意:* 本方法只支持单表操作,不支持导航属性级联保存
/// </summary>
/// <param name="data"></param>
void BeginEdit(List<TEntity> data);
/// <summary>
/// 完成编辑数据,进行保存动作<para></para>
/// 该方法根据 BeginEdit 传入的数据状态分析出添加、修改、删除 SQL 语句<para></para>
/// 注意:* 本方法只支持单表操作,不支持导航属性级联保存
/// </summary>
/// <param name="data">可选参数:手工传递最终的 data 值进行对比<para></para>默认:如果不传递,则使用 BeginEdit 传入的 data 引用进行对比</param>
/// <returns></returns>
int EndEdit(List<TEntity> data = null);
#if net40
#else
Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default);
Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default);
Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default);
Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default);
Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default);
Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
#endif
}
public interface IBaseRepository<TEntity, TKey> : IBaseRepository<TEntity>
where TEntity : class
{
TEntity Get(TKey id);
TEntity Find(TKey id);
int Delete(TKey id);
#if net40
#else
Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default);
Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(TKey id, CancellationToken cancellationToken = default);
#endif
}
}