为 TreeList DataSource 组合来自不同对象类型的多个 IQueryable

Combining multiple IQueryable from different object types for TreeList DataSource

我正在寻找一种方法来组合来自不同对象类型的两个或多个 IQueryable,以便将其用作我的树列表的数据源。

对于树列表,我使用 DevExpress WinForms 组件“TreeList”。 它为我提供了通常映射到“ID”的属性“KeyFieldName”和映射到父 ID 的 ParentFieldName 以构建层次结构。

我使用 entity framework 6 作为或映射器。

我有以下两个 类 我需要合并:

XObject:

[Table("tbl_objects")]
public class XObject
{
    [Column("id")]
    public int Id { get; set; }
    [Column("display_name")]
    public String DisplayName { get; set; }
    [Column("description")]
    public String Description { get; set; }
    [Column("usage_reason")]
    public String UsageReason { get; set; }
    [Column("is_network_compatible")]
    public bool IsNetworkCompatible { get; set; }
    [Column("ip_address")]
    public String IpAddress { get; set; }
    [Column("network_name")]
    public String NetworkName { get; set; }
    [Column("serial_number")]
    public String SerialNumber { get; set; }
    [Column("manufacturer_identification_code")]
    public String ManufacturerIdentificationCode { get; set; }
    [Column("web_link")]
    public String WebLink { get; set; }
    [Column("warranty")]
    public int WarrantyInDays { get; set; }

    [Column("ref_manufacturer")]
    public virtual XManufacturer Manufacturer { get; set; }
    [Column("ref_order")]
    public virtual XOrder Order { get; set; }
    [Column("ref_owner")]
    public virtual XOwner Owner { get; set; }
    [Column("ref_room")]
    public virtual XRoom Room { get; set; }
    [Column("ref_object_folder")]
    public virtual XObjectFolder ObjectFolder { get; set; }
    public virtual ICollection<XAdditionalObjectData> AdditionalObjectData { get; set; }
}

XObjectFolder:

[Table("tbl_object_folders")]
public class XObjectFolder
{
    [Column("id")]
    public int Id { get; set; }
    [Column("display_name")]
    public String DisplayName { get; set; }
    [Column("short_name")]
    public String ShortName { get; set; }
    [Column("ref_parent_folder")]
    public virtual XObjectFolder ParentFolder { get; set; }
    public virtual ICollection<XObjectFolder> ChildFolders { get; set; }
    public virtual ICollection<XObject> Objects { get; set; }

    [NotMapped]
    public int ParentFolderId { get { return ParentFolder == null ? -1 : ParentFolder.Id; } }
}

您可能已经看到,对象文件夹可以包含子文件夹,也可以包含对象。 我的目标是将其视为我的树列表中的一个“数据源”。 例如像这样:

在这里的其他问题中,我发现了连接或合并可查询对象的可能性,但这只适用于相同类型的对象:

        using (var db = new XDbContext(_conString))
        {
            // Queryables
            var ofs = from of in db.ObjectFolders orderby of.DisplayName ascending select of; // <- All ObjectFolders
            var obs = from obj in db.Objects orderby obj.DisplayName ascending select obj; // <- All Objects

            // Concat them
            var comb = ofs.Concat(obs); // <- not the same type

            // As DataSource for my TreeList
            TreeListObjects.DataSource = comb.ToList();
        }

这就是为什么我正在寻找一种使这成为可能的好方法。

我也可以想象我使用一种非常糟糕的方法来实现我的目标。所以我愿意接受建议。这是我为了提高自己的个人项目。

提前致谢!

编辑

所以我设法通过使用一个接口更进一步 类 共享:

public interface ITreeListCombinable
{
    int Id { get; set; }
    int ParentId { get; }
    String DisplayName { get; set; }
}

但是...谁会想到...又出现了一个问题:

看看数据库结构: Db_Struture

由于两个对象都存储在不同的表中,因此将它们合并时,id 肯定不是唯一的。 这在设置数据源时是必需的。

解法:

所以我对我的问题采取了自己的方法并且成功了。

完全公开 -> 我认为自己是初学者,所以这个解决方案可能不是最好的。尽管如此,如果有人处于类似情况,那么它是如何工作的:

首先我创建了一个界面,文件夹和对象共享:

ITreeListCombinable

public interface ITreeListCombinable
{
    int Id { get; set; }
    int ParentId { get; }
    int ListId { get; set; }
    int ParentListId { get; set; }
    String DisplayName { get; set; }
    ObjectTreeListElementTypes TreeListElementType { get; }
}

然后我确定,我的 XObject 和 XObjectFolder 类 都持有它们对应的 ObjectTreeListElementTypes 值:

ObjectTreeListElementTypes 枚举:

public enum ObjectTreeListElementTypes
{ 
    Folder,
    Object
}

类:

[NotMapped]
public ObjectTreeListElementTypes TreeListElementType => ObjectTreeListElementTypes.Folder; // or *.Object for that matter

所以后来我写了我自己的“控制器”来处理我的特定场景。

ObjectTreeListElementController:

public class ObjectTreeListElementController
{
    private List<ITreeListCombinable> _list;

    public ObjectTreeListElementController()
    {
        _list = new List<ITreeListCombinable>();
    }

    public void AddRange(List<ITreeListCombinable> list)
    {
        // add incoming items to private _list
        _list.AddRange(list);
    }

    public List<ITreeListCombinable> GetDataSourceList()
    {
        // create auto increment list id
        var listId = 0;
        foreach (var item in _list)
        {
            item.ListId = listId;
            listId++;
        }

        // set new parent list id according to incremental list id
        foreach (var item in _list)
        {
            var parents = _list.Where(x => x.Id == item.ParentId && x.TreeListElementType == ObjectTreeListElementTypes.Folder);
            if (parents.Count() > 0)
                item.ParentListId = parents.First().ListId;
            else
                item.ParentListId = -1;
        }

            return _list;
    }
}

本质上,在调用GetDataSourceList()方法时,它首先分发增量的、临时的list-id。 在第二个循环中,我然后搜索原始父 ID 并匹配树列表元素类型。如果找到 none,则此文件夹是我的树列表中的根文件夹,如果找到,给定的列表 ID 将成为父列表 ID:

        using (var db = new XDbContext(_conString))
        {
            // Queryables
            IQueryable<ITreeListCombinable> ofs = from of in db.ObjectFolders orderby of.DisplayName ascending select of;
            IQueryable<ITreeListCombinable> objs = from obj in db.Objects orderby obj.DisplayName ascending select obj;

            var lofs = ofs.ToList();
            var lobjs = objs.ToList();

            var ctrl = new ObjectTreeListElementController();
            ctrl.AddRange(lofs);
            ctrl.AddRange(lobjs);
            var sourceList = ctrl.GetDataSourceList();

            // As DataSource for my TreeList
            TreeListObjects.DataSource = sourceList;
        }

这给我带来了我想要的确切输出:

希望这对另一个初学者有帮助:)