LINQ导航属性有多个来源

时间:2015-03-15 13:48:41

标签: c# sql linq linq-to-entities linq-to-objects

我有一个存储不同位置的数据库。根据位置的类型,唯一的详细信息存储在与公共属性不同的表中(即名称,坐标,描述存储在Locations表中,而人口存储在CityDetails中表)。 表格通过FK在“具体细节”表中连接。 由于有多个“特定细节”表,我正在摸索如何查询这些多个表。 我的位置对象如下所示:

[Table("Locations")]
public class LocationData
{
    public Guid Id { get; set; }

    // multiple different properties here.

    public string Type { get; set; }

    public DetailsData { get; set; }
}

基于Type属性,我可以看到我必须查询的详细信息表。详细信息表至少看起来像这样:

public abstract class DetailsData 
{
    [Key, ForeignKey("Location")]
    public Guid { get; set; }

    public LocationData Location { get; set; }
}

表示位置类型的每个表都从DetailsData类继承,并配置为使用“Table-per-concrete-class”继承层次结构。 以下是“特定详细信息”表格的示例:

[Table("CityDetails")]
public class CityDetailsData : DetailsData
{
    public string Planet { get; set; }

    public int Population { get; set; }
}

如何填充DetailsData课程中的Location属性。这是我当前的LINQ查询:

from l in Locations
select new
{
    Id = l.Id,
    Name = l.Name,
    Description = l.Description,
    Coordinates = l.Coordinates,
    Type = l.Type,
    DetailsData = CityDetails // Here lies the problem. How can I populate this property properly?
}

2 个答案:

答案 0 :(得分:0)

好的,我正在假设您的详细信息表都有不同的列集,您希望能够使用IEnumerable<LocationData>个实例,并且您希望能够直接访问这些属性您返回的任何详细信息表对象的实例。

这是一个艰难的过程。基本上有三种方法可以为未知类型的对象编写代码:

  1. 给它一种Object
  2. 使所有“详细信息”表继承自公共接口,并使CityDetails成为实现该接口的对象。
  3. 使用泛型类型,这意味着您最终会指定类型,但您可以将该决定从链中推送到您实现类型的声明者。
  4. 使用System.Object意味着您需要将所有DetailsData对象强制转换为所需类型(或使用Reflection访问其属性),而使用接口意味着您可以直接访问某些属性,但可能不是你想要的一切。

    我认为通用类型可能是您最好的混合解决方案。

    我会做这样的事情:

    1. 接受没有完美的解决方案。
    2. 而不是像在示例代码中那样创建匿名类型,而是定义一个这样的类(类似于ViewModel,如果您使用的是ASP.NET MVC):

      public class LocationViewModel<TDetailsData>
      {
          public Guid Id { get; set; }
      
          // multiple different properties here.
      
          public string Type { get; set; }
      
          public TDetailsData DetailsData { get; set; }
      }
      
    3. 定义一个辅助方法,该方法接受一个类型参数并返回类型TDetailsData的值。用它填充DetailsData上的LocationViewModel媒体资源。

       TDetailsData GetLocationDetails<TDetailsData>(string type, Guid ID)
       {
           // get the appropriate data based on the type.
       }
      
    4. 如果您需要保留IEnumerable这些对象,请定义一个界面ILocationDataViewModel,另一个ILocationDataViewModel<TDetailsData>,请LocationViewModel实现这两个对象,并且根据情况要求使用IEnumerable<ILocationDataViewModel>IEnumerable<ILocationDataViewModel<CityDetailsData>>等。

答案 1 :(得分:0)

如果我理解正确,您希望动态确定类型并动态处理它。在这种情况下,您可以使用dynamic。我没有测试过这段代码,但你可以在那行思考

        public DetailsData GetDetailsData(Type t) 
        {
            dynamic typVal = new ExpandoObject();

            if(t == typeof(CityDetails))
            {
                typVal = new CityDetails();
            }
            // add other types

            return (DetailsData)PopulateDetailsData(typVal);
        }

        public DetailsData PopulateDetailsData(CityDetails cd)
        {
            cd.Planet = new CityDetails().Planet;
            return cd;
        }

        // Add other type related methods with same 
        // signature with input parameter differentiated by types
        // for example
        public DetailsData PopulateDetailsData(TownDetails td)
        {
            td.Income = new TownDetails().Income;
            return td;
        }

在查询中,您可以使用下面的内容

from l in Locations
select new
{
    Id = l.Id,
    Name = l.Name,
    Description = l.Description,
    Coordinates = l.Coordinates,
    Type = l.Type,
    DetailsData = GetDetailsData(l.Type)
}