实体框架 - 实体只读属性映射到相关表的列

9

我有一个有趣的问题需要解决,但是虽然很常见,但看起来使用Entity Framework并不容易实现。有两个表:

Player(Id,TeamId,FirstName,LastName)
Team(Id, Name, IsProfessional)

玩家只能属于一个团队。使用TPT(数据库优先),我们将两个类映射到这些表:

public class Player
{
   public int Id{get;set;}
   public int TeamId{get;set;}
   public string FirstName{get; set;}
   public string LastName{get; set;}
   public Team Team{get;set;}
}

public class Team
{ 
   public int Id{get; set;}
   public string Name{get;set;}
   public bool IsProfessional{get;set;}
   public IEnumerable<Player> Players{get;}
}

我希望实现的是在Player实体上有一个IsProfessional属性:
public class Player
    {
       public int Id{get;set;}
       public int TeamId{get;set;}
       public string FirstName{get; set;}
       public string LastName{get; set;}
       public Team Team{get;set;}
       **public bool IsProfessional{get;}** should be read-only
    }

是否可以配置映射,以便在linq查询中使用IsProfessional属性?

var result= db.Players.Where(p=>p.IsProfessional==true);

并且希望每当Player实体被实例化时,该字段都能被填充?
Player pl = db.Players.Where(p=>p.FirstName="Lionel").FirstOrDefault();
if(pl.IsProfessional)
{
//do something...
}

已经尝试过以下方法:

  • 实体拆分。不可行,因为我想保留Team映射,并且关系不是1:1。
  • 将Player实体映射到数据库视图。不喜欢这种方法,因为Player实体还有其他关系需要使用。我知道可以手动创建它们,但从数据库更新edmx将重置ssdl。

谢谢

解决方案

基于Gert Arnold答案中的第二个选项,适合我的解决方案如下:

  1. I create function GetIsProfessional (had to do it because computed fields normally can be made only from own table fields)

    CREATE FUNCTION [dbo].[GetIsProfessional](@teamId as INT)
    RETURNS bit
    
    BEGIN
    
    DECLARE @isProfi AS bit
    
    SELECT @isProfi = IsProfessional
    FROM Teams
    WHERE Id = @teamId
    
    RETURN @isProfi
    
    END
    
  2. I created computed field on Player table

    ALTER TABLE Players ADD [IsProfessional] AS dbo.GetIsProfessional(TeamId)
    
  3. As I'm using db first approach, I just update model from database and that's it, I can query on that field and it's pre populated when Player object is materialized.


那么 Player.IsProfessional 应该和 Player.Team.IsProfessional 得到相同的结果吗?EF 目前不支持不映射到简单数据库字段的属性,很抱歉,但也许有人会回答一个好的替代方案。 - user743382
这正是我所需要的,只是Player.IsProfessional应该是只读的。我自己能找到的所有替代方案都不是很好。 - Misha N.
3个回答

10
这不能用EF完成。有一些选项不完全符合您的要求,但大体上接近:
  1. 在您的上下文中创建一个名为TeamPlayers的属性,返回包含该团队的球员,这样即使上下文已被销毁,您仍然可以始终执行player.Team.IsProfessional

    public IQueryable<Player> TeamPlayers
    {
        get { return this.Players.Include("Team"); }
    }
    
  2. 在数据库表中创建一个计算字段,并使用 DatabaseGeneratedOption.Computed 进行映射。

  3. Player 类中创建一个静态属性,返回访问 Team.IsProfessional 表达式(需要包含上下文或已包含 Team):

  4. public static Expression<Func<Player, bool>> IsProfessional
    {
        get { return p => p.Team.IsProfessional; }
    }
    ...
    db.Players.Where( p=> p.FirstName="Lionel").Where(Player.IsProfessional)....
    

    我更喜欢使用计算字段,因为它始终保持有值,所以您可以在上下文范围内和外部使用它。


太好了!计算字段最适合我的需求:1)我可以在计算字段上查询,2)它会填充其他玩家属性。非常感谢! - Misha N.

0

您可以使用System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute来防止像IsProfessional属性这样的映射:

// Mapped part of entity
public class Player
{
    public int Id { get; set; }
    public int TeamId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Team Team { get; set; }
}

// Unmapped part of entity
using System.ComponentModel.DataAnnotations.Schema;
...
public partial class Player
{
    [NotMapped()]
    public bool IsProfessional { get { /* ... IsProfessional calculation logic comes here ... */ } }
}

我在EF5的模型优先方法中使用了这个属性,并且在DbContext/DbSetObjectContext/ObjectQuery上查询,没有出现任何异常。(已经100%测试过)


好的,但是我从这个实体创建的数据源中没有显示这个属性。 - Alin I
我通常使用ObjectDataSource,它显示我的实体类的所有公共属性。 我认为EntityDataSource只读取生成实体的映射属性,因为在.edmx文件中声明了它们的模式。 - Perseus
这个能用吗?上面提到的 Linq 问题怎么样了? - Worthy7
提供计算(虚拟)数据列以及急切加载方法已经足够好了,但是无法将其逻辑转换为自定义查询的WHERE子句中的实际数据列。 - Perseus

0
如果你将Player扩展到具有从Team获取的属性,会怎样呢?
public partial class Player
{
   public int Id{get;set;}
   public int TeamId{get;set;}
   public string FirstName{get; set;}
   public string LastName{get; set;}
   public Team Team{get;set;}

   public bool IsProfessional{ get { return Team.IsProfessional; } }
}

当然,如果你担心重新生成EDMX,你可以将其设置为部分类:

public partial class Player
{
   public bool IsProfessional{ get { return Team.IsProfessional; } }
}

是的,但这种方式Player.IsProfessional在linq查询中不可用(正如我在问题中所解释的)。此外,当访问属性Player.IsProfessional时,它将执行SQL查询,只从数据库获取一个布尔值。如果该布尔值与所有其他Player属性同时填充,我会更喜欢。 - Misha N.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接