База Entity - сущность, свойство только для чтения сопоставлен столбец из связанной таблицы


У меня есть интересная проблема, которую нужно решить, но, несмотря на то, что она распространена, похоже, что это не так легко достижимо с 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;}
}

Чего я хотел бы достичь, так это property IsProfessional on Player 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;}
       **public bool IsProfessional{get;}** should be read-only
    }

Можно ли настроить отображение таким образом, чтобы свойство IsProfessional можно было использовать в linq запросы?

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

И чтобы это поле заполнялось каждый раз, когда материализуется сущность игрока?

Player pl = db.Players.Where(p=>p.FirstName="Lionel").FirstOrDefault();
if(pl.IsProfessional)
{
//do something...
}

Уже пробовал с:

  • Разделение Сущностей . Невозможно, потому что я хочу сохранить отображение команды и потому что отношения не 1:1)
  • отображение сущности игрока в представление БД . Мне это не понравилось, потому что есть и другие отношения, которые мне нужны. Я знаю, что их можно создать вручную, но обновление edmx от база данных сбросит ssdl.

Спасибо

Решение

Основываясь на втором варианте ответа Герта Арнольда, решение, которое соответствует моим потребностям, выглядит следующим образом:

  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 approach, я просто обновляю модель из базы данных и вот и все, я могу запросить это поле, и оно предварительно заполняется, когда материализуется объект игрока.

3 6

3 ответа:

Это не может быть сделано с помощью 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)....
    

Я бы предпочел вычисляемое поле, потому что оно всегда заполнено, поэтому вы можете использовать его внутри и вне контекста.

Вы можете использовать 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/DbSet и еще ObjectContext/ObjectQuery без всяких исключений. (Проверено на 100%)

Что делать, если вы расширяете игрока, чтобы иметь свойство, которое тянет из команды?

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