mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-08-02 17:57:31 +08:00
initial commit
This commit is contained in:
179
Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs
Normal file
179
Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs
Normal file
@ -0,0 +1,179 @@
|
||||
#if NET40
|
||||
using FreeSql.DataAnnotations;
|
||||
using System;
|
||||
|
||||
#else
|
||||
using FreeSql.DataAnnotations;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
#endif
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace FreeSql
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity base class, including CreateTime/UpdateTime/IsDeleted, the CRUD methods, and ID primary key definition.
|
||||
/// <para></para>
|
||||
/// 包括 CreateTime/UpdateTime/IsDeleted、CRUD 方法、以及 ID 主键定义 的实体基类
|
||||
/// <para></para>
|
||||
/// When TKey is int/long, the Id is set to be an auto-incremented primary key
|
||||
/// <para></para>
|
||||
/// 当 TKey 为 int/long 时,Id 主键被设为自增值主键
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
[Table(DisableSyncStructure = true)]
|
||||
public abstract class BaseEntity<TEntity, TKey> : BaseEntity<TEntity> where TEntity : class
|
||||
{
|
||||
static BaseEntity()
|
||||
{
|
||||
var keyType = typeof(TKey).NullableTypeOrThis();
|
||||
if (keyType == typeof(int) || keyType == typeof(long))
|
||||
ConfigEntity(typeof(TEntity), t => t.Property("Id").IsIdentity(true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary key <br />
|
||||
/// 主键
|
||||
/// </summary>
|
||||
[Column(Position = 1)]
|
||||
public virtual TKey Id { get; set; }
|
||||
|
||||
#if !NET40
|
||||
/// <summary>
|
||||
/// Get data based on the value of the primary key <br />
|
||||
/// 根据主键值获取数据
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<TEntity> FindAsync(TKey id)
|
||||
{
|
||||
var item = await Select.WhereDynamic(id).FirstAsync();
|
||||
(item as BaseEntity<TEntity>)?.Attach();
|
||||
return item;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get data based on the value of the primary key <br />
|
||||
/// 根据主键值获取数据
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public static TEntity Find(TKey id)
|
||||
{
|
||||
var item = Select.WhereDynamic(id).First();
|
||||
(item as BaseEntity<TEntity>)?.Attach();
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entity base class, including CreateTime/UpdateTime/IsDeleted, and sync/async CRUD methods.
|
||||
/// <para></para>
|
||||
/// 包括 CreateTime/UpdateTime/IsDeleted、以及 CRUD 异步和同步方法的实体基类
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
[Table(DisableSyncStructure = true)]
|
||||
public abstract class BaseEntity<TEntity> : BaseEntityAsync<TEntity> where TEntity : class
|
||||
{
|
||||
bool UpdateIsDeleted(bool value)
|
||||
{
|
||||
if (Repository == null)
|
||||
return Orm.Update<TEntity>(this as TEntity)
|
||||
.WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction())
|
||||
.Set(a => (a as BaseEntity).IsDeleted, IsDeleted = value)
|
||||
.ExecuteAffrows() == 1;
|
||||
|
||||
IsDeleted = value;
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.Update(this as TEntity) == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To delete data <br />
|
||||
/// 删除数据
|
||||
/// </summary>
|
||||
/// <param name="physicalDelete">To flag whether to delete the physical level of the data</param>
|
||||
/// <returns></returns>
|
||||
public virtual bool Delete(bool physicalDelete = false)
|
||||
{
|
||||
if (physicalDelete == false)
|
||||
return UpdateIsDeleted(true);
|
||||
|
||||
if (Repository == null)
|
||||
return Orm.Delete<TEntity>(this as TEntity)
|
||||
.WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction())
|
||||
.ExecuteAffrows() == 1;
|
||||
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.Delete(this as TEntity) == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To recover deleted data <br />
|
||||
/// 恢复删除的数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool Restore() => UpdateIsDeleted(false);
|
||||
|
||||
/// <summary>
|
||||
/// To update data <br />
|
||||
/// 更新数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool Update()
|
||||
{
|
||||
UpdateTime = DateTime.Now;
|
||||
if (Repository == null)
|
||||
return Orm.Update<TEntity>()
|
||||
.WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction())
|
||||
.SetSource(this as TEntity)
|
||||
.ExecuteAffrows() == 1;
|
||||
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.Update(this as TEntity) == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To insert data <br />
|
||||
/// 插入数据
|
||||
/// </summary>
|
||||
public virtual TEntity Insert()
|
||||
{
|
||||
CreateTime = DateTime.Now;
|
||||
if (Repository == null)
|
||||
Repository = Orm.GetRepository<TEntity>();
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.Insert(this as TEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To insert or update data <br />
|
||||
/// 更新或插入
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual TEntity Save()
|
||||
{
|
||||
UpdateTime = DateTime.Now;
|
||||
if (Repository == null)
|
||||
Repository = Orm.GetRepository<TEntity>();
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.InsertOrUpdate(this as TEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To completely save the navigation properties of the entity in the form of sub-tables. <br />
|
||||
/// 【完整】保存导航属性,子表
|
||||
/// </summary>
|
||||
/// <param name="navigatePropertyName">Navigation property name</param>
|
||||
public virtual void SaveMany(string navigatePropertyName)
|
||||
{
|
||||
if (Repository == null)
|
||||
Repository = Orm.GetRepository<TEntity>();
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
Repository.SaveMany(this as TEntity, navigatePropertyName);
|
||||
}
|
||||
}
|
||||
}
|
167
Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs
Normal file
167
Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs
Normal file
@ -0,0 +1,167 @@
|
||||
#if NET40
|
||||
using FreeSql.DataAnnotations;
|
||||
|
||||
#else
|
||||
using FreeSql.DataAnnotations;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
#endif
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace FreeSql
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity base class, including CreateTime/UpdateTime/IsDeleted, the async CRUD methods, and ID primary key definition.
|
||||
/// <para></para>
|
||||
/// 包括 CreateTime/UpdateTime/IsDeleted、CRUD 异步方法、以及 ID 主键定义 的实体基类
|
||||
/// <para></para>
|
||||
/// When TKey is int/long, the Id is set to be an auto-incremented primary key
|
||||
/// <para></para>
|
||||
/// 当 TKey 为 int/long 时,Id 主键被设为自增值主键
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
[Table(DisableSyncStructure = true)]
|
||||
public abstract class BaseEntityAsync<TEntity, TKey> : BaseEntityAsync<TEntity> where TEntity : class
|
||||
{
|
||||
static BaseEntityAsync()
|
||||
{
|
||||
var keyType = typeof(TKey).NullableTypeOrThis();
|
||||
if (keyType == typeof(int) || keyType == typeof(long))
|
||||
ConfigEntity(typeof(TEntity), t => t.Property("Id").IsIdentity(true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary key <br />
|
||||
/// 主键
|
||||
/// </summary>
|
||||
[Column(Position = 1)]
|
||||
public virtual TKey Id { get; set; }
|
||||
|
||||
#if !NET40
|
||||
/// <summary>
|
||||
/// Get data based on the value of the primary key <br />
|
||||
/// 根据主键值获取数据
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<TEntity> FindAsync(TKey id)
|
||||
{
|
||||
var item = await Select.WhereDynamic(id).FirstAsync();
|
||||
(item as BaseEntity<TEntity>)?.Attach();
|
||||
return item;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entity base class, including CreateTime/UpdateTime/IsDeleted, and async CRUD methods.
|
||||
/// <para></para>
|
||||
/// 包括 CreateTime/UpdateTime/IsDeleted、以及 CRUD 异步方法的实体基类
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
[Table(DisableSyncStructure = true)]
|
||||
public abstract class BaseEntityAsync<TEntity> : BaseEntityReadOnly<TEntity> where TEntity : class
|
||||
{
|
||||
#if !NET40
|
||||
async Task<bool> UpdateIsDeletedAsync(bool value)
|
||||
{
|
||||
if (Repository == null)
|
||||
return await Orm.Update<TEntity>(this as TEntity)
|
||||
.WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction())
|
||||
.Set(a => (a as BaseEntity).IsDeleted, IsDeleted = value)
|
||||
.ExecuteAffrowsAsync() == 1;
|
||||
|
||||
IsDeleted = value;
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return await Repository.UpdateAsync(this as TEntity) == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To delete data <br />
|
||||
/// 删除数据
|
||||
/// </summary>
|
||||
/// <param name="physicalDelete">To flag whether to delete the physical level of the data</param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<bool> DeleteAsync(bool physicalDelete = false)
|
||||
{
|
||||
if (physicalDelete == false)
|
||||
return await UpdateIsDeletedAsync(true);
|
||||
|
||||
if (Repository == null)
|
||||
return await Orm.Delete<TEntity>(this as TEntity)
|
||||
.WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction())
|
||||
.ExecuteAffrowsAsync() == 1;
|
||||
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return await Repository.DeleteAsync(this as TEntity) == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To recover deleted data <br />
|
||||
/// 恢复删除的数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual Task<bool> RestoreAsync() => UpdateIsDeletedAsync(false);
|
||||
|
||||
/// <summary>
|
||||
/// To update data <br />
|
||||
/// 更新数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<bool> UpdateAsync()
|
||||
{
|
||||
UpdateTime = DateTime.Now;
|
||||
if (Repository == null)
|
||||
return await Orm.Update<TEntity>()
|
||||
.WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction())
|
||||
.SetSource(this as TEntity)
|
||||
.ExecuteAffrowsAsync() == 1;
|
||||
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return await Repository.UpdateAsync(this as TEntity) == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To insert data <br />
|
||||
/// 插入数据
|
||||
/// </summary>
|
||||
public virtual Task<TEntity> InsertAsync()
|
||||
{
|
||||
CreateTime = DateTime.Now;
|
||||
if (Repository == null)
|
||||
Repository = Orm.GetRepository<TEntity>();
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.InsertAsync(this as TEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To insert or update data <br />
|
||||
/// 更新或插入
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual Task<TEntity> SaveAsync()
|
||||
{
|
||||
UpdateTime = DateTime.Now;
|
||||
if (Repository == null)
|
||||
Repository = Orm.GetRepository<TEntity>();
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.InsertOrUpdateAsync(this as TEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To completely save the navigation properties of the entity in the form of sub-tables. <br />
|
||||
/// 【完整】保存导航属性,子表
|
||||
/// </summary>
|
||||
/// <param name="navigatePropertyName">Navigation property name</param>
|
||||
public virtual Task SaveManyAsync(string navigatePropertyName)
|
||||
{
|
||||
if (Repository == null)
|
||||
Repository = Orm.GetRepository<TEntity>();
|
||||
Repository.UnitOfWork = _resolveUow?.Invoke();
|
||||
return Repository.SaveManyAsync(this as TEntity, navigatePropertyName);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
215
Extensions/FreeSql.Extensions.BaseEntity/BaseEntityReadOnly.cs
Normal file
215
Extensions/FreeSql.Extensions.BaseEntity/BaseEntityReadOnly.cs
Normal file
@ -0,0 +1,215 @@
|
||||
using FreeSql.DataAnnotations;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
|
||||
// ReSharper disable CheckNamespace
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable InconsistentlySynchronizedField
|
||||
namespace FreeSql
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity base class, including CreateTime/UpdateTime/IsDeleted.
|
||||
/// <para></para>
|
||||
/// 包括 CreateTime/UpdateTime/IsDeleted 的实体基类
|
||||
/// </summary>
|
||||
[Table(DisableSyncStructure = true)]
|
||||
public abstract class BaseEntity
|
||||
{
|
||||
static Func<IFreeSql> _resoleOrm;
|
||||
internal static Func<IUnitOfWork> _resolveUow;
|
||||
|
||||
public static IFreeSql Orm => _resoleOrm?.Invoke() ?? throw new Exception(CoreStrings.S_BaseEntity_Initialization_Error);
|
||||
|
||||
public static void Initialization(IFreeSql fsql, Func<IUnitOfWork> resolveUow) => Initialization(() => fsql, resolveUow);
|
||||
public static void Initialization(Func<IFreeSql> resoleOrm, Func<IUnitOfWork> resolveUow)
|
||||
{
|
||||
_resoleOrm = resoleOrm;
|
||||
_resolveUow = resolveUow;
|
||||
|
||||
if (_configEntityQueues.Any())
|
||||
{
|
||||
lock (_configEntityLock)
|
||||
{
|
||||
while (_configEntityQueues.TryDequeue(out var cei))
|
||||
Orm.CodeFirst.ConfigEntity(cei.EntityType, cei.Fluent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigEntityInfo
|
||||
{
|
||||
public Type EntityType;
|
||||
public Action<TableFluent> Fluent;
|
||||
}
|
||||
|
||||
static readonly ConcurrentQueue<ConfigEntityInfo> _configEntityQueues = new ConcurrentQueue<ConfigEntityInfo>();
|
||||
static readonly object _configEntityLock = new object();
|
||||
|
||||
internal static void ConfigEntity(Type entityType, Action<TableFluent> fluent)
|
||||
{
|
||||
lock (_configEntityLock)
|
||||
{
|
||||
if (_resoleOrm?.Invoke() == null)
|
||||
_configEntityQueues.Enqueue(new ConfigEntityInfo { EntityType = entityType, Fluent = fluent });
|
||||
else
|
||||
Orm.CodeFirst.ConfigEntity(entityType, fluent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Created time <br />
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
[Column(Position = -4)]
|
||||
public virtual DateTime CreateTime { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// Updated time <br />
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[Column(Position = -3)]
|
||||
public virtual DateTime UpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Logical Delete <br />
|
||||
/// 逻辑删除
|
||||
/// </summary>
|
||||
[Column(Position = -2)]
|
||||
public virtual bool IsDeleted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sort <br />
|
||||
/// 排序
|
||||
/// </summary>
|
||||
[Column(Position = -1)]
|
||||
public virtual int Sort { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A readonly entity base class, including CreateTime/UpdateTime/IsDeleted.
|
||||
/// <para></para>
|
||||
/// 包括 CreateTime/UpdateTime/IsDeleted 的只读实体基类
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
[Table(DisableSyncStructure = true)]
|
||||
public abstract class BaseEntityReadOnly<TEntity> : BaseEntity where TEntity : class
|
||||
{
|
||||
/// <summary>
|
||||
/// To query data <br />
|
||||
/// 查询数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static ISelect<TEntity> Select
|
||||
{
|
||||
get
|
||||
{
|
||||
var select = Orm.Select<TEntity>()
|
||||
.TrackToList(TrackToList) //自动为每个元素 Attach
|
||||
.WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction(false));
|
||||
return select.WhereCascade(a => (a as BaseEntity).IsDeleted == false);
|
||||
}
|
||||
}
|
||||
|
||||
static void TrackToList(object list)
|
||||
{
|
||||
if (list == null) return;
|
||||
|
||||
var ls = list as IList<TEntity>;
|
||||
if (ls == null)
|
||||
{
|
||||
var ie = list as IEnumerable;
|
||||
if (ie == null) return;
|
||||
|
||||
var isFirst = true;
|
||||
IBaseRepository<TEntity> baseRepo = null;
|
||||
|
||||
foreach (var item in ie)
|
||||
{
|
||||
if (item == null) return;
|
||||
|
||||
if (isFirst)
|
||||
{
|
||||
isFirst = false;
|
||||
var itemType = item.GetType();
|
||||
if (itemType == typeof(object)) return;
|
||||
if (itemType.FullName.Contains("FreeSqlLazyEntity__")) itemType = itemType.BaseType;
|
||||
if (Orm.CodeFirst.GetTableByEntity(itemType)?.Primarys.Any() != true) return;
|
||||
if (itemType.GetConstructor(Type.EmptyTypes) == null) return;
|
||||
if (item is BaseEntity<TEntity> == false) return;
|
||||
}
|
||||
|
||||
if (item is BaseEntity<TEntity> entity)
|
||||
{
|
||||
if (baseRepo == null) baseRepo = Orm.GetRepository<TEntity>();
|
||||
entity.Repository = baseRepo;
|
||||
entity.Attach();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ls.Any() == false) return;
|
||||
if (ls.FirstOrDefault() is BaseEntity<TEntity> == false) return;
|
||||
if (Orm.CodeFirst.GetTableByEntity(typeof(TEntity))?.Primarys.Any() != true) return;
|
||||
|
||||
IBaseRepository<TEntity> repo = null;
|
||||
foreach (var item in ls)
|
||||
{
|
||||
if (item is BaseEntity<TEntity> entity)
|
||||
{
|
||||
if (repo == null) repo = Orm.GetRepository<TEntity>();
|
||||
entity.Repository = repo;
|
||||
entity.Attach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query conditions <br />
|
||||
/// 查询条件,Where(a => a.Id> 10)
|
||||
/// <para></para>
|
||||
/// Support navigation object query <br />
|
||||
/// 支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com")
|
||||
/// </summary>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
public static ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => Select.Where(exp);
|
||||
|
||||
/// <summary>
|
||||
/// Query conditions <br />
|
||||
/// 查询条件,Where(true, a => a.Id > 10)
|
||||
/// <para></para>
|
||||
/// Support navigation object query <br />
|
||||
/// 支导航对象查询,Where(true, a => a.Author.Email == "2881099@qq.com")
|
||||
/// </summary>
|
||||
/// <param name="condition">true 时生效</param>
|
||||
/// <param name="exp">lambda表达式</param>
|
||||
/// <returns></returns>
|
||||
public static ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => Select.WhereIf(condition, exp);
|
||||
|
||||
/// <summary>
|
||||
/// Repository object. <br />
|
||||
/// 仓储对象
|
||||
/// </summary>
|
||||
protected IBaseRepository<TEntity> Repository { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// To Attach entities. When updating data, only the changed part is updated. <br />
|
||||
/// 附加实体。在更新数据时,只更新变化的部分
|
||||
/// </summary>
|
||||
public TEntity Attach()
|
||||
{
|
||||
if (Repository == null)
|
||||
Repository = Orm.GetRepository<TEntity>();
|
||||
var item = this as TEntity;
|
||||
Repository.Attach(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net45;net40</TargetFrameworks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>FreeSql;ncc;YeXiangQin</Authors>
|
||||
<Description>BaseEntity 是一种极简单的 CodeFirst 开发方式,特别对单表或多表CRUD,利用继承节省了每个实体类的重复属性(创建时间、ID等字段),软件删除等功能,进行 crud 操作时不必时常考虑仓储的使用.</Description>
|
||||
<PackageProjectUrl>https://github.com/dotnetcore/FreeSql/tree/master/Extensions/FreeSql.Extensions.BaseEntity</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/dotnetcore/FreeSql/tree/master/Extensions/FreeSql.Extensions.BaseEntity</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageTags>FreeSql;ORM;BaseEntity</PackageTags>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Version>3.2.833</Version>
|
||||
<PackageReadmeFile>readme.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../../readme.md" Pack="true" PackagePath="\"/>
|
||||
<None Include="../../logo.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
|
||||
<DocumentationFile>FreeSql.Extensions.BaseEntity.xml</DocumentationFile>
|
||||
<WarningLevel>3</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net40'">
|
||||
<DefineConstants>net40</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
@ -0,0 +1,251 @@
|
||||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>FreeSql.Extensions.BaseEntity</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:FreeSql.BaseEntity`2">
|
||||
<summary>
|
||||
Entity base class, including CreateTime/UpdateTime/IsDeleted, the CRUD methods, and ID primary key definition.
|
||||
<para></para>
|
||||
包括 CreateTime/UpdateTime/IsDeleted、CRUD 方法、以及 ID 主键定义 的实体基类
|
||||
<para></para>
|
||||
When TKey is int/long, the Id is set to be an auto-incremented primary key
|
||||
<para></para>
|
||||
当 TKey 为 int/long 时,Id 主键被设为自增值主键
|
||||
</summary>
|
||||
<typeparam name="TEntity"></typeparam>
|
||||
<typeparam name="TKey"></typeparam>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntity`2.Id">
|
||||
<summary>
|
||||
Primary key <br />
|
||||
主键
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`2.FindAsync(`1)">
|
||||
<summary>
|
||||
Get data based on the value of the primary key <br />
|
||||
根据主键值获取数据
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`2.Find(`1)">
|
||||
<summary>
|
||||
Get data based on the value of the primary key <br />
|
||||
根据主键值获取数据
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:FreeSql.BaseEntity`1">
|
||||
<summary>
|
||||
Entity base class, including CreateTime/UpdateTime/IsDeleted, and sync/async CRUD methods.
|
||||
<para></para>
|
||||
包括 CreateTime/UpdateTime/IsDeleted、以及 CRUD 异步和同步方法的实体基类
|
||||
</summary>
|
||||
<typeparam name="TEntity"></typeparam>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`1.Delete(System.Boolean)">
|
||||
<summary>
|
||||
To delete data <br />
|
||||
删除数据
|
||||
</summary>
|
||||
<param name="physicalDelete">To flag whether to delete the physical level of the data</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`1.Restore">
|
||||
<summary>
|
||||
To recover deleted data <br />
|
||||
恢复删除的数据
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`1.Update">
|
||||
<summary>
|
||||
To update data <br />
|
||||
更新数据
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`1.Insert">
|
||||
<summary>
|
||||
To insert data <br />
|
||||
插入数据
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`1.Save">
|
||||
<summary>
|
||||
To insert or update data <br />
|
||||
更新或插入
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntity`1.SaveMany(System.String)">
|
||||
<summary>
|
||||
To completely save the navigation properties of the entity in the form of sub-tables. <br />
|
||||
【完整】保存导航属性,子表
|
||||
</summary>
|
||||
<param name="navigatePropertyName">Navigation property name</param>
|
||||
</member>
|
||||
<member name="T:FreeSql.BaseEntityAsync`2">
|
||||
<summary>
|
||||
Entity base class, including CreateTime/UpdateTime/IsDeleted, the async CRUD methods, and ID primary key definition.
|
||||
<para></para>
|
||||
包括 CreateTime/UpdateTime/IsDeleted、CRUD 异步方法、以及 ID 主键定义 的实体基类
|
||||
<para></para>
|
||||
When TKey is int/long, the Id is set to be an auto-incremented primary key
|
||||
<para></para>
|
||||
当 TKey 为 int/long 时,Id 主键被设为自增值主键
|
||||
</summary>
|
||||
<typeparam name="TEntity"></typeparam>
|
||||
<typeparam name="TKey"></typeparam>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntityAsync`2.Id">
|
||||
<summary>
|
||||
Primary key <br />
|
||||
主键
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityAsync`2.FindAsync(`1)">
|
||||
<summary>
|
||||
Get data based on the value of the primary key <br />
|
||||
根据主键值获取数据
|
||||
</summary>
|
||||
<param name="id"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:FreeSql.BaseEntityAsync`1">
|
||||
<summary>
|
||||
Entity base class, including CreateTime/UpdateTime/IsDeleted, and async CRUD methods.
|
||||
<para></para>
|
||||
包括 CreateTime/UpdateTime/IsDeleted、以及 CRUD 异步方法的实体基类
|
||||
</summary>
|
||||
<typeparam name="TEntity"></typeparam>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityAsync`1.DeleteAsync(System.Boolean)">
|
||||
<summary>
|
||||
To delete data <br />
|
||||
删除数据
|
||||
</summary>
|
||||
<param name="physicalDelete">To flag whether to delete the physical level of the data</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityAsync`1.RestoreAsync">
|
||||
<summary>
|
||||
To recover deleted data <br />
|
||||
恢复删除的数据
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityAsync`1.UpdateAsync">
|
||||
<summary>
|
||||
To update data <br />
|
||||
更新数据
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityAsync`1.InsertAsync">
|
||||
<summary>
|
||||
To insert data <br />
|
||||
插入数据
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityAsync`1.SaveAsync">
|
||||
<summary>
|
||||
To insert or update data <br />
|
||||
更新或插入
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityAsync`1.SaveManyAsync(System.String)">
|
||||
<summary>
|
||||
To completely save the navigation properties of the entity in the form of sub-tables. <br />
|
||||
【完整】保存导航属性,子表
|
||||
</summary>
|
||||
<param name="navigatePropertyName">Navigation property name</param>
|
||||
</member>
|
||||
<member name="T:FreeSql.BaseEntity">
|
||||
<summary>
|
||||
Entity base class, including CreateTime/UpdateTime/IsDeleted.
|
||||
<para></para>
|
||||
包括 CreateTime/UpdateTime/IsDeleted 的实体基类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntity.CreateTime">
|
||||
<summary>
|
||||
Created time <br />
|
||||
创建时间
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntity.UpdateTime">
|
||||
<summary>
|
||||
Updated time <br />
|
||||
更新时间
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntity.IsDeleted">
|
||||
<summary>
|
||||
Logical Delete <br />
|
||||
逻辑删除
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntity.Sort">
|
||||
<summary>
|
||||
Sort <br />
|
||||
排序
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:FreeSql.BaseEntityReadOnly`1">
|
||||
<summary>
|
||||
A readonly entity base class, including CreateTime/UpdateTime/IsDeleted.
|
||||
<para></para>
|
||||
包括 CreateTime/UpdateTime/IsDeleted 的只读实体基类
|
||||
</summary>
|
||||
<typeparam name="TEntity"></typeparam>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntityReadOnly`1.Select">
|
||||
<summary>
|
||||
To query data <br />
|
||||
查询数据
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityReadOnly`1.Where(System.Linq.Expressions.Expression{System.Func{`0,System.Boolean}})">
|
||||
<summary>
|
||||
Query conditions <br />
|
||||
查询条件,Where(a => a.Id> 10)
|
||||
<para></para>
|
||||
Support navigation object query <br />
|
||||
支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com")
|
||||
</summary>
|
||||
<param name="exp">lambda表达式</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityReadOnly`1.WhereIf(System.Boolean,System.Linq.Expressions.Expression{System.Func{`0,System.Boolean}})">
|
||||
<summary>
|
||||
Query conditions <br />
|
||||
查询条件,Where(true, a => a.Id > 10)
|
||||
<para></para>
|
||||
Support navigation object query <br />
|
||||
支导航对象查询,Where(true, a => a.Author.Email == "2881099@qq.com")
|
||||
</summary>
|
||||
<param name="condition">true 时生效</param>
|
||||
<param name="exp">lambda表达式</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="P:FreeSql.BaseEntityReadOnly`1.Repository">
|
||||
<summary>
|
||||
Repository object. <br />
|
||||
仓储对象
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FreeSql.BaseEntityReadOnly`1.Attach">
|
||||
<summary>
|
||||
To Attach entities. When updating data, only the changed part is updated. <br />
|
||||
附加实体。在更新数据时,只更新变化的部分
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
133
Extensions/FreeSql.Extensions.BaseEntity/README.MD
Normal file
133
Extensions/FreeSql.Extensions.BaseEntity/README.MD
Normal file
@ -0,0 +1,133 @@
|
||||
[中文](README.zh-CN.MD) | **English**
|
||||
|
||||
# Preface
|
||||
|
||||
I have tried ADO.NET, Dapper, EF, and Repository storage, and even wrote a generator tool myself to do common CRUD operations.
|
||||
|
||||
Their operation is inconvenient:
|
||||
|
||||
- Need to declare before use;
|
||||
|
||||
- Each entity class corresponds to an operation class (or DAL, DbContext, Repository).
|
||||
|
||||
BaseEntity is a very simple way of CodeFirst development, especially for single-table or multi-table CRUD operations. BaseEntity uses "inheritance" to save the repetitive code (creation time, ID and other fields) and functions of each entity class, and at the same time, it is not necessary to consider the use of repository when performing CURD operations.
|
||||
|
||||
|
||||
This article will introduce a very simple CRUD operation method of BaseEntity.
|
||||
|
||||
# Features
|
||||
|
||||
- Automatically migrate the entity structure (CodeFirst) to the database;
|
||||
|
||||
- Directly perform CRUD operations on entities;
|
||||
|
||||
- Simplify user-defined entity types, eliminating hard-coded primary keys, common fields and their configuration (such as CreateTime, UpdateTime);
|
||||
|
||||
- Logic delete of single-table and multi-table query;
|
||||
|
||||
# Declaring
|
||||
|
||||
> dotnet add package FreeSql.Extensions.BaseEntity
|
||||
|
||||
> dotnet add package FreeSql.Provider.Sqlite
|
||||
|
||||
```csharp
|
||||
BaseEntity.Initialization(fsql, null);
|
||||
```
|
||||
|
||||
1. Define an auto-increment primary key of type `int`. When the `TKey` of `BaseEntity` is specified as `int/long`, the primary key will be considered as auto-increment;
|
||||
|
||||
```csharp
|
||||
public class UserGroup : BaseEntity<UserGroup, int>
|
||||
{
|
||||
public string GroupName { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
If you don't want the primary key to be an auto-increment key, you can override the attribute:
|
||||
|
||||
```csharp
|
||||
public class UserGroup : BaseEntity<UserGroup, int>
|
||||
{
|
||||
[Column(IsIdentity = false)]
|
||||
public override int Id { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
}
|
||||
```
|
||||
> For more information about the attributes of entities, please refer to: https://github.com/dotnetcore/FreeSql/wiki/Entity-Attributes
|
||||
|
||||
2. Define an entity whose primary key is Guid type, when saving data, it will automatically generate ordered and non-repeated Guid values (you don't need to specify `Guid.NewGuid()` yourself);
|
||||
|
||||
```csharp
|
||||
public class User : BaseEntity<UserGroup, Guid>
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
# Usage of CRUD
|
||||
|
||||
```csharp
|
||||
//Insert Data
|
||||
var item = new UserGroup { GroupName = "Group One" };
|
||||
item.Insert();
|
||||
|
||||
//Update Data
|
||||
item.GroupName = "Group Two";
|
||||
item.Update();
|
||||
|
||||
//Insert or Update Data
|
||||
item.Save();
|
||||
|
||||
//Logic Delete
|
||||
item.Delete();
|
||||
|
||||
//Recover Logic Delete
|
||||
item.Restore();
|
||||
|
||||
//Get the object by the primary key
|
||||
var item = UserGroup.Find(1);
|
||||
|
||||
//Query Data
|
||||
var items = UserGroup.Where(a => a.Id > 10).ToList();
|
||||
```
|
||||
|
||||
`{ENTITY_TYPE}.Select` returns a query object, the same as `FreeSql.ISelect`.
|
||||
|
||||
In the multi-table query, the logic delete condition will be attached to the query of each table.
|
||||
|
||||
> For more information about query data, please refer to: https://github.com/2881099/FreeSql/wiki/Query-Data
|
||||
|
||||
# Transaction Suggestion
|
||||
|
||||
Because the `AsyncLocal` platform is not compatible, the transaction is managed by the outside.
|
||||
|
||||
```csharp
|
||||
static AsyncLocal<IUnitOfWork> _asyncUow = new AsyncLocal<IUnitOfWork>();
|
||||
|
||||
BaseEntity.Initialization(fsql, () => _asyncUow.Value);
|
||||
```
|
||||
|
||||
At the beginning of `Scoped`: `_asyncUow.Value = fsql.CreateUnitOfWork();` (You can also use the `UnitOfWorkManager` object to get uow)
|
||||
|
||||
At the end of `Scoped`: `_asyncUow.Value = null;`
|
||||
|
||||
as follows:
|
||||
|
||||
```csharp
|
||||
using (var uow = fsql.CreateUnitOfWork())
|
||||
{
|
||||
_asyncUow.Value = uow;
|
||||
|
||||
try
|
||||
{
|
||||
//todo ... BaseEntity internal CURD method keeps using uow transaction
|
||||
}
|
||||
finally
|
||||
{
|
||||
_asyncUow.Value = null;
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
```
|
143
Extensions/FreeSql.Extensions.BaseEntity/README.zh-CN.MD
Normal file
143
Extensions/FreeSql.Extensions.BaseEntity/README.zh-CN.MD
Normal file
@ -0,0 +1,143 @@
|
||||
**中文** | [English](README.MD)
|
||||
|
||||
# 前言
|
||||
|
||||
尝试过 ado.net、dapper、ef,以及Repository仓储,甚至自己还写过生成器工具,以便做常规CRUD操作。
|
||||
|
||||
它们日常操作不方便之处:
|
||||
|
||||
- 每次使用前需要声明,再操作;
|
||||
|
||||
- 很多人一个实体类,对应一个操作类(或DAL、DbContext、Repository);
|
||||
|
||||
BaseEntity 是一种极简单的 CodeFirst 开发方式,特别对单表或多表CRUD,利用继承节省了每个实体类的重复属性(创建时间、ID等字段),软件删除等功能,进行 crud 操作时不必时常考虑仓储的使用;
|
||||
|
||||
本文介绍 BaseEntity 一种极简约的 CRUD 操作方法。
|
||||
|
||||
# 功能特点
|
||||
|
||||
- 自动迁移实体结构(CodeFirst),到数据库;
|
||||
|
||||
- 直接操作实体的方法,进行 CRUD 操作;
|
||||
|
||||
- 简化用户定义实体类型,省去主键、常用字段的配置(如CreateTime、UpdateTime);
|
||||
|
||||
- 实现单表、多表查询的软删除逻辑;
|
||||
|
||||
# 声明
|
||||
|
||||
> dotnet add package FreeSql.Extensions.BaseEntity
|
||||
|
||||
> dotnet add package FreeSql.Provider.Sqlite
|
||||
|
||||
```csharp
|
||||
BaseEntity.Initialization(fsql, null);
|
||||
```
|
||||
|
||||
1、定义一个主键 int 并且自增的实体类型,BaseEntity TKey 指定为 int/long 时,会认为主键是自增;
|
||||
|
||||
```csharp
|
||||
public class UserGroup : BaseEntity<UserGroup, int>
|
||||
{
|
||||
public string GroupName { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
如果不想主键是自增键,可以重写属性:
|
||||
|
||||
```csharp
|
||||
public class UserGroup : BaseEntity<UserGroup, int>
|
||||
{
|
||||
[Column(IsIdentity = false)]
|
||||
public override int Id { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
}
|
||||
```
|
||||
> 有关更多实体的特性配置,请参考资料:https://github.com/dotnetcore/FreeSql/wiki/%e5%ae%9e%e4%bd%93%e7%89%b9%e6%80%a7
|
||||
|
||||
2、定义一个主键 Guid 的实体类型,保存数据时会自动产生有序不重复的 Guid 值(不用自己指定 Guid.NewGuid());
|
||||
|
||||
```csharp
|
||||
public class User : BaseEntity<UserGroup, Guid>
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
# CRUD 使用
|
||||
|
||||
```csharp
|
||||
//添加
|
||||
var item = new UserGroup { GroupName = "组一" };
|
||||
item.Insert();
|
||||
|
||||
//更新
|
||||
item.GroupName = "组二";
|
||||
item.Update();
|
||||
|
||||
//添加或更新
|
||||
item.Save();
|
||||
|
||||
//软删除
|
||||
item.Delete();
|
||||
|
||||
//恢复软删除
|
||||
item.Restore();
|
||||
|
||||
//根据主键获取对象
|
||||
var item = UserGroup.Find(1);
|
||||
|
||||
//查询数据
|
||||
var items = UserGroup.Where(a => a.Id > 10).ToList();
|
||||
```
|
||||
|
||||
实体类型.Select 是一个查询对象,使用方法和 FreeSql.ISelect 一样;
|
||||
|
||||
支持多表查询时,软删除条件会附加在每个表中;
|
||||
|
||||
> 有关更多查询方法,请参考资料:https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2
|
||||
|
||||
# 事务建议
|
||||
|
||||
1、同线程事务,不支持异步:
|
||||
|
||||
```c#
|
||||
fsql.Transaction(() =>
|
||||
{
|
||||
//todo ...
|
||||
})
|
||||
```;
|
||||
|
||||
2、如果你是异步控
|
||||
|
||||
由于 AsyncLocal 平台兼容不好,所以交给外部管理事务。
|
||||
|
||||
```csharp
|
||||
static AsyncLocal<IUnitOfWork> _asyncUow = new AsyncLocal<IUnitOfWork>();
|
||||
|
||||
BaseEntity.Initialization(fsql, () => _asyncUow.Value);
|
||||
```
|
||||
|
||||
在 Scoped 开始时:_asyncUow.Value = fsql.CreateUnitOfWork(); (也可以使用 UnitOfWorkManager 对象获取 uow)
|
||||
|
||||
在 Scoped 结束时:_asyncUow.Value = null;
|
||||
|
||||
如下:
|
||||
|
||||
```csharp
|
||||
using (var uow = fsql.CreateUnitOfWork())
|
||||
{
|
||||
_asyncUow.Value = uow;
|
||||
|
||||
try
|
||||
{
|
||||
//todo ... BaseEntity 内部 curd 方法保持使用 uow 事务
|
||||
}
|
||||
finally
|
||||
{
|
||||
_asyncUow.Value = null;
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
```
|
BIN
Extensions/FreeSql.Extensions.BaseEntity/key.snk
Normal file
BIN
Extensions/FreeSql.Extensions.BaseEntity/key.snk
Normal file
Binary file not shown.
Reference in New Issue
Block a user