【问题标题】:Generically add children to parents一般将孩子添加到父母
【发布时间】:2020-01-27 03:12:38
【问题描述】:

我正在从数据库中查询多个相关表并将它们映射到类。出于性能原因,我分别查询每个表并将它们加入我的代码中,而不是使用 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; }
}

实体表示数据库表的抽象,在一对多的关系中,父是一,子是多。我写了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&lt;TParent, TChild&gt;(parents, children) 也会有所改进。

或者,有没有更好的方法来解决这个问题?我是否因为试图抽象事物太多而自取其辱?

【问题讨论】:

  • 有什么方法可以简化泛型参数? → 像什么简化?以什么成本,失去一些约束或失去一些功能?
  • 我应该做一些完全不同的事情吗? → 您分享了您的解决方案,但没有说明您要满足的要求。
  • 这是代码审查吗?
  • 出于性能原因,我分别查询每个表并将它们加入我的代码中,而不是使用 SQL 连接。 => 这让我很困惑!那么,当您需要加入 3 个表中的数百万条记录时会发生什么?将它们全部放入内存,连接对象?
  • 通过网络从服务器中提取所有数据以将它们加入内存是一个糟糕的主意,不要这样做。 SQL 在服务器上完成所有工作,然后通过网络发送您真正需要的数据。

标签: c# oop interface


【解决方案1】:

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

携带所有泛型类型参数会给你很大的类型强度。但显然,这是以易用性为代价的。您可以通过稍微降低字体强度来增加易用性。

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

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)
    {
        // ...
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-03
    相关资源
    最近更新 更多