mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
- 增加 .First()/.FirstAsync() 指定字段查询的重载方法;
- 调整 FreeSql.Repository 直接引用 FreeSql.DbContext 内的仓储实现; - 补充 单独针对 MySql 枚举类型的单元测试;
This commit is contained in:
parent
b5c79204d8
commit
4686d7e0af
@ -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>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Version>0.4.10</Version>
|
<Version>0.4.11</Version>
|
||||||
<Authors>YeXiangQin</Authors>
|
<Authors>YeXiangQin</Authors>
|
||||||
<Description>FreeSql Implementation of General Repository, Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite, and read/write separation、and split table.</Description>
|
<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>
|
<PackageProjectUrl>https://github.com/2881099/FreeSql/wiki/Repository</PackageProjectUrl>
|
||||||
@ -11,11 +11,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.4.0" />
|
<PackageReference Include="FreeSql.DbContext" Version="0.4.10" />
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\FreeSql\FreeSql.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</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 string Title { get; set; }
|
||||||
public DateTime CreateTime { 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]
|
[Fact]
|
||||||
public void AppendData() {
|
public void AppendData() {
|
||||||
@ -35,6 +42,12 @@ namespace FreeSql.Tests.MySql {
|
|||||||
|
|
||||||
sql = insert.AppendData(items).IgnoreColumns(a => a.CreateTime).ToSql();
|
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);
|
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]
|
[Fact]
|
||||||
@ -66,6 +79,9 @@ namespace FreeSql.Tests.MySql {
|
|||||||
|
|
||||||
Assert.Equal(1, insert.AppendData(items.First()).ExecuteAffrows());
|
Assert.Equal(1, insert.AppendData(items.First()).ExecuteAffrows());
|
||||||
Assert.Equal(10, insert.AppendData(items).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]
|
[Fact]
|
||||||
public void ExecuteIdentity() {
|
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 });
|
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());
|
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]
|
[Fact]
|
||||||
public void ExecuteInserted() {
|
public void ExecuteInserted() {
|
||||||
|
@ -16,7 +16,14 @@ namespace FreeSql.Tests.MySql {
|
|||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public DateTime CreateTime { 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]
|
[Fact]
|
||||||
public void Dywhere() {
|
public void Dywhere() {
|
||||||
Assert.Null(g.mysql.Update<Topic>().ToSql());
|
Assert.Null(g.mysql.Update<Topic>().ToSql());
|
||||||
@ -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", "");
|
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);
|
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]
|
[Fact]
|
||||||
public void IgnoreColumns() {
|
public void IgnoreColumns() {
|
||||||
var sql = update.SetSource(new Topic { Id = 1, Title = "newtitle" }).IgnoreColumns(a => new { a.Clicks, a.CreateTime }).ToSql().Replace("\r\n", "");
|
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);
|
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]
|
[Fact]
|
||||||
public void Set() {
|
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", "");
|
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);
|
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]
|
[Fact]
|
||||||
public void SetRaw() {
|
public void SetRaw() {
|
||||||
var sql = update.Where(a => a.Id == 1).SetRaw("clicks = clicks + ?incrClick", new { incrClick = 1 }).ToSql().Replace("\r\n", "");
|
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);
|
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]
|
[Fact]
|
||||||
public void Where() {
|
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 });
|
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", "");
|
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);
|
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]
|
[Fact]
|
||||||
public void WhereExists() {
|
public void WhereExists() {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<Version>0.4.10</Version>
|
<Version>0.4.11</Version>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Authors>YeXiangQin</Authors>
|
<Authors>YeXiangQin</Authors>
|
||||||
<Description>FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite.</Description>
|
<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>
|
/// <returns></returns>
|
||||||
List<TReturn> ToList<TReturn>(Expression<Func<T1, TReturn>> select);
|
List<TReturn> ToList<TReturn>(Expression<Func<T1, TReturn>> select);
|
||||||
Task<List<TReturn>> ToListAsync<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>
|
/// <summary>
|
||||||
/// 返回即将执行的SQL语句
|
/// 返回即将执行的SQL语句
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -228,5 +228,13 @@ namespace FreeSql.Internal.CommonProvider {
|
|||||||
public bool Any(Expression<Func<T1, bool>> exp) => this.Where(exp).Any();
|
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 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user