问题
I have interesting problem to solve but, although common, it looks like it's not easily achievable with Entity Framework. There are two tables:
Player(Id,TeamId,FirstName,LastName)
Team(Id, Name, IsProfessional)
Player can belong only to one team. Using TPT (DB first), we have two classes mapped to those tables:
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;}
}
What I would like to achieve is 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
}
Is it possible to configure mapping that way IsProfessional property can be used in linq queries?
var result= db.Players.Where(p=>p.IsProfessional==true);
and to have that field populated every time Player entity is materialized?
Player pl = db.Players.Where(p=>p.FirstName="Lionel").FirstOrDefault();
if(pl.IsProfessional)
{
//do something...
}
Already tried with:
- Entity Splitting. Not possible because I want to keep Team mapping and because relationship is not 1:1)
- Mapping Player entity to a db view. Didn't like it because there are other relationships Player entity has that I need. I know it is possible to create them manually, but updating edmx from database will reset ssdl.
Thanks
Solution
Based on second option in Gert Arnold answer, solution that fits my needs is as follows:
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
I created computed field on
Player
tableALTER TABLE Players ADD [IsProfessional] AS dbo.GetIsProfessional(TeamId)
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.
回答1:
This can't be done with EF. There are some options that don't do exactly what you want, but get close more or less:
Create a property
TeamPlayers
in your context that returns the players with the team included, so that you can always doplayer.Team.IsProfessional
even when the context has already been diposed.public IQueryable<Player> TeamPlayers { get { return this.Players.Include("Team"); } }
Create a calculated field in the database table and map to it with
DatabaseGeneratedOption.Computed
.Create a static property in
Player
that returns the expression that accessesTeam.IsProfessional
(requires a living context or team included):public static Expression<Func<Player, bool>> IsProfessional { get { return p => p.Team.IsProfessional; } } ... db.Players.Where( p=> p.FirstName="Lionel").Where(Player.IsProfessional)....
I would prefer the calculated field, because it is always populated, so you can use it inside and outside the scope of a context.
回答2:
You can use System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute to prevent mapping of the IsProfessional
property like these:
// 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 ... */ } }
}
I used this attribute in EF5's Model First
approach and I queried over DbContext/DbSet
and ObjectContext/ObjectQuery
without any exceptions. (100% tested)
回答3:
What if you extend Player to have a property that pulls from 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; } }
}
Of course, if you're worried about regenerating your EDMX, you can make it a partial:
public partial class Player
{
public bool IsProfessional{ get { return Team.IsProfessional; } }
}
来源:https://stackoverflow.com/questions/12798502/entity-framework-entity-read-only-property-mapped-to-a-column-of-related-table