Skip to content

Commit 75960f8

Browse files
committed
🎨 refactor(Ddd): 重构 EntityHelper 类
- 重新组织类结构,按功能模块划分区域 - 新增 CreateGuid 方法,用于生成 Guid 类型的 ID - 添加 RegisterIdGenerator 方法,支持自定义 ID 生成器 - 优化 EntityEquals 方法,提高实体相等性比较的可读性和性能 - 新增 IsEntityWithId 方法,判断类型是否为带主键的实体 - 重构 HasDefaultKeys 方法,提高默认键值检查的准确性 - 优化 FindPrimaryKeyType 方法,简化主键类型查找逻辑 - 新增表达式构建相关方法,用于创建实体 ID 比较的 Lambda 表达式
1 parent c573240 commit 75960f8

File tree

1 file changed

+125
-63
lines changed

1 file changed

+125
-63
lines changed

framework/src/Bing.Ddd.Domain/Bing/Domain/Entities/EntityHelper.cs

Lines changed: 125 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ namespace Bing.Domain.Entities;
1111
/// </summary>
1212
public static class EntityHelper
1313
{
14+
#region ID生成相关
15+
1416
/// <summary>
1517
/// ID生成器字典
1618
/// </summary>
@@ -42,6 +44,12 @@ public static class EntityHelper
4244
/// </summary>
4345
public static Func<int> IntGenerateFunc { get; set; } = () => throw new InvalidOperationException("不支持 Int 作为 ID,请使用 Guid, string 或 long。");
4446

47+
/// <summary>
48+
/// 生成唯一标识 ID,默认使用 Guid 类型。
49+
/// </summary>
50+
/// <returns>生成的 Guid 值</returns>
51+
public static Guid CreateGuid() => CreateKey<Guid>();
52+
4553
/// <summary>
4654
/// 生成唯一标识 ID,支持 Guid、string、long 类型。
4755
/// </summary>
@@ -54,6 +62,22 @@ public static TKey CreateKey<TKey>()
5462
throw new InvalidOperationException($"不支持的 ID 类型: {typeof(TKey)},请使用 Guid, string, long。");
5563
}
5664

65+
/// <summary>
66+
/// 注册自定义 ID 生成器
67+
/// </summary>
68+
/// <typeparam name="TKey">ID 类型</typeparam>
69+
/// <param name="generator">生成器函数</param>
70+
/// <exception cref="ArgumentNullException">当<paramref name="generator"/>为null时抛出</exception>
71+
public static void RegisterIdGenerator<TKey>(Func<TKey> generator)
72+
{
73+
Check.NotNull(generator, nameof(generator));
74+
_idGenerators[typeof(TKey)] = () => generator();
75+
}
76+
77+
#endregion
78+
79+
#region 实体相等性比较
80+
5781
/// <summary>
5882
/// 判断实体类型是否为多租户实体。
5983
/// </summary>
@@ -74,14 +98,13 @@ public static TKey CreateKey<TKey>()
7498
/// </returns>
7599
public static bool EntityEquals(IEntity entity1, IEntity entity2)
76100
{
101+
// 基本检查
77102
if (entity1 == null || entity2 == null)
78103
return false;
79-
80-
// 如果引用相同,则直接返回 true
81104
if (ReferenceEquals(entity1, entity2))
82105
return true;
83106

84-
// 如果两个实体类型不兼容,则返回 false
107+
// 类型兼容性检查
85108
var typeOfEntity1 = entity1.GetType();
86109
var typeOfEntity2 = entity2.GetType();
87110
if (!typeOfEntity1.IsAssignableFrom(typeOfEntity2) && !typeOfEntity2.IsAssignableFrom(typeOfEntity1))
@@ -91,44 +114,54 @@ public static bool EntityEquals(IEntity entity1, IEntity entity2)
91114
if (IsMultiTenantEntity(entity1, entity2))
92115
return AllowSameIdAcrossTenants(entity1, entity2);
93116

94-
// 瞬时对象不视为相等
117+
// 瞬时对象检查 - 瞬时对象不视为相等
95118
if (HasDefaultKeys(entity1) && HasDefaultKeys(entity2))
96119
return false;
97120

98-
// 如果键数量不匹配,则不相等
121+
// 键数量检查
99122
var entity1Keys = entity1.GetKeys();
100123
var entity2Keys = entity2.GetKeys();
101124
if (entity1Keys.Length != entity2Keys.Length)
102125
return false;
103126

104-
// 逐个比较主键值
105-
for (var i = 0; i < entity1Keys.Length; i++)
106-
{
107-
// 如果 `entity1Key` 为 null,`entity2Key` 也必须为 null,否则不相等
108-
var entity1Key = entity1Keys[i];
109-
var entity2Key = entity2Keys[i];
110-
if (entity1Key == null)
111-
{
112-
if (entity2Key == null)
113-
continue;
114-
return false;
115-
}
127+
// 键值比较
128+
return KeysEqual(entity1Keys, entity2Keys);
129+
}
116130

117-
// 如果 `entity2Key` 为 null,则不相等
118-
if (entity2Key == null)
131+
/// <summary>
132+
/// 比较两个键数组是否相等
133+
/// </summary>
134+
/// <param name="keys1">第一个键数组</param>
135+
/// <param name="keys2">第二个键数组</param>
136+
/// <returns>如果键数组相等返回true,否则返回false</returns>
137+
private static bool KeysEqual(object[] keys1, object[] keys2)
138+
{
139+
for (var i = 0; i < keys1.Length; i++)
140+
{
141+
var key1 = keys1[i];
142+
var key2 = keys2[i];
143+
144+
// 空值检查
145+
if (key1 == null)
146+
return key2 == null;
147+
if (key2 == null)
119148
return false;
120149

121-
// 如果两个键值都是默认值(如 0、null、Guid.Empty),则继续比较
122-
if (Types.IsDefaultValue(entity1Key) && Types.IsDefaultValue(entity2Key))
150+
// 默认值检查 - 如果两个键值都是默认值,则视为不相等
151+
if (Types.IsDefaultValue(key1) && Types.IsDefaultValue(key2))
123152
return false;
124153

125-
// 进行键值比较,如果不同,则返回 false
126-
if (!entity1Key.Equals(entity2Key))
154+
// 值比较
155+
if (!key1.Equals(key2))
127156
return false;
128157
}
129158
return true;
130159
}
131160

161+
#endregion
162+
163+
#region 实体和值对象类型检查
164+
132165
/// <summary>
133166
/// 判断指定的类型是否实现了 <see cref="IEntity"/> 接口。
134167
/// </summary>
@@ -141,6 +174,38 @@ public static bool IsEntity(Type type)
141174
return typeof(IEntity).IsAssignableFrom(type);
142175
}
143176

177+
/// <summary>
178+
/// 判断指定的类型是否实现了 <see cref="IEntity{TKey}"/> 接口
179+
/// </summary>
180+
/// <param name="type">要检查的类型</param>
181+
/// <param name="keyType">如果找到,则输出键类型;否则为null</param>
182+
/// <returns>是否为带主键的实体类型</returns>
183+
/// <exception cref="ArgumentNullException">当<paramref name="type"/>为null时抛出</exception>
184+
public static bool IsEntityWithId(Type type, out Type keyType)
185+
{
186+
Check.NotNull(type, nameof(type));
187+
keyType = null;
188+
189+
foreach (var interfaceType in type.GetInterfaces())
190+
{
191+
if (interfaceType.GetTypeInfo().IsGenericType &&
192+
interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>))
193+
{
194+
keyType = interfaceType.GenericTypeArguments[0];
195+
return true;
196+
}
197+
}
198+
199+
return false;
200+
}
201+
202+
/// <summary>
203+
/// 判断指定的类型是否实现了 <see cref="IEntity{TKey}"/> 泛型接口。
204+
/// </summary>
205+
/// <param name="type">要检查的类型。</param>
206+
/// <returns>如果该类型实现了 <see cref="IEntity{TKey}"/> 泛型接口,则返回 <c>true</c>;否则返回 <c>false</c>。</returns>
207+
public static bool IsEntityWithId(Type type) => IsEntityWithId(type, out _);
208+
144209
/// <summary>
145210
/// 值对象判断谓词
146211
/// </summary>
@@ -153,7 +218,7 @@ public static bool IsEntity(Type type)
153218
/// 是否值对象类型
154219
/// </summary>
155220
/// <param name="type">类型</param>
156-
/// <returns>是否为值对象类型。如果类型符合值对象判断条件,则返回true;否则返回false</returns>
221+
/// <returns>是否为值对象</returns>
157222
/// <exception cref="ArgumentNullException">当<paramref name="type"/>为null时抛出</exception>
158223
public static bool IsValueObject(Type type)
159224
{
@@ -165,11 +230,8 @@ public static bool IsValueObject(Type type)
165230
/// 是否值对象
166231
/// </summary>
167232
/// <param name="obj">对象实例</param>
168-
/// <returns>是否为值对象。如果对象不为null且其类型符合值对象判断条件,则返回true;否则返回false</returns>
169-
public static bool IsValueObject(object obj)
170-
{
171-
return obj != null && IsValueObject(obj.GetType());
172-
}
233+
/// <returns>是否为值对象</returns>
234+
public static bool IsValueObject(object obj) => obj != null && IsValueObject(obj.GetType());
173235

174236
/// <summary>
175237
/// 检查指定的类型是否为实体
@@ -184,25 +246,12 @@ public static void CheckEntity(Type type)
184246
throw new ArgumentException($"参数 '{type.FullName}' 不是有效的实体类型。必须实现 {typeof(IEntity).FullName} 接口。", nameof(type));
185247
}
186248

187-
/// <summary>
188-
/// 判断指定的类型是否实现了 <see cref="IEntity{TKey}"/> 泛型接口。
189-
/// </summary>
190-
/// <param name="type">要检查的类型。</param>
191-
/// <returns>如果该类型实现了 <see cref="IEntity{TKey}"/> 泛型接口,则返回 <c>true</c>;否则返回 <c>false</c>。</returns>
192-
public static bool IsEntityWithId(Type type)
193-
{
194-
Check.NotNull(type, nameof(type));
195-
foreach (var interfaceType in type.GetInterfaces())
196-
{
197-
if (interfaceType.GetTypeInfo().IsGenericType &&
198-
interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>))
199-
return true;
200-
}
201-
return false;
202-
}
249+
#endregion
250+
251+
#region 主键检查
203252

204253
/// <summary>
205-
/// 是否有默认标识值
254+
/// 判断实体是否有默认标识值
206255
/// </summary>
207256
/// <typeparam name="TKey">标识类型</typeparam>
208257
/// <param name="entity">实体</param>
@@ -215,18 +264,29 @@ public static bool HasDefaultId<TKey>(IEntity<TKey> entity)
215264
{
216265
if (EqualityComparer<TKey>.Default.Equals(entity.Id, default!))
217266
return true;
267+
return IsDefaultNumericKey(entity.Id);
268+
}
269+
270+
/// <summary>
271+
/// 判断是否为默认的数值类型键值
272+
/// </summary>
273+
/// <typeparam name="TKey">键类型</typeparam>
274+
/// <param name="id">ID值</param>
275+
/// <returns>如果是默认值返回true,否则返回false</returns>
276+
private static bool IsDefaultNumericKey<TKey>(TKey id)
277+
{
218278
if (typeof(TKey) == typeof(int))
219-
return Convert.ToInt32(entity.Id) <= 0;
279+
return Convert.ToInt32(id) <= 0;
220280
if (typeof(TKey) == typeof(long))
221-
return Convert.ToInt64(entity.Id) <= 0;
281+
return Convert.ToInt64(id) <= 0;
222282
return false;
223283
}
224284

225285
/// <summary>
226-
/// 是否有默认值
286+
/// 判断实体是否有默认键值
227287
/// </summary>
228288
/// <param name="entity">实体</param>
229-
/// <returns>是否为默认值。如果所有键都是默认值,则返回true;否则返回false</returns>
289+
/// <returns>是否为默认值</returns>
230290
/// <exception cref="ArgumentNullException">当<paramref name="entity"/>为null时抛出</exception>
231291
public static bool HasDefaultKeys(IEntity entity)
232292
{
@@ -256,17 +316,17 @@ private static bool IsDefaultKeyValue(object value)
256316
return Types.IsDefaultValue(value);
257317
}
258318

319+
#endregion
320+
321+
#region 主键类型查找
322+
259323
/// <summary>
260324
/// 获取指定实体类型的主键类型。
261325
/// </summary>
262326
/// <typeparam name="TEntity">要获取主键类型的实体类型,必须实现 <see cref="IEntity"/> 接口。</typeparam>
263327
/// <returns>主键的类型。</returns>
264328
/// <exception cref="ArgumentException">如果 <typeparamref name="TEntity"/> 不是实体类型,则抛出异常。</exception>
265-
public static Type FindPrimaryKeyType<TEntity>()
266-
where TEntity : IEntity
267-
{
268-
return FindPrimaryKeyType(typeof(TEntity));
269-
}
329+
public static Type FindPrimaryKeyType<TEntity>() where TEntity : IEntity => FindPrimaryKeyType(typeof(TEntity));
270330

271331
/// <summary>
272332
/// 获取指定实体类型的主键类型。
@@ -278,18 +338,18 @@ public static Type FindPrimaryKeyType<TEntity>()
278338
public static Type FindPrimaryKeyType(Type entityType)
279339
{
280340
Check.NotNull(entityType, nameof(entityType));
281-
if (!typeof(IEntity).IsAssignableFrom(entityType))
282-
throw new ArgumentException($"参数 '{entityType.FullName}' 不是有效的实体类型。必须实现 {typeof(IEntity).FullName} 接口。", nameof(entityType));
341+
CheckEntity(entityType);
342+
343+
if (IsEntityWithId(entityType, out var keyType))
344+
return keyType;
283345

284-
foreach (var interfaceType in entityType.GetTypeInfo().GetInterfaces())
285-
{
286-
if (interfaceType.GetTypeInfo().IsGenericType &&
287-
interfaceType.GetGenericTypeDefinition() == typeof(IEntity<>))
288-
return interfaceType.GenericTypeArguments[0];
289-
}
290346
return null;
291347
}
292348

349+
#endregion
350+
351+
#region 表达式构建
352+
293353
/// <summary>
294354
/// 创建一个用于比较实体 ID 是否相等的 Lambda 表达式。
295355
/// </summary>
@@ -314,4 +374,6 @@ public static Expression<Func<TEntity, bool>> CreateEqualityExpressionForId<TEnt
314374
var lambdaBody = Expression.Equal(leftExpression, rightExpression); // 生成 entity.Id == id 的比较表达式
315375
return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
316376
}
377+
378+
#endregion
317379
}

0 commit comments

Comments
 (0)