通常将孩子添加到父母中



我正在从数据库中查询多个相关表并将它们映射到类。出于性能原因,我单独查询每个表并在代码中联接它们,而不是使用 SQL 联接。做连接相当简单,每个级别都有一个foreach循环,但感觉是多余的,所以我尝试抽象它。

我有一个基类Entity和两个接口,IParentIChild

public abstract class Entity<TId> : IEquatable<Entity<TId>>
{
public TId Id { get; set; }
}
public interface IParent<TParent, TParentId, TChild, TChildId>
where TChild : Entity<TChildId>, IChild<TChild, TChildId, TParent, TParentId>
where TParent : Entity<TParentId>, IParent<TParent, TParentId, TChild, TChildId>
{
ICollection<TChild> Children { get; set; }
}
public interface IChild<TChild, TChildId, TParent, TParentId> 
where TParent : Entity<TParentId>, IParent<TParent, TParentId, TChild, TChildId>
where TChild : Entity<TChildId>, IChild<TChild, TChildId, TParent, TParentId>
{
TParent Parent { get; set; }
TParentId ParentId { get; set; }
}

实体表示数据库表的抽象,在一对多关系中,父表是 1,子项是多。我写了AddChildren这样的方法:

public partial class Helpers
{
public static void AddChildren<TParent, TParentId, TChild, TChildId>(
IDictionary<TParentId, TParent> parents,
IList<TChild> children)
where TParent : Entity<TParentId>, IParent<TParent, TParentId, TChild, TChildId>
where TChild : Entity<TChildId>, IChild<TChild, TChildId, TParent, TParentId>
{
foreach (var child in children)
{
parents[child.ParentId].Children.Add(child);
}
}
public static void AddChildren<T1, T1Id, T2, T2Id, T3, T3Id>(
IDictionary<T1Id, T1> t1,
IDictionary<T2Id, T2> t2,
IList<T3> t3)
where T1 : Entity<T1Id>, IParent<T1, T1Id, T2, T2Id>
where T2 : Entity<T2Id>, IChild<T2, T2Id, T1, T1Id>, IParent<T2, T2Id, T3, T3Id>
where T3: Entity<T3Id>, IChild<T3, T3Id, T2, T2Id>
{
AddChildren<T2, T2Id, T3, T3Id>(t2, t3);
AddChildren<T1, T1Id, T2, T2Id>(t1, t2.Values.ToList());
}
}

每当我实现接口并调用方法时都必须指定所有类型参数,我并不特别兴奋。简单类如下所示:

public class MyGuidParentEntity : Entity<System.Guid>, IParent<MyGuidParentEntity, System.Guid, MyLongChildEntity, long>
{
public ICollection<MyLongChildEntity> Children { get; set; }
}
public class MyLongChildEntity : Long.Entity, IChild<MyLongChildEntity, long, MyGuidParentEntity, System.Guid>
{
public MyGuidParentEntity Parent { get; set; }
public System.Guid ParentId { get; set; }
}

简单的方法调用如下所示:

Helpers.AddChildren<MyGuidParentEntity, System.Guid, MyLongChildEntity, long>(parents, children);

有没有办法缩短/删除泛型类型参数?如果不是从类声明,那么从AddChildren方法?我希望能够做AddChildren(parents, children)并整理类型。即使AddChildren<TParent, TChild>(parents, children)也将是一种改进。

或者,有没有更好的方法来解决这个问题?我是否因为试图过多地抽象事物而搬起石头砸自己的脚?

从本质上讲,您的问题似乎是关于如何在开发其他方法和派生类的过程中摆脱泛型参数。

携带所有泛型类型参数可以为您提供很大的类型强度。但显然,这是以易用性为代价的。您可以通过稍微降低类型强度来提高易用性。

一种方法是引入一个接口,如下所示:

public interface IEntity: IEquatable<IEntity>
{
// ...
}
public class Entity<TId,TChild>: IEntity
where TChild: IEntity
{
public TId Id { get; private set; }
public ICollection<TChild> Children { get; } = new Collection<TChild>();
// ...
}

另一种方法也可能有意义,具体取决于您的情况。那就是将父/子关系与节点分开定义。喜欢这个:

public interface IEntity
{
// ...
}
public class Entity<TId>: IEntity
{
public TId Id { get; private set; }
public Type ChildrenType { get; private set; }
// ... 
}
public static class Tree
{
static private Dictionary<IEntity, ICollection<IEntity>> ChildCollections = new Dictionary<IEntity, ICollection<IEntity>>();
static private Dictionary<IEntity, IEntity> Parents = new Dictionary<IEntity,IEntity>();
static public ICollection<IEntity> GetChildren(this IEntity parent) => Tree.ChildCollections.ContainsKey(parent) ? this.ChildCollections[parent] : null;
static public IEntity GetParent(this IEntity child) => Tree.Parents.ContainsKey(child) ? this.Parents[child] : null;
static public void AddChild<TParentId,TChildId>( this Entity<TParentId> parent, Entity<TChildId> child)
{
if (!parent.ChildrenType.IsAssignableFrom(child.GetType())) { throw new InvalidOperationException("Child is not of correct type"); }
if (Tree.Parents.ContainsKey(child)) { throw new InvalidOperationException("Child already has a parent"); }
Tree.Parents.Add(child, parent);
if (!Tree.ChildCollections.ContainsKey(parent)) {
Tree.ChildCollections.Add(parent, new Collection<IEntity>());
}
Tree.ChildCollections[parent].Add(child);
}
static public void RemoveChild( this IEntity parent, IEntity child)
{
// ...
}
}

最新更新