mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 09:15:27 +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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -18,6 +18,13 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
			public string Title { get; set; }
 | 
			
		||||
			public DateTime CreateTime { get; set; }
 | 
			
		||||
		}
 | 
			
		||||
		class TestEnumInsertTb {
 | 
			
		||||
			[Column(IsIdentity = true)]
 | 
			
		||||
			public int id { get; set; }
 | 
			
		||||
			public TestEnumInserTbType type { get; set; }
 | 
			
		||||
			public DateTime time { get; set; } = new DateTime();
 | 
			
		||||
		}
 | 
			
		||||
		enum TestEnumInserTbType { str1, biggit, sum211 }
 | 
			
		||||
 
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void AppendData() {
 | 
			
		||||
@@ -35,6 +42,12 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
 | 
			
		||||
			sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql();
 | 
			
		||||
			Assert.Equal("INSERT INTO `tb_topic`(`Clicks`, `Title`) VALUES(?Clicks_0, ?Title_0), (?Clicks_1, ?Title_1), (?Clicks_2, ?Title_2), (?Clicks_3, ?Title_3), (?Clicks_4, ?Title_4), (?Clicks_5, ?Title_5), (?Clicks_6, ?Title_6), (?Clicks_7, ?Title_7), (?Clicks_8, ?Title_8), (?Clicks_9, ?Title_9)", sql);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Insert<TestEnumInsertTb>().AppendData(new TestEnumInsertTb { type = TestEnumInserTbType.sum211 }).ToSql();
 | 
			
		||||
			Assert.Equal("INSERT INTO `TestEnumInsertTb`(`type`, `time`) VALUES(?type_0, ?time_0)", sql);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Insert<TestEnumInsertTb>().AppendData(new TestEnumInsertTb { type = TestEnumInserTbType.sum211 }).NoneParameter().ToSql();
 | 
			
		||||
			Assert.Equal("INSERT INTO `TestEnumInsertTb`(`type`, `time`) VALUES('sum211', '0001-01-01 00:00:00')", sql);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
@@ -66,6 +79,9 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(1, insert.AppendData(items.First()).ExecuteAffrows());
 | 
			
		||||
			Assert.Equal(10, insert.AppendData(items).ExecuteAffrows());
 | 
			
		||||
 | 
			
		||||
			Assert.Equal(1, g.mysql.Insert<TestEnumInsertTb>().AppendData(new TestEnumInsertTb { type = TestEnumInserTbType.sum211 }).ExecuteAffrows());
 | 
			
		||||
			Assert.Equal(1, g.mysql.Insert<TestEnumInsertTb>().AppendData(new TestEnumInsertTb { type = TestEnumInserTbType.sum211 }).NoneParameter().ExecuteAffrows());
 | 
			
		||||
		}
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void ExecuteIdentity() {
 | 
			
		||||
@@ -73,6 +89,11 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
			for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
 | 
			
		||||
 | 
			
		||||
			Assert.NotEqual(0, insert.AppendData(items.First()).ExecuteIdentity());
 | 
			
		||||
 | 
			
		||||
			var id = g.mysql.Insert<TestEnumInsertTb>().AppendData(new TestEnumInsertTb { type = TestEnumInserTbType.sum211 }).ExecuteIdentity();
 | 
			
		||||
			Assert.Equal(TestEnumInserTbType.sum211, g.mysql.Select< TestEnumInsertTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
			id = g.mysql.Insert<TestEnumInsertTb>().AppendData(new TestEnumInsertTb { type = TestEnumInserTbType.sum211 }).NoneParameter().ExecuteIdentity();
 | 
			
		||||
			Assert.Equal(TestEnumInserTbType.sum211, g.mysql.Select<TestEnumInsertTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
		}
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void ExecuteInserted() {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,13 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
			public string Title { get; set; }
 | 
			
		||||
			public DateTime CreateTime { get; set; }
 | 
			
		||||
		}
 | 
			
		||||
		class TestEnumUpdateTb {
 | 
			
		||||
			[Column(IsIdentity = true)]
 | 
			
		||||
			public int id { get; set; }
 | 
			
		||||
			public TestEnumUpdateTbType type { get; set; }
 | 
			
		||||
			public DateTime time { get; set; } = new DateTime();
 | 
			
		||||
		}
 | 
			
		||||
		enum TestEnumUpdateTbType { str1, biggit, sum211 }
 | 
			
		||||
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void Dywhere() {
 | 
			
		||||
@@ -42,11 +49,39 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
 | 
			
		||||
			sql = update.SetSource(items).Set(a => a.CreateTime, new DateTime(2020,1,1)).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `tb_topic` SET `CreateTime` = ?p_0 WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))", sql);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Insert<TestEnumUpdateTb>().AppendData(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("INSERT INTO `TestEnumUpdateTb`(`type`, `time`) VALUES(?type_0, ?time_0)", sql);
 | 
			
		||||
			var id = g.mysql.Insert<TestEnumUpdateTb>().AppendData(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).ExecuteIdentity();
 | 
			
		||||
			Assert.True(id > 0);
 | 
			
		||||
			Assert.Equal(TestEnumUpdateTbType.sum211, g.mysql.Select<TestEnumUpdateTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().SetSource(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `TestEnumUpdateTb` SET `type` = ?p_0, `time` = ?p_1 WHERE (`id` = 0)", sql);
 | 
			
		||||
			g.mysql.Update<TestEnumUpdateTb>().SetSource(new TestEnumUpdateTb { id = (int)id, type = TestEnumUpdateTbType.biggit }).ExecuteAffrows();
 | 
			
		||||
			Assert.Equal(TestEnumUpdateTbType.biggit, g.mysql.Select<TestEnumUpdateTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Insert<TestEnumUpdateTb>().NoneParameter().AppendData(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("INSERT INTO `TestEnumUpdateTb`(`type`, `time`) VALUES('sum211', '0001-01-01 00:00:00')", sql);
 | 
			
		||||
			id = g.mysql.Insert<TestEnumUpdateTb>().NoneParameter().AppendData(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).ExecuteIdentity();
 | 
			
		||||
			Assert.True(id > 0);
 | 
			
		||||
			Assert.Equal(TestEnumUpdateTbType.sum211, g.mysql.Select<TestEnumUpdateTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().NoneParameter().SetSource(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `TestEnumUpdateTb` SET `type` = 'sum211', `time` = '0001-01-01 00:00:00' WHERE (`id` = 0)", sql);
 | 
			
		||||
			g.mysql.Update<TestEnumUpdateTb>().NoneParameter().SetSource(new TestEnumUpdateTb { id = (int)id, type = TestEnumUpdateTbType.biggit }).ExecuteAffrows();
 | 
			
		||||
			Assert.Equal(TestEnumUpdateTbType.biggit, g.mysql.Select<TestEnumUpdateTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
		}
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void IgnoreColumns() {
 | 
			
		||||
			var sql = update.SetSource(new Topic { Id = 1, Title = "newtitle" }).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `tb_topic` SET `Title` = ?p_0 WHERE (`Id` = 1)", sql);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().SetSource(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).IgnoreColumns(a => a.time).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `TestEnumUpdateTb` SET `type` = ?p_0 WHERE (`id` = 0)", sql);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().NoneParameter().SetSource(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).IgnoreColumns(a => a.time).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `TestEnumUpdateTb` SET `type` = 'sum211' WHERE (`id` = 0)", sql);
 | 
			
		||||
		}
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void Set() {
 | 
			
		||||
@@ -68,11 +103,26 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
 | 
			
		||||
			sql = update.Set(a => a.Id - incrv).Where(a => a.Id == 1).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `tb_topic` SET `Id` = `Id` - 10 WHERE (`Id` = 1)", sql);
 | 
			
		||||
 | 
			
		||||
			var id = g.mysql.Insert<TestEnumUpdateTb>().AppendData(new TestEnumUpdateTb { type = TestEnumUpdateTbType.sum211 }).ExecuteIdentity();
 | 
			
		||||
			Assert.True(id > 0);
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().Where(a => a.id == id).Set(a => a.type, TestEnumUpdateTbType.biggit).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal($"UPDATE `TestEnumUpdateTb` SET `type` = ?p_0 WHERE (`id` = {id})", sql);
 | 
			
		||||
			g.mysql.Update<TestEnumUpdateTb>().Where(a => a.id == id).Set(a => a.type, TestEnumUpdateTbType.biggit).ExecuteAffrows();
 | 
			
		||||
			Assert.Equal(TestEnumUpdateTbType.biggit, g.mysql.Select<TestEnumUpdateTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().NoneParameter().Where(a => a.id == id).Set(a => a.type, TestEnumUpdateTbType.str1).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal($"UPDATE `TestEnumUpdateTb` SET `type` = 'str1' WHERE (`id` = {id})", sql);
 | 
			
		||||
			g.mysql.Update<TestEnumUpdateTb>().NoneParameter().Where(a => a.id == id).Set(a => a.type, TestEnumUpdateTbType.str1).ExecuteAffrows();
 | 
			
		||||
			Assert.Equal(TestEnumUpdateTbType.str1, g.mysql.Select<TestEnumUpdateTb>().Where(a => a.id == id).First()?.type);
 | 
			
		||||
		}
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void SetRaw() {
 | 
			
		||||
			var sql = update.Where(a => a.Id == 1).SetRaw("clicks = clicks + ?incrClick", new { incrClick = 1 }).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `tb_topic` SET clicks = clicks + ?incrClick WHERE (`Id` = 1)", sql);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().NoneParameter().Where(a => a.id == 0).SetRaw("`type` = {0}".FormatMySql(TestEnumUpdateTbType.sum211)).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `TestEnumUpdateTb` SET `type` = 'sum211' WHERE (`id` = 0)", sql);
 | 
			
		||||
		}
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void Where() {
 | 
			
		||||
@@ -90,6 +140,10 @@ namespace FreeSql.Tests.MySql {
 | 
			
		||||
			for (var a = 0; a < 10; a++) items.Add(new Topic { Id = a + 1, Title = $"newtitle{a}", Clicks = a * 100 });
 | 
			
		||||
			sql = update.Where(items).SetRaw("title='newtitle'").ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `tb_topic` SET title='newtitle' WHERE (`Id` IN (1,2,3,4,5,6,7,8,9,10))", sql);
 | 
			
		||||
 | 
			
		||||
			sql = g.mysql.Update<TestEnumUpdateTb>().NoneParameter().Where(a => a.id == 0 && a.type == TestEnumUpdateTbType.str1)
 | 
			
		||||
				.Set(a => a.type, TestEnumUpdateTbType.sum211).ToSql().Replace("\r\n", "");
 | 
			
		||||
			Assert.Equal("UPDATE `TestEnumUpdateTb` SET `type` = 'sum211' WHERE (`id` = 0 AND `type` = 'str1')", sql);
 | 
			
		||||
		}
 | 
			
		||||
		[Fact]
 | 
			
		||||
		public void WhereExists() {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFramework>netstandard2.0</TargetFramework>
 | 
			
		||||
		<Version>0.4.10</Version>
 | 
			
		||||
		<Version>0.4.11</Version>
 | 
			
		||||
		<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
			
		||||
		<Authors>YeXiangQin</Authors>
 | 
			
		||||
		<Description>FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite.</Description>
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,25 @@ namespace FreeSql {
 | 
			
		||||
		/// <returns></returns>
 | 
			
		||||
		List<TReturn> ToList<TReturn>(Expression<Func<T1, TReturn>> select);
 | 
			
		||||
		Task<List<TReturn>> ToListAsync<TReturn>(Expression<Func<T1, TReturn>> select);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// 执行SQL查询,返回指定字段的记录的第一条记录,记录不存在时返回 TReturn 默认值
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <typeparam name="TReturn">返回类型</typeparam>
 | 
			
		||||
		/// <param name="select">选择列</param>
 | 
			
		||||
		/// <returns></returns>
 | 
			
		||||
		TReturn ToOne<TReturn>(Expression<Func<T1, TReturn>> select);
 | 
			
		||||
		Task<TReturn> ToOneAsync<TReturn>(Expression<Func<T1, TReturn>> select);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// 执行SQL查询,返回指定字段的记录的第一条记录,记录不存在时返回 TReturn 默认值
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		/// <typeparam name="TReturn">返回类型</typeparam>
 | 
			
		||||
		/// <param name="select">选择列</param>
 | 
			
		||||
		/// <returns></returns>
 | 
			
		||||
		TReturn First<TReturn>(Expression<Func<T1, TReturn>> select);
 | 
			
		||||
		Task<TReturn> FirstAsync<TReturn>(Expression<Func<T1, TReturn>> select);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// 返回即将执行的SQL语句
 | 
			
		||||
		/// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -228,5 +228,13 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
		public bool Any(Expression<Func<T1, bool>> exp) => this.Where(exp).Any();
 | 
			
		||||
 | 
			
		||||
		public Task<bool> AnyAsync(Expression<Func<T1, bool>> exp) => this.Where(exp).AnyAsync();
 | 
			
		||||
 | 
			
		||||
		public TReturn ToOne<TReturn>(Expression<Func<T1, TReturn>> select) => this.Limit(1).ToList(select).FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
		async public Task<TReturn> ToOneAsync<TReturn>(Expression<Func<T1, TReturn>> select) => (await this.Limit(1).ToListAsync(select)).FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
		public TReturn First<TReturn>(Expression<Func<T1, TReturn>> select) => this.ToOne(select);
 | 
			
		||||
 | 
			
		||||
		public Task<TReturn> FirstAsync<TReturn>(Expression<Func<T1, TReturn>> select) => this.ToOneAsync(select);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user