9

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

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

玩家只能属于一支球队。使用 TPT(DB first),我们有两个类映射到这些表:

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...
}

已经尝试过:

  • 实体拆分。不可能,因为我想保留团队映射并且因为关系不是 1:1)
  • 将播放器实体映射到数据库视图。不喜欢它,因为玩家实体还有我需要的其他关系。我知道可以手动创建它们,但是从数据库更新 edmx 会重置 ssdl。

谢谢

解决方案

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

  1. 我创建函数GetIsProfessional(必须这样做,因为计算字段通常只能从自己的表字段中创建)

    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. Player我在表上创建了计算字段

    ALTER TABLE Players ADD [IsProfessional] AS dbo.GetIsProfessional(TeamId)
    
  3. 当我使用 db first 方法时,我只是从数据库中更新模型,仅此而已,我可以在该字段上进行查询,并在 Player 对象物化时预先填充它。

4

3 回答 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(需要包含生活上下文或团队):

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

我更喜欢计算字段,因为它总是被填充,所以你可以在上下文范围内外使用它。

于 2012-10-10T10:33:45.217 回答
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; } }
}
于 2012-10-09T20:47:13.270 回答
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's Model First我查询了一遍DbContext/DbSetObjectContext/ObjectQuery没有任何异常。(100% 测试)

于 2013-06-05T00:20:42.043 回答