首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >可以在EF Core Include查询中连接不相关的表吗

可以在EF Core Include查询中连接不相关的表吗
EN

Stack Overflow用户
提问于 2020-08-09 02:38:42
回答 1查看 708关注 0票数 0

我有一个EF Core code first项目,并创建了下表

代码语言:javascript
复制
[Table("Activity")]
public class Activity : BaseModel
{
    [Key]
    public int Id { get; set; }
    public int ActivityId { get; set; }
    public TransactionType Type { get; set; }
    public virtual ActivityItem Item { get; set; }
}

ActivityItem是一个抽象类,不映射到它自己的表,但是我有三个独立的模型,它们都继承自它。

代码语言:javascript
复制
//Not its own table in the database
public abstract class ActivityItem
{}

[Table("ClassA")]
public class ClassA : ActivityItem
{
    public int Id {get; set;}
}

[Table("ClassB")]
public class ClassB : ActivityItem
{
    public int Id {get; set;}
}

[Table("ClassC")]
public class ClassC : ActivityItem
{
    public int Id {get; set;}
}

在这个特定的实例中,我显式地避免了每个层次结构的Table-Per-Hierarchy方案,因为我需要让ClassAClassBClassC在数据库中拥有自己的唯一表。

每当查询Activity表中的条目时,我都会使用Type属性来指示ItemClassAClassB还是ClassC的实例。

在形成查询以填充Item导航属性时,有没有办法仍然使用Include方法?

当前解决方案

代码语言:javascript
复制
var activity = _context.Activity.Where(...).FirstOrDefault();

if (activity.Type == "ClassA")
    activity.Item = _context.ClassA.First(p => p.Id == activity.ActivityId);
else if (activity.Type == "ClassB")
    activity.Item = _context.ClassB.First(p => p.Id == activity.ActivityId);
else if (activity.Type == "ClassC")
    activity.Item = _context.ClassC.First(p => p.Id == activity.ActivityId);

所需解决方案

代码语言:javascript
复制
var activity = _context.Activity.Include(p => p.Item).Where(...).FirstOrDefault();

我知道当您使用Include方法时,EF Core将在相关表上编写连接查询,但在我的例子中没有相关表,因为数据库中没有表示ActivityItem。有没有一种方法可以显式地指定根据我的自定义Type字段连接哪个表,而不是求助于每个层次结构的表方案?

EN

回答 1

Stack Overflow用户

发布于 2020-08-10 23:20:51

因此,您有一个包含活动的表,以及一个从该表中过滤零个或多个活动的谓词。您只对第一个通过过滤器的活动感兴趣。可能根本没有任何活动可以通过筛选器,因此是OrDefault。

这个获取的活动有一个(字符串?)属性类型。此属性的值描述您是否应该查看表ClassA / ClassB / ClassC。

获取的活动还具有属性ActivityId。您需要第一个(或默认的) ActivityItem (ClassA / ClassB / ClassC),它的Id等于此ActivityId。

您的设计使属性ActivityId不是此Activity所属的ActivityItem的适当外键。实际上,您的外键是[Type, ActivityId]的组合。

因此,您需要的是一种方法,它可以记住Type,将所有ClassA / ClassB / ClassC连接到一个序列中。之后你就可以加入[Type, Id]了。

如果你只需要这样做一次,你可以使用下面的方法。如果您必须这样做才能解决几个问题,请考虑创建扩展方法。

代码语言:javascript
复制
var activityItems = dbContext.ClassAItems.Cast<ActivityItem>()
    .Select(classAItem => new
    {
        Type = "ClassA",
        Data = classAItem,
    })
.Concat(dbContext.ClassBItems.Cast<ActivityItem>()
    .Select(classBItem => new
    {
        Type = "ClassB",
        Data = classBItem,
    }))
.Concat(dbContext.ClassCItems.Cast<ActivityItem>()
    .Select(classCItem => new
    {
        Type = "ClassC",
        Data = classCItem,
    }));

现在你可以只做一个内连接:

代码语言:javascript
复制
// join the filtered activities with the activityitems:
var result = dbContext.Activities.Where(...)
    .Join(activityItems,

    activity => new                     // from each activity take [type, activityId]
    {
        Type = activity.Type
        Id = activity.ActivityId,
    }
    activityItem => new                 // from each ActivityItems take [Type, Id]
    {
        Type = activityItem.Type,
        Id = activityItem.Data.Id,
    }

    // when these keys match, use the Activity and the matching ActivityItem to make one new
    // well, in this case, you are only interested in the Data of the ActivityItem
    (activity, activityItem) => activityItem.Data)

    // From the joined items you only want the first:
    .FirstOrDefault();
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63319012

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档