mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-08-03 01:57:58 +08:00
- 增加 .First()/.FirstAsync() 指定字段查询的重载方法;
- 调整 FreeSql.Repository 直接引用 FreeSql.DbContext 内的仓储实现; - 补充 单独针对 MySql 枚举类型的单元测试;
This commit is contained in:
@ -1,153 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
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 ConcurrentDictionary<string, FilterItem> _filters = new ConcurrentDictionary<string, FilterItem>(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>();
|
||||
foreach (var name in filterName) {
|
||||
if (_filters.TryGetValue(name, out var tryfi)) {
|
||||
if (tryfi.IsEnabled) {
|
||||
restore.Add(name);
|
||||
tryfi.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new UsingAny(() => this.Enable(restore.ToArray()));
|
||||
}
|
||||
public IDisposable DisableAll() {
|
||||
List<string> restore = new List<string>();
|
||||
foreach (var val in _filters) {
|
||||
if (val.Value.IsEnabled) {
|
||||
restore.Add(val.Key);
|
||||
val.Value.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
return new UsingAny(() => this.Enable(restore.ToArray()));
|
||||
}
|
||||
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>();
|
||||
foreach (var name in filterName) {
|
||||
if (_filters.TryGetValue(name, out var tryfi)) {
|
||||
if (tryfi.IsEnabled == false) {
|
||||
restore.Add(name);
|
||||
tryfi.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new UsingAny(() => this.Disable(restore.ToArray()));
|
||||
}
|
||||
public IDisposable EnableAll() {
|
||||
List<string> restore = new List<string>();
|
||||
foreach (var val in _filters) {
|
||||
if (val.Value.IsEnabled == false) {
|
||||
restore.Add(val.Key);
|
||||
val.Value.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
return new UsingAny(() => this.Disable(restore.ToArray()));
|
||||
}
|
||||
|
||||
public bool IsEnabled(string filterName) {
|
||||
if (filterName == null) return false;
|
||||
return _filters.TryGetValue(filterName, out var tryfi) ? tryfi.IsEnabled : false;
|
||||
}
|
||||
|
||||
~DataFilter() {
|
||||
this.Dispose();
|
||||
}
|
||||
public void Dispose() {
|
||||
_filters.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public class FluentDataFilter : IDisposable {
|
||||
|
||||
internal List<(Type type, string name, LambdaExpression exp)> _filters = new List<(Type type, string name, LambdaExpression exp)>();
|
||||
|
||||
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((typeof(TEntity), filterName, filterAndValidateExp));
|
||||
return this;
|
||||
}
|
||||
|
||||
~FluentDataFilter() {
|
||||
this.Dispose();
|
||||
}
|
||||
public void Dispose() {
|
||||
_filters.Clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
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 IRepository).EntityType;
|
||||
if (entityType == null) throw new Exception("FreeSql.Repository 设置过滤器失败,原因是对象不属于 IRepository");
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
using Autofac;
|
||||
using FreeSql;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
|
||||
public static class FreeSqlRepositoryAutofacExtenssions {
|
||||
|
||||
/// <summary>
|
||||
/// 注册 FreeSql.Repository 包括 泛型、继承实现的仓储
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="globalDataFilter">全局过滤设置</param>
|
||||
/// <param name="assemblies">继承实现的仓储,所在的程序集</param>
|
||||
public static void RegisterFreeRepository(this ContainerBuilder builder, Action<FluentDataFilter> globalDataFilter = null, params Assembly[] assemblies) =>
|
||||
RegisterFreeRepositoryPrivate(builder, globalDataFilter, assemblies);
|
||||
|
||||
static void RegisterFreeRepositoryPrivate(ContainerBuilder builder, Action<FluentDataFilter> globalDataFilter, params Assembly[] assemblies) {
|
||||
|
||||
DataFilterUtil._globalDataFilter = globalDataFilter;
|
||||
|
||||
builder.RegisterGeneric(typeof(GuidRepository<>)).As(
|
||||
typeof(GuidRepository<>),
|
||||
typeof(BaseRepository<>),
|
||||
typeof(IBasicRepository<>),
|
||||
typeof(IReadOnlyRepository<>)
|
||||
).OnActivating(a => {
|
||||
//Utils.SetRepositoryDataFilter(a.Instance);
|
||||
}).InstancePerDependency();
|
||||
|
||||
builder.RegisterGeneric(typeof(DefaultRepository<,>)).As(
|
||||
typeof(DefaultRepository<,>),
|
||||
typeof(BaseRepository<,>),
|
||||
typeof(IBasicRepository<,>),
|
||||
typeof(IReadOnlyRepository<,>)
|
||||
).OnActivating(a => {
|
||||
//Utils.SetRepositoryDataFilter(a.Instance);
|
||||
}).InstancePerDependency();
|
||||
|
||||
builder.RegisterAssemblyTypes(assemblies).Where(a => {
|
||||
return typeof(IRepository).IsAssignableFrom(a);
|
||||
}).InstancePerDependency();
|
||||
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
using FreeSql;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace FreeSql {
|
||||
public static class FreeSqlRepositoryDependencyInjection {
|
||||
|
||||
public static IServiceCollection AddFreeRepository(this IServiceCollection services, Action<FluentDataFilter> globalDataFilter = null, params Assembly[] assemblies) {
|
||||
|
||||
DataFilterUtil._globalDataFilter = globalDataFilter;
|
||||
|
||||
services.AddScoped(typeof(IReadOnlyRepository<>), typeof(GuidRepository<>));
|
||||
services.AddScoped(typeof(IBasicRepository<>), typeof(GuidRepository<>));
|
||||
services.AddScoped(typeof(BaseRepository<>), typeof(GuidRepository<>));
|
||||
services.AddScoped(typeof(GuidRepository<>));
|
||||
|
||||
services.AddScoped(typeof(IReadOnlyRepository<,>), typeof(DefaultRepository<,>));
|
||||
services.AddScoped(typeof(IBasicRepository<,>), typeof(DefaultRepository<,>));
|
||||
services.AddScoped(typeof(BaseRepository<,>), typeof(DefaultRepository<,>));
|
||||
services.AddScoped(typeof(DefaultRepository<,>));
|
||||
|
||||
if (assemblies?.Any() == true) {
|
||||
foreach(var asse in assemblies) {
|
||||
foreach (var repos in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(IRepository).IsAssignableFrom(a))) {
|
||||
|
||||
services.AddScoped(repos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
using FreeSql;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Linq.Expressions;
|
||||
using System.Linq;
|
||||
using System.Data;
|
||||
|
||||
public static class FreeSqlRepositoryExtenssions {
|
||||
|
||||
/// <summary>
|
||||
/// 返回默认仓库类
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="filter">数据过滤 + 验证</param>
|
||||
/// <returns></returns>
|
||||
public static DefaultRepository<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>
|
||||
/// 返回仓库类,适用 Insert 方法无须返回插入的数据
|
||||
/// </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 GuidRepository<TEntity> 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>
|
||||
/// 合并两个仓储的设置(过滤+分表),以便查询
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="T2"></typeparam>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="repos"></param>
|
||||
/// <returns></returns>
|
||||
public static ISelect<TEntity> FromRepository<TEntity, T2>(this ISelect<TEntity> that, BaseRepository<T2> repos) where TEntity : class where T2 : class {
|
||||
var filters = (repos.DataFilter as DataFilter<T2>)._filters.Where(a => a.Value.IsEnabled == true);
|
||||
foreach (var filter in filters) that.Where<T2>(filter.Value.Expression);
|
||||
return that.AsTable(repos.AsTableSelectInternal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的工作单元,务必使用 using 包含使用
|
||||
/// </summary>
|
||||
/// <param name="that"></param>
|
||||
/// <returns></returns>
|
||||
public static IUnitOfWork CreateUnitOfWork(this IFreeSql that) {
|
||||
return new UnitOfWork(that);
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>0.4.10</Version>
|
||||
<Version>0.4.11</Version>
|
||||
<Authors>YeXiangQin</Authors>
|
||||
<Description>FreeSql Implementation of General Repository, Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite, and read/write separation、and split table.</Description>
|
||||
<PackageProjectUrl>https://github.com/2881099/FreeSql/wiki/Repository</PackageProjectUrl>
|
||||
@ -11,11 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\FreeSql\FreeSql.csproj" />
|
||||
<PackageReference Include="FreeSql.DbContext" Version="0.4.10" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,290 +0,0 @@
|
||||
using FreeSql.Extensions.EntityUtil;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql {
|
||||
public abstract class BaseRepository<TEntity> : IRepository<TEntity>
|
||||
where TEntity : class {
|
||||
|
||||
protected IFreeSql _fsql;
|
||||
internal UnitOfWork _unitOfWork;
|
||||
public IDataFilter<TEntity> DataFilter { get; } = new DataFilter<TEntity>();
|
||||
|
||||
Func<string, string> _asTableVal;
|
||||
protected Func<string, string> AsTable {
|
||||
get => _asTableVal;
|
||||
set {
|
||||
_asTableVal = value;
|
||||
AsTableSelect = value == null ? null : new Func<Type, string, string>((a, b) => a == EntityType ? value(b) : null);
|
||||
}
|
||||
}
|
||||
protected Func<Type, string, string> AsTableSelect { get; private set; }
|
||||
internal Func<Type, string, string> AsTableSelectInternal => AsTableSelect;
|
||||
|
||||
public Type EntityType { get; } = typeof(TEntity);
|
||||
|
||||
protected BaseRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter, Func<string, string> asTable = null) : base() {
|
||||
_fsql = fsql ?? throw new NullReferenceException(nameof(fsql));
|
||||
DataFilterUtil.SetRepositoryDataFilter(this, null);
|
||||
DataFilter.Apply("", filter);
|
||||
AsTable = asTable;
|
||||
}
|
||||
|
||||
public ISelect<TEntity> Select => OrmSelect(null);
|
||||
public IUpdate<TEntity> UpdateDiy => OrmUpdate(null);
|
||||
|
||||
public int Delete(Expression<Func<TEntity, bool>> predicate) => OrmDelete(null).Where(predicate).ExecuteAffrows();
|
||||
public int Delete(TEntity entity) => OrmDelete(entity).ExecuteAffrows();
|
||||
public Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate) => OrmDelete(null).Where(predicate).ExecuteAffrowsAsync();
|
||||
public Task<int> DeleteAsync(TEntity entity) => OrmDelete(entity).ExecuteAffrowsAsync();
|
||||
|
||||
public virtual TEntity Insert(TEntity entity) {
|
||||
Add(entity);
|
||||
return entity;
|
||||
}
|
||||
public virtual List<TEntity> Insert(IEnumerable<TEntity> entitys) {
|
||||
AddRange(entitys);
|
||||
return entitys.ToList();
|
||||
}
|
||||
async public virtual Task<TEntity> InsertAsync(TEntity entity) {
|
||||
await AddAsync(entity);
|
||||
return entity;
|
||||
}
|
||||
async public virtual Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys) {
|
||||
await AddRangeAsync(entitys);
|
||||
return entitys.ToList();
|
||||
}
|
||||
|
||||
public int Update(TEntity entity) => OrmUpdate(entity).ExecuteAffrows();
|
||||
public Task<int> UpdateAsync(TEntity entity) => OrmUpdate(entity).ExecuteAffrowsAsync();
|
||||
|
||||
protected ISelect<TEntity> OrmSelect(object dywhere) {
|
||||
var select = _fsql.Select<TEntity>(dywhere).WithTransaction(_unitOfWork?.GetOrBeginTransaction(false));
|
||||
var filters = (DataFilter as DataFilter<TEntity>)._filters.Where(a => a.Value.IsEnabled == true);
|
||||
foreach (var filter in filters) select.Where(filter.Value.Expression);
|
||||
return select.AsTable(AsTableSelect);
|
||||
}
|
||||
protected IUpdate<TEntity> OrmUpdate(object dywhere) {
|
||||
var entityObj = dywhere as TEntity;
|
||||
var update = _fsql.Update<TEntity>(dywhere).WithTransaction(_unitOfWork?.GetOrBeginTransaction());
|
||||
var filters = (DataFilter as DataFilter<TEntity>)._filters.Where(a => a.Value.IsEnabled == true);
|
||||
foreach (var filter in filters) {
|
||||
if (entityObj != null && filter.Value.ExpressionDelegate?.Invoke(entityObj) == false)
|
||||
throw new Exception($"FreeSql.Repository Update 失败,因为设置了 {filter.Key}: {filter.Value.Expression},更新的数据不符合");
|
||||
update.Where(filter.Value.Expression);
|
||||
}
|
||||
return update.AsTable(AsTable);
|
||||
}
|
||||
protected IDelete<TEntity> OrmDelete(object dywhere) {
|
||||
var delete = _fsql.Delete<TEntity>(dywhere).WithTransaction(_unitOfWork?.GetOrBeginTransaction());
|
||||
var filters = (DataFilter as DataFilter<TEntity>)._filters.Where(a => a.Value.IsEnabled == true);
|
||||
foreach (var filter in filters) delete.Where(filter.Value.Expression);
|
||||
return delete.AsTable(AsTable);
|
||||
}
|
||||
protected IInsert<TEntity> OrmInsert(TEntity entity) => OrmInsert(new[] { entity });
|
||||
protected IInsert<TEntity> OrmInsert(IEnumerable<TEntity> entitys) {
|
||||
var insert = _fsql.Insert<TEntity>(entitys).WithTransaction(_unitOfWork?.GetOrBeginTransaction());
|
||||
var filters = (DataFilter as DataFilter<TEntity>)._filters.Where(a => a.Value.IsEnabled == true);
|
||||
foreach (var filter in filters) {
|
||||
foreach (var entity in entitys)
|
||||
if (entity != null && filter.Value.ExpressionDelegate?.Invoke(entity) == false)
|
||||
throw new Exception($"FreeSql.Repository Insert 失败,因为设置了 {filter.Key}: {filter.Value.Expression},插入的数据不符合");
|
||||
}
|
||||
return insert.AsTable(AsTable);
|
||||
}
|
||||
|
||||
protected void ApplyDataFilter(string name, Expression<Func<TEntity, bool>> exp) => DataFilter.Apply(name, exp);
|
||||
|
||||
#region 参考 FreeSql.DbContext/dbset
|
||||
|
||||
TableInfo _tablePriv;
|
||||
TableInfo _table => _tablePriv ?? (_tablePriv = _fsql.CodeFirst.GetTableByEntity(EntityType));
|
||||
ColumnInfo[] _tableIdentitysPriv;
|
||||
ColumnInfo[] _tableIdentitys => _tableIdentitysPriv ?? (_tableIdentitysPriv = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray());
|
||||
|
||||
bool CanAdd(TEntity[] data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanAdd(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
bool CanAdd(IEnumerable<TEntity> data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
foreach (var s in data) if (CanAdd(s, isThrow) == false) return false;
|
||||
return true;
|
||||
}
|
||||
bool CanAdd(TEntity data, bool isThrow) {
|
||||
if (data == null) {
|
||||
if (isThrow) throw new ArgumentNullException(nameof(data));
|
||||
return false;
|
||||
}
|
||||
var key = _fsql.GetEntityKeyString(data);
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
return true;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
return true;
|
||||
}
|
||||
if (isThrow) throw new Exception($"不可添加,未设置主键的值:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
var idval = _fsql.GetEntityIdentityValueWithPrimary(data);
|
||||
if (idval > 0) {
|
||||
if (isThrow) throw new Exception($"不可添加,自增属性有值:{_fsql.GetEntityString(data)}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddPriv(TEntity data, bool isCheck) {
|
||||
if (isCheck && CanAdd(data, true) == false) return;
|
||||
if (_tableIdentitys.Length > 0) {
|
||||
//有自增,马上执行
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
var idtval = this.OrmInsert(data).ExecuteIdentity();
|
||||
_fsql.SetEntityIdentityValueWithPrimary(data, idtval);
|
||||
} else {
|
||||
var newval = this.OrmInsert(data).ExecuteInserted().First();
|
||||
_fsql.MapEntityValue(newval, data);
|
||||
}
|
||||
return;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
var idtval = this.OrmInsert(data).ExecuteIdentity();
|
||||
_fsql.SetEntityIdentityValueWithPrimary(data, idtval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.OrmInsert(data).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
public void Add(TEntity source) => AddPriv(source, true);
|
||||
public void AddRange(TEntity[] data) => AddRange(data.ToList());
|
||||
public void AddRange(IEnumerable<TEntity> data) {
|
||||
if (CanAdd(data, true) == false) return;
|
||||
if (data.ElementAtOrDefault(1) == default(TEntity)) {
|
||||
Add(data.First());
|
||||
return;
|
||||
}
|
||||
if (_tableIdentitys.Length > 0) {
|
||||
//有自增,马上执行
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
var rets = this.OrmInsert(data).ExecuteInserted();
|
||||
if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配");
|
||||
var idx = 0;
|
||||
foreach (var s in data)
|
||||
_fsql.MapEntityValue(rets[idx++], s);
|
||||
return;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
foreach (var s in data)
|
||||
AddPriv(s, false);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.OrmInsert(data).ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
|
||||
async Task AddPrivAsync(TEntity data, bool isCheck) {
|
||||
if (isCheck && CanAdd(data, true) == false) return;
|
||||
if (_tableIdentitys.Length > 0) {
|
||||
//有自增,马上执行
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
var idtval = await this.OrmInsert(data).ExecuteIdentityAsync();
|
||||
_fsql.SetEntityIdentityValueWithPrimary(data, idtval);
|
||||
} else {
|
||||
var newval = (await this.OrmInsert(data).ExecuteInsertedAsync()).First();
|
||||
_fsql.MapEntityValue(newval, data);
|
||||
}
|
||||
return;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) {
|
||||
var idtval = await this.OrmInsert(data).ExecuteIdentityAsync();
|
||||
_fsql.SetEntityIdentityValueWithPrimary(data, idtval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await this.OrmInsert(data).ExecuteAffrowsAsync();
|
||||
}
|
||||
}
|
||||
public Task AddAsync(TEntity source) => AddPrivAsync(source, true);
|
||||
public Task AddRangeAsync(TEntity[] data) => AddRangeAsync(data.ToList());
|
||||
async public Task AddRangeAsync(IEnumerable<TEntity> data) {
|
||||
if (CanAdd(data, true) == false) return;
|
||||
if (data.ElementAtOrDefault(1) == default(TEntity)) {
|
||||
Add(data.First());
|
||||
return;
|
||||
}
|
||||
if (_tableIdentitys.Length > 0) {
|
||||
//有自增,马上执行
|
||||
switch (_fsql.Ado.DataType) {
|
||||
case DataType.SqlServer:
|
||||
case DataType.PostgreSQL:
|
||||
var rets = await this.OrmInsert(data).ExecuteInsertedAsync();
|
||||
if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配");
|
||||
var idx = 0;
|
||||
foreach (var s in data)
|
||||
_fsql.MapEntityValue(rets[idx++], s);
|
||||
return;
|
||||
case DataType.MySql:
|
||||
case DataType.Oracle:
|
||||
case DataType.Sqlite:
|
||||
foreach (var s in data)
|
||||
await AddPrivAsync(s, false);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
await this.OrmInsert(data).ExecuteAffrowsAsync();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public abstract class BaseRepository<TEntity, TKey> : BaseRepository<TEntity>, IRepository<TEntity, TKey>
|
||||
where TEntity : class {
|
||||
|
||||
public BaseRepository(IFreeSql fsql, Expression<Func<TEntity, bool>> filter, Func<string, string> asTable = null) : base(fsql, filter, asTable) {
|
||||
}
|
||||
|
||||
public int Delete(TKey id) => OrmDelete(id).ExecuteAffrows();
|
||||
public Task<int> DeleteAsync(TKey id) => OrmDelete(id).ExecuteAffrowsAsync();
|
||||
|
||||
public TEntity Find(TKey id) => OrmSelect(id).ToOne();
|
||||
public Task<TEntity> FindAsync(TKey id) => OrmSelect(id).ToOneAsync();
|
||||
|
||||
public TEntity Get(TKey id) => Find(id);
|
||||
public Task<TEntity> GetAsync(TKey id) => FindAsync(id);
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql {
|
||||
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 override List<TEntity> Insert(IEnumerable<TEntity> entity) {
|
||||
OrmInsert(entity).ExecuteAffrows();
|
||||
return entity.ToList();
|
||||
}
|
||||
async public override Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entity) {
|
||||
await OrmInsert(entity).ExecuteAffrowsAsync();
|
||||
return entity.ToList();
|
||||
}
|
||||
|
||||
public override TEntity Insert(TEntity entity) {
|
||||
OrmInsert(entity).ExecuteAffrows();
|
||||
return entity;
|
||||
}
|
||||
async public override Task<TEntity> InsertAsync(TEntity entity) {
|
||||
await OrmInsert(entity).ExecuteAffrowsAsync();
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface IBasicRepository<TEntity> : IReadOnlyRepository<TEntity>
|
||||
where TEntity : class {
|
||||
TEntity Insert(TEntity entity);
|
||||
|
||||
List<TEntity> Insert(IEnumerable<TEntity> entitys);
|
||||
|
||||
Task<TEntity> InsertAsync(TEntity entity);
|
||||
|
||||
Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys);
|
||||
|
||||
int Update(TEntity entity);
|
||||
|
||||
Task<int> UpdateAsync(TEntity entity);
|
||||
|
||||
IUpdate<TEntity> UpdateDiy { get; }
|
||||
|
||||
int Delete(TEntity entity);
|
||||
|
||||
Task<int> DeleteAsync(TEntity entity);
|
||||
}
|
||||
|
||||
public interface IBasicRepository<TEntity, TKey> : IBasicRepository<TEntity>, IReadOnlyRepository<TEntity, TKey>
|
||||
where TEntity : class {
|
||||
int Delete(TKey id);
|
||||
|
||||
Task<int> DeleteAsync(TKey id);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface IReadOnlyRepository<TEntity> : IRepository
|
||||
where TEntity : class {
|
||||
|
||||
IDataFilter<TEntity> DataFilter { get; }
|
||||
|
||||
ISelect<TEntity> Select { get; }
|
||||
}
|
||||
|
||||
public interface IReadOnlyRepository<TEntity, TKey> : IReadOnlyRepository<TEntity>
|
||||
where TEntity : class {
|
||||
TEntity Get(TKey id);
|
||||
|
||||
Task<TEntity> GetAsync(TKey id);
|
||||
|
||||
TEntity Find(TKey id);
|
||||
|
||||
Task<TEntity> FindAsync(TKey id);
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FreeSql {
|
||||
|
||||
public interface IRepository {
|
||||
Type EntityType { get; }
|
||||
}
|
||||
|
||||
public interface IRepository<TEntity> : IReadOnlyRepository<TEntity>, IBasicRepository<TEntity>
|
||||
where TEntity : class {
|
||||
int Delete(Expression<Func<TEntity, bool>> predicate);
|
||||
|
||||
Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate);
|
||||
}
|
||||
|
||||
public interface IRepository<TEntity, TKey> : IRepository<TEntity>, IReadOnlyRepository<TEntity, TKey>, IBasicRepository<TEntity, TKey>
|
||||
where TEntity : class {
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql {
|
||||
public interface IUnitOfWork : IDisposable {
|
||||
|
||||
/// <summary>
|
||||
/// 在工作单元内创建默认仓库类,工作单元下的仓储操作具有事务特点
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <param name="filter">数据过滤 + 验证</param>
|
||||
/// <returns></returns>
|
||||
DefaultRepository<TEntity, TKey> GetRepository<TEntity, TKey>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class;
|
||||
|
||||
/// <summary>
|
||||
/// 在工作单元内创建仓库类,适用 Insert 方法无须返回插入的数据,工作单元下的仓储操作具有事务特点
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <param name="filter">数据过滤 + 验证</param>
|
||||
/// <param name="asTable">分表规则,参数:旧表名;返回:新表名 https://github.com/2881099/FreeSql/wiki/Repository</param>
|
||||
/// <returns></returns>
|
||||
GuidRepository<TEntity> GetGuidRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null, Func<string, string> asTable = null) where TEntity : class;
|
||||
|
||||
void Commit();
|
||||
|
||||
void Rollback();
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
using SafeObjectPool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql {
|
||||
class UnitOfWork : IUnitOfWork {
|
||||
|
||||
IFreeSql _fsql;
|
||||
Object<DbConnection> _conn;
|
||||
DbTransaction _tran;
|
||||
|
||||
public UnitOfWork(IFreeSql fsql) {
|
||||
_fsql = fsql;
|
||||
}
|
||||
|
||||
void ReturnObject() {
|
||||
_fsql.Ado.MasterPool.Return(_conn);
|
||||
_tran = null;
|
||||
_conn = null;
|
||||
}
|
||||
internal DbTransaction GetOrBeginTransaction(bool isCreate = true) {
|
||||
|
||||
if (_tran != null) return _tran;
|
||||
if (isCreate == false) return null;
|
||||
if (_conn != null) _fsql.Ado.MasterPool.Return(_conn);
|
||||
|
||||
_conn = _fsql.Ado.MasterPool.Get();
|
||||
try {
|
||||
_tran = _conn.Value.BeginTransaction();
|
||||
} catch {
|
||||
ReturnObject();
|
||||
throw;
|
||||
}
|
||||
return _tran;
|
||||
}
|
||||
|
||||
public void Commit() {
|
||||
if (_tran != null) {
|
||||
try {
|
||||
_tran.Commit();
|
||||
} finally {
|
||||
ReturnObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Rollback() {
|
||||
if (_tran != null) {
|
||||
try {
|
||||
_tran.Rollback();
|
||||
} finally {
|
||||
ReturnObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void Dispose() {
|
||||
this.Rollback();
|
||||
}
|
||||
|
||||
public DefaultRepository<TEntity, TKey> GetRepository<TEntity, TKey>(Expression<Func<TEntity, bool>> filter = null) where TEntity : class {
|
||||
var repos = new DefaultRepository<TEntity, TKey>(_fsql, filter);
|
||||
repos._unitOfWork = this;
|
||||
return repos;
|
||||
}
|
||||
public GuidRepository<TEntity> GetGuidRepository<TEntity>(Expression<Func<TEntity, bool>> filter = null, Func<string, string> asTable = null) where TEntity : class {
|
||||
var repos = new GuidRepository<TEntity>(_fsql, filter, asTable);
|
||||
repos._unitOfWork = this;
|
||||
return repos;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user