- 增加 .First()/.FirstAsync() 指定字段查询的重载方法;

- 调整 FreeSql.Repository 直接引用 FreeSql.DbContext 内的仓储实现;
- 补充 单独针对 MySql 枚举类型的单元测试;
This commit is contained in:
28810 2019-04-11 17:34:21 +08:00
parent b5c79204d8
commit 4686d7e0af
19 changed files with 106 additions and 922 deletions

View File

@ -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();
}
}
}

View File

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

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

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

View File

@ -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>

View File

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

View File

@ -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) {
}
}
}

View File

@ -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;
}
}
}

View File

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

View File

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

View File

@ -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 {
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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() {

View File

@ -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() {

View File

@ -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>

View File

@ -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>

View File

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