【问题标题】:EF Core entity inheritance from different sources without discriminator来自不同来源的 EF Core 实体继承,没有鉴别器
【发布时间】:2019-05-09 10:20:03
【问题描述】:

我正在尝试使用 EF Core 配置下面显示的两个实体:

用户:

public class User
{
    public int UserId { get; set; }
    public string Title { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    [Required]
    public string Email { get; set; }

    [NotMapped]
    public string FriendlyIdentifier
    {
        get
        {
            var name = $"{Name} {Surname}";
            if (!string.IsNullOrWhiteSpace(name))
                return name;
            else if (!string.IsNullOrWhiteSpace(Email))
                return Email;
            else
                return UserId.ToString();
        }
    }

    public ICollection<Booking> Bookings { get; set; }
}

用户详情:

public class UserDetails : User
{
    public DateTime? LatestBookingDate { get; set; }
}

这样做的原因是我希望能够用他们的最新预订日期拉取用户,而不必拉取所有预订,然后在代码中手动确定最新的预订日期。为此,我创建了一个选择最新预订日期的 SQL 视图,并且我想将 UserDetails 绑定到此视图。

我有以下 EF 设置:

modelBuilder.Entity<User>(builder =>
{
    builder.ToTable("userdetails");
    builder.HasKey(x => x.UserId);
    builder.HasIndex(x => new { x.Name, x.Surname, x.Email });
});

modelBuilder.Query<UserDetails>()
    .ToView("view_userdetails");

但是在尝试选择UserDetails 实体时会出现以下错误:

'不能将'User'设置为'UserDetails'的基本类型。遗产 层次结构不能包含实体类型和查询类型的混合。'

我知道有一个HasDiscriminator 扩展名,如下所述:https://docs.microsoft.com/en-us/ef/core/modeling/relational/inheritance,但我并没有真正的鉴别器,因为UserDetails 只是User 的扩展名。

我有什么办法可以让它工作吗?我真的不想将User 的属性复制到UserDetails 中,因为当发生任何变化时需要维护两个实体。我想保留继承权。

【问题讨论】:

  • 也许您可以将LatestBookingDate 作为计算列添加到userdetails 表中。

标签: c# asp.net entity-framework asp.net-core entity-framework-core


【解决方案1】:

我想保留继承权。

你不能(至少现在不是这样)。异常消息清楚地表明了这一点 - 它说“不能包含混合”并且没有为您提供建议。

您可以做的是将您当前的User 类成员移动到一个新的基础非实体非查询 类型类,并让User 实体和UserDetails查询类型继承自它,例如:

public abstract class UserInfo
{
    public int UserId { get; set; }
    public string Title { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    [Required]
    public string Email { get; set; }

    [NotMapped]
    public string FriendlyIdentifier
    {
        get
        {
            var name = $"{Name} {Surname}";
            if (!string.IsNullOrWhiteSpace(name))
                return name;
            else if (!string.IsNullOrWhiteSpace(Email))
                return Email;
            else
                return UserId.ToString();
        }
    }

    public ICollection<Booking> Bookings { get; set; }
}

public class User : UserInfo { }

public class UserDetails : UserInfo
{
    public DateTime? LatestBookingDate { get; set; }
}

这将允许您在一个地方维护通用模型。缺点是UserDetails 将无法在需要User 的地方使用,并且必须进行转换(尽管由于所有常见属性都相同,因此对于 AutoMapper 来说应该是微不足道的“自动”任务)。

请注意,此解决方案要求您在查询类型中 Ignore Bookings 导航属性:

modelBuilder.Query<UserDetails>(builder =>
{ 
    builder.ToView("view_userdetails");
    builder.Ignore(e => e.Bookings);
});

或将其移至User 实体。

【讨论】:

  • 谢谢@ivan。这是一个相当合理的解决方案。我可能需要对我的服务进行一些更改,以便它们将 UserInfo 而不是 User 作为参数,但这不是世界末日。
猜你喜欢
  • 2022-06-30
  • 2013-12-04
  • 2011-03-26
  • 1970-01-01
  • 2011-09-27
  • 1970-01-01
  • 1970-01-01
  • 2019-02-17
相关资源
最近更新 更多