diff --git a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs index 40382f84..8986e5b5 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs +++ b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs @@ -1,17 +1,24 @@ -using FreeSql; +#if NET40 +using FreeSql.DataAnnotations; +using System; + +#else using FreeSql.DataAnnotations; using System; -using System.Data; -using System.Diagnostics; -using System.Linq.Expressions; -using System.Threading; using System.Threading.Tasks; +#endif + +// ReSharper disable once CheckNamespace namespace FreeSql { /// + /// Entity base class, including CreateTime/UpdateTime/IsDeleted, the CRUD methods, and ID primary key definition. + /// /// 包括 CreateTime/UpdateTime/IsDeleted、CRUD 方法、以及 ID 主键定义 的实体基类 /// + /// When TKey is int/long, the Id is set to be an auto-incremented primary key + /// /// 当 TKey 为 int/long 时,Id 主键被设为自增值主键 /// /// @@ -21,25 +28,26 @@ namespace FreeSql { static BaseEntity() { - var tkeyType = typeof(TKey)?.NullableTypeOrThis(); - if (tkeyType == typeof(int) || tkeyType == typeof(long)) - BaseEntity.ConfigEntity(typeof(TEntity), t => t.Property("Id").IsIdentity(true)); + var keyType = typeof(TKey).NullableTypeOrThis(); + if (keyType == typeof(int) || keyType == typeof(long)) + ConfigEntity(typeof(TEntity), t => t.Property("Id").IsIdentity(true)); } /// + /// Primary key
/// 主键 ///
[Column(Position = 1)] public virtual TKey Id { get; set; } -#if net40 -#else +#if !NET40 /// + /// Get data based on the value of the primary key
/// 根据主键值获取数据 ///
/// /// - async public static Task FindAsync(TKey id) + public static async Task FindAsync(TKey id) { var item = await Select.WhereDynamic(id).FirstAsync(); (item as BaseEntity)?.Attach(); @@ -48,6 +56,7 @@ namespace FreeSql #endif /// + /// Get data based on the value of the primary key
/// 根据主键值获取数据 ///
/// @@ -61,6 +70,8 @@ namespace FreeSql } /// + /// Entity base class, including CreateTime/UpdateTime/IsDeleted, and sync/async CRUD methods. + /// /// 包括 CreateTime/UpdateTime/IsDeleted、以及 CRUD 异步和同步方法的实体基类 /// /// @@ -69,88 +80,97 @@ namespace FreeSql { bool UpdateIsDeleted(bool value) { - if (this.Repository == null) + if (Repository is null) + { return Orm.Update(this as TEntity) - .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) - .Set(a => (a as BaseEntity).IsDeleted, this.IsDeleted = value).ExecuteAffrows() == 1; + .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) + .Set(a => (a as BaseEntity).IsDeleted, IsDeleted = value).ExecuteAffrows() == 1; + } - this.IsDeleted = value; - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.Update(this as TEntity) == 1; + IsDeleted = value; + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.Update(this as TEntity) == 1; } + /// + /// To delete data
/// 删除数据 ///
- /// 是否物理删除 + /// To flag whether to delete the physical level of the data /// public virtual bool Delete(bool physicalDelete = false) { - if (physicalDelete == false) return this.UpdateIsDeleted(true); - if (this.Repository == null) + if (physicalDelete == false) + return UpdateIsDeleted(true); + + if (Repository is null) return Orm.Delete(this as TEntity).ExecuteAffrows() == 1; - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.Delete(this as TEntity) == 1; + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.Delete(this as TEntity) == 1; } + /// + /// To recover deleted data
/// 恢复删除的数据 ///
/// - public virtual bool Restore() => this.UpdateIsDeleted(false); + public virtual bool Restore() => UpdateIsDeleted(false); /// + /// To update data
/// 更新数据 ///
/// public virtual bool Update() { - this.UpdateTime = DateTime.Now; - if (this.Repository == null) + UpdateTime = DateTime.Now; + if (Repository is null) + { return Orm.Update() - .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) - .SetSource(this as TEntity).ExecuteAffrows() == 1; + .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) + .SetSource(this as TEntity).ExecuteAffrows() == 1; + } - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.Update(this as TEntity) == 1; + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.Update(this as TEntity) == 1; } + /// + /// To insert data
/// 插入数据 ///
public virtual TEntity Insert() { - this.CreateTime = DateTime.Now; - if (this.Repository == null) - this.Repository = Orm.GetRepository(); - - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.Insert(this as TEntity); + CreateTime = DateTime.Now; + Repository ??= Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.Insert(this as TEntity); } /// + /// To insert or update data
/// 更新或插入 ///
/// public virtual TEntity Save() { - this.UpdateTime = DateTime.Now; - if (this.Repository == null) - this.Repository = Orm.GetRepository(); - - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.InsertOrUpdate(this as TEntity); + UpdateTime = DateTime.Now; + Repository ??= Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.InsertOrUpdate(this as TEntity); } /// + /// To completely save the navigation properties of the entity in the form of sub-tables.
/// 【完整】保存导航属性,子表 ///
- /// 导航属性名 + /// Navigation property name public virtual void SaveMany(string navigatePropertyName) { - if (this.Repository == null) - this.Repository = Orm.GetRepository(); - - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - this.Repository.SaveMany(this as TEntity, navigatePropertyName); + Repository ??= Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + Repository.SaveMany(this as TEntity, navigatePropertyName); } } } \ No newline at end of file diff --git a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs index ca589470..58f1300e 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs +++ b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs @@ -1,14 +1,23 @@ - -using FreeSql; +#if NET40 +using FreeSql.DataAnnotations; + +#else using FreeSql.DataAnnotations; using System; using System.Threading.Tasks; +#endif + +// ReSharper disable once CheckNamespace namespace FreeSql { /// + /// Entity base class, including CreateTime/UpdateTime/IsDeleted, the async CRUD methods, and ID primary key definition. + /// /// 包括 CreateTime/UpdateTime/IsDeleted、CRUD 异步方法、以及 ID 主键定义 的实体基类 /// + /// When TKey is int/long, the Id is set to be an auto-incremented primary key + /// /// 当 TKey 为 int/long 时,Id 主键被设为自增值主键 /// /// @@ -18,128 +27,138 @@ namespace FreeSql { static BaseEntityAsync() { - var tkeyType = typeof(TKey)?.NullableTypeOrThis(); - if (tkeyType == typeof(int) || tkeyType == typeof(long)) - BaseEntity.ConfigEntity(typeof(TEntity), t => t.Property("Id").IsIdentity(true)); + var keyType = typeof(TKey).NullableTypeOrThis(); + if (keyType == typeof(int) || keyType == typeof(long)) + ConfigEntity(typeof(TEntity), t => t.Property("Id").IsIdentity(true)); } /// + /// Primary key
/// 主键 ///
- [Column(Position = 1)] + [Column(Position = 1)] public virtual TKey Id { get; set; } -#if net40 -#else +#if !NET40 /// + /// Get data based on the value of the primary key
/// 根据主键值获取数据 ///
/// /// - async public static Task FindAsync(TKey id) + public static async Task FindAsync(TKey id) { var item = await Select.WhereDynamic(id).FirstAsync(); (item as BaseEntity)?.Attach(); return item; } #endif - } /// + /// Entity base class, including CreateTime/UpdateTime/IsDeleted, and async CRUD methods. + /// /// 包括 CreateTime/UpdateTime/IsDeleted、以及 CRUD 异步方法的实体基类 /// /// [Table(DisableSyncStructure = true)] public abstract class BaseEntityAsync : BaseEntityReadOnly where TEntity : class { -#if net40 -#else +#if !NET40 async Task UpdateIsDeletedAsync(bool value) { - if (this.Repository == null) + if (Repository is null) + { return await Orm.Update(this as TEntity) - .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) - .Set(a => (a as BaseEntity).IsDeleted, this.IsDeleted = value).ExecuteAffrowsAsync() == 1; + .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) + .Set(a => (a as BaseEntity).IsDeleted, IsDeleted = value).ExecuteAffrowsAsync() == 1; + } - this.IsDeleted = value; - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return await this.Repository.UpdateAsync(this as TEntity) == 1; + IsDeleted = value; + Repository.UnitOfWork = _resolveUow?.Invoke(); + return await Repository.UpdateAsync(this as TEntity) == 1; } + /// + /// To delete data
/// 删除数据 ///
- /// 是否物理删除 + /// To flag whether to delete the physical level of the data /// - async public virtual Task DeleteAsync(bool physicalDelete = false) + public virtual async Task DeleteAsync(bool physicalDelete = false) { - if (physicalDelete == false) return await this.UpdateIsDeletedAsync(true); - if (this.Repository == null) + if (physicalDelete == false) + return await UpdateIsDeletedAsync(true); + + if (Repository is null) return await Orm.Delete(this as TEntity).ExecuteAffrowsAsync() == 1; - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return await this.Repository.DeleteAsync(this as TEntity) == 1; + Repository.UnitOfWork = _resolveUow?.Invoke(); + return await Repository.DeleteAsync(this as TEntity) == 1; } + /// + /// To recover deleted data
/// 恢复删除的数据 ///
/// - public virtual Task RestoreAsync() => this.UpdateIsDeletedAsync(false); + public virtual Task RestoreAsync() => UpdateIsDeletedAsync(false); /// + /// To update data
/// 更新数据 ///
/// - async public virtual Task UpdateAsync() + public virtual async Task UpdateAsync() { - this.UpdateTime = DateTime.Now; - if (this.Repository == null) + UpdateTime = DateTime.Now; + if (Repository is null) + { return await Orm.Update() - .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) - .SetSource(this as TEntity).ExecuteAffrowsAsync() == 1; + .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction()) + .SetSource(this as TEntity).ExecuteAffrowsAsync() == 1; + } - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return await this.Repository.UpdateAsync(this as TEntity) == 1; + Repository.UnitOfWork = _resolveUow?.Invoke(); + return await Repository.UpdateAsync(this as TEntity) == 1; } + /// + /// To insert data
/// 插入数据 ///
public virtual Task InsertAsync() { - this.CreateTime = DateTime.Now; - if (this.Repository == null) - this.Repository = Orm.GetRepository(); - - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.InsertAsync(this as TEntity); + CreateTime = DateTime.Now; + Repository ??= Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.InsertAsync(this as TEntity); } /// + /// To insert or update data
/// 更新或插入 ///
/// public virtual Task SaveAsync() { - this.UpdateTime = DateTime.Now; - if (this.Repository == null) - this.Repository = Orm.GetRepository(); - - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.InsertOrUpdateAsync(this as TEntity); + UpdateTime = DateTime.Now; + Repository ??= Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.InsertOrUpdateAsync(this as TEntity); } /// + /// To completely save the navigation properties of the entity in the form of sub-tables.
/// 【完整】保存导航属性,子表 ///
- /// 导航属性名 + /// Navigation property name public virtual Task SaveManyAsync(string navigatePropertyName) { - if (this.Repository == null) - this.Repository = Orm.GetRepository(); - - this.Repository.UnitOfWork = _resolveUow?.Invoke(); - return this.Repository.SaveManyAsync(this as TEntity, navigatePropertyName); + Repository ??= Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.SaveManyAsync(this as TEntity, navigatePropertyName); } #endif } -} +} \ No newline at end of file diff --git a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityReadOnly.cs b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityReadOnly.cs index b3dd1c8a..8b10be63 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityReadOnly.cs +++ b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityReadOnly.cs @@ -1,44 +1,53 @@ - -using FreeSql.DataAnnotations; +using FreeSql.DataAnnotations; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Data.Common; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Threading; +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable InconsistentlySynchronizedField namespace FreeSql { /// + /// Entity base class, including CreateTime/UpdateTime/IsDeleted. + /// /// 包括 CreateTime/UpdateTime/IsDeleted 的实体基类 /// [Table(DisableSyncStructure = true)] public abstract class BaseEntity { internal static IFreeSql _ormPriv; + + private const string ErrorMessageTemplate = @"使用前请初始化: +BaseEntity.Initialization(new FreeSqlBuilder() + .UseAutoSyncStructure(true) + .UseConnectionString(DataType.Sqlite, ""data source=test.db;max pool size=5"") + .Build());"; + /// - /// 全局 IFreeSql orm 对象 + /// Global IFreeSql ORM Object
+ /// 全局 IFreeSql ORM 对象 ///
- public static IFreeSql Orm => _ormPriv ?? throw new Exception(@"使用前请初始化 BaseEntity.Initialization(new FreeSqlBuilder() -.UseAutoSyncStructure(true) -.UseConnectionString(DataType.Sqlite, ""data source=test.db;max pool size=5"") -.Build());"); + public static IFreeSql Orm => _ormPriv ?? throw new Exception(ErrorMessageTemplate); + internal static Func _resolveUow; /// - /// 初始化BaseEntity - /// BaseEntity.Initialization(new FreeSqlBuilder() + /// To initialize the BaseEntity
+ /// 初始化 BaseEntity /// - /// .UseAutoSyncStructure(true) - /// - /// .UseConnectionString(DataType.Sqlite, "data source=test.db;max pool size=5") - /// - /// .Build()); + /// BaseEntity.Initialization(
+ /// new FreeSqlBuilder()
+ /// .UseAutoSyncStructure(true)
+ /// .UseConnectionString(DataType.Sqlite, "data source=test.db;max pool size=5")
+ /// .Build()); ///
- /// IFreeSql orm 对象 + /// IFreeSql ORM Object /// 工作单元(事务)委托,如果不使用事务请传 null解释:由于AsyncLocal平台兼容不好,所以交给外部管理 public static void Initialization(IFreeSql fsql, Func resolveUow) { @@ -52,21 +61,24 @@ namespace FreeSql _ormPriv.CodeFirst.ConfigEntity(cei.EntityType, cei.Fluent); } } + _resolveUow = resolveUow; } - + class ConfigEntityInfo { public Type EntityType; public Action Fluent; } - static ConcurrentQueue _configEntityQueues = new ConcurrentQueue(); - static object _configEntityLock = new object(); + + static readonly ConcurrentQueue _configEntityQueues = new(); + static readonly object _configEntityLock = new(); + internal static void ConfigEntity(Type entityType, Action fluent) { lock (_configEntityLock) { - if (_ormPriv == null) + if (_ormPriv is null) _configEntityQueues.Enqueue(new ConfigEntityInfo { EntityType = entityType, Fluent = fluent }); else _ormPriv.CodeFirst.ConfigEntity(entityType, fluent); @@ -74,31 +86,45 @@ namespace FreeSql } /// + /// Created time
/// 创建时间 ///
[Column(Position = -4)] public virtual DateTime CreateTime { get; set; } = DateTime.Now; + /// + /// Updated time
/// 更新时间 ///
[Column(Position = -3)] public virtual DateTime UpdateTime { get; set; } + /// + /// Logical Delete
/// 逻辑删除 ///
[Column(Position = -2)] public virtual bool IsDeleted { get; set; } + /// + /// Sort
/// 排序 ///
[Column(Position = -1)] public virtual int Sort { get; set; } } + /// + /// A readonly entity base class, including CreateTime/UpdateTime/IsDeleted. + /// + /// 包括 CreateTime/UpdateTime/IsDeleted 的只读实体基类 + /// + /// [Table(DisableSyncStructure = true)] public abstract class BaseEntityReadOnly : BaseEntity where TEntity : class { /// + /// To query data
/// 查询数据 ///
/// @@ -107,68 +133,92 @@ namespace FreeSql get { var select = Orm.Select() - .TrackToList(TrackToList) //自动为每个元素 Attach - .WithTransaction(_resolveUow?.Invoke()?.GetOrBeginTransaction(false)); + .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; - if (ls == null) + if (list is null) + return; + + if (list is not IList ls) { - var ie = list as IEnumerable; - if (ie == null) return; + if (list is not IEnumerable ie) + return; + var isFirst = true; - IBaseRepository berepo = null; + IBaseRepository baseRepo = null; + foreach (var item in ie) { - if (item == null) return; + if (item is null) + { + return; + } + if (isFirst) { isFirst = false; var itemType = item.GetType(); if (itemType == typeof(object)) return; - if (itemType.FullName.Contains("FreeSqlLazyEntity__")) itemType = itemType.BaseType; + if (itemType.FullName!.Contains("FreeSqlLazyEntity__")) itemType = itemType.BaseType; if (Orm.CodeFirst.GetTableByEntity(itemType)?.Primarys.Any() != true) return; - if (item is BaseEntity == false) return; + if (item is not BaseEntity) return; } - var beitem = item as BaseEntity; - if (beitem != null) + + if (item is BaseEntity entity) { - if (berepo == null) berepo = Orm.GetRepository(); - beitem.Repository = berepo; - beitem.Attach(); + baseRepo ??= Orm.GetRepository(); + entity.Repository = baseRepo; + entity.Attach(); } } + return; } - if (ls.Any() == false) return; - if (ls.FirstOrDefault() is BaseEntity == false) return; - if (Orm.CodeFirst.GetTableByEntity(typeof(TEntity))?.Primarys.Any() != true) return; + + if (ls.Any() == false) + return; + + if (ls.FirstOrDefault() is not BaseEntity) + return; + + if (Orm.CodeFirst.GetTableByEntity(typeof(TEntity))?.Primarys.Any() != true) + return; + IBaseRepository repo = null; + foreach (var item in ls) { - var beitem = item as BaseEntity; - if (beitem != null) + if (item is BaseEntity entity) { - if (repo == null) repo = Orm.GetRepository(); - beitem.Repository = repo; - beitem.Attach(); + repo ??= Orm.GetRepository(); + entity.Repository = repo; + entity.Attach(); } } } /// - /// 查询条件,Where(a => a.Id > 10),支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com") + /// Query conditions
+ /// 查询条件,Where(a => a.Id> 10) + /// + /// Support navigation object query
+ /// 支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com") ///
/// lambda表达式 /// public static ISelect Where(Expression> exp) => Select.Where(exp); + /// - /// 查询条件,Where(true, a => a.Id > 10),支导航对象查询,Where(true, a => a.Author.Email == "2881099@qq.com") + /// Query conditions
+ /// 查询条件,Where(true, a => a.Id > 10) + /// + /// Support navigation object query
+ /// 支导航对象查询,Where(true, a => a.Author.Email == "2881099@qq.com") ///
/// true 时生效 /// lambda表达式 @@ -176,21 +226,21 @@ namespace FreeSql public static ISelect WhereIf(bool condition, Expression> exp) => Select.WhereIf(condition, exp); /// + /// Repository object.
/// 仓储对象 ///
protected IBaseRepository Repository { get; set; } /// - /// 附加实体,在更新数据时,只更新变化的部分 + /// To Attach entities. When updating data, only the changed part is updated.
+ /// 附加实体。在更新数据时,只更新变化的部分 ///
public TEntity Attach() { - if (this.Repository == null) - this.Repository = Orm.GetRepository(); - + Repository ??= Orm.GetRepository(); var item = this as TEntity; - this.Repository.Attach(item); + Repository.Attach(item); return item; } } -} +} \ No newline at end of file diff --git a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj index bfd13274..8a9e2002 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj +++ b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj @@ -6,8 +6,8 @@ true FreeSql;ncc;YeXiangQin BaseEntity 是一种极简单的 CodeFirst 开发方式,特别对单表或多表CRUD,利用继承节省了每个实体类的重复属性(创建时间、ID等字段),软件删除等功能,进行 crud 操作时不必时常考虑仓储的使用. - https://github.com/2881099/FreeSql/tree/master/Extensions/FreeSql.Extensions.BaseEntity - https://github.com/2881099/FreeSql/tree/master/Extensions/FreeSql.Extensions.BaseEntity + https://github.com/dotnetcore/FreeSql/tree/master/Extensions/FreeSql.Extensions.BaseEntity + https://github.com/dotnetcore/FreeSql/tree/master/Extensions/FreeSql.Extensions.BaseEntity git MIT FreeSql;ORM;BaseEntity @@ -19,6 +19,7 @@ true key.snk false + latest