База 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.
Спасибо
Решение
Основываясь на втором варианте ответа Герта Арнольда, решение, которое соответствует моим потребностям, выглядит следующим образом:
-
Я создаю функцию
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
-
Я создал вычисляемое поле на
Player
таблицеALTER TABLE Players ADD [IsProfessional] AS dbo.GetIsProfessional(TeamId)
-
Поскольку я использую db first approach, я просто обновляю модель из базы данных и вот и все, я могу запросить это поле, и оно предварительно заполняется, когда материализуется объект игрока.
3 ответа:
Это не может быть сделано с помощью EF. Есть несколько вариантов, которые не делают именно то, что вы хотите, но приближаются более или менее:
Создайте свойство
TeamPlayers
в вашем контексте, которое возвращает игроков с включенной командой, так что вы всегда можете сделатьplayer.Team.IsProfessional
, даже если контекст уже был погружен.public IQueryable<PLayer> TeamPlayers { get { return this.Players.Include("Team"); } }
Создайте вычисляемое поле в таблице базы данных и сопоставьте его с помощью
DatabaseGeneratedOption.Computed
.Создайте статическое свойство в
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; } } }