[英]Entity Framework - Custom Field that get data from another Table
i have this scenario: 我有这种情况:
public class Source
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public virtual ICollection<InvoiceMembership> InvoiceMemberships { get; set;}
}
public class InvoiceMembership
{
public int ID { get; set; }
[Column(TypeName = "date")]
public DateTime StartDate { get; set; }
[Column(TypeName = "date")]
public DateTime? EndDate { get; set; }
public virtual Source source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
}
public class InvoiceTemplate
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public bool Enabled { get; set; }
public int NumberOfPayment { get; set; }
}
How can i have a field named CurrentTemplate
with type of InvoiceTemplate
in Source
Entity That have EndDate=null
in related InvoiceMembership
row? 如何在相关
InvoiceMembership
行中具有EndDate=null
Source
实体中具有InvoiceTemplate
类型的名为CurrentTemplate
的字段?
EDIT: 编辑:
i use bellow code, but it's not really true way! 我使用波纹管代码,但这不是真正的方法!
[NotMapped]
public InvoiceTemplate CurrentTemplate { get {
var context=new MedicalContext();
var template = context.InvoiceMemberships.Where(m => m.source == this).Where(m => m.EndDate == null).Select(m => m.InvoiceTemplate);
if (template != null)
return (InvoiceTemplate)template;
else
return null;
} }
Yes you can, but with EF computed properties are such a hassle. 是的,您可以,但是使用EF计算属性非常麻烦。
Let's say you have: 假设您有:
public class Source
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get
{
return InvoiceMemberships
.Where(i = i.EndDate == null)
.Select(i => i.InvoiceTemplate)
.FirstOrDefault();
}
}
public virtual ICollection<InvoiceMembership> InvoiceMemberships { get; set;}
}
There just too many conditions (in my opinion) that should be met to make this work: (我认为)要完成这项工作需要满足太多条件:
CurrentTemplate
can't be used directly in a LINQ query: EF will complain that the expression can't be translated into SQL. CurrentTemplate
:EF会抱怨该表达式无法转换为SQL。 Include()
InvoiceMemberships.InvoiceTemplate
to be able to access CurrentTemplate
in memory, after the context is disposed. Include()
InvoiceMemberships.InvoiceTemplate
才能访问内存中的CurrentTemplate
。 This will always load all InvoiceMemberships
+ InvoiceTemplate
for each Source
object (in one query). Source
对象加载所有 InvoiceMemberships
+ InvoiceTemplate
(在一个查询中)。 Include()
you can only access CurrentTemplate
before the context is disposed. Include()
,则只能在处置上下文之前访问CurrentTemplate
。 This will trigger lazy loading of all InvoiceMemberships
+ InvoiceTemplate
for each Source
object in separate queries. Source
对象的所有 InvoiceMemberships
+ InvoiceTemplate
延迟加载。 As you see, you'll always load (much) more data than needed to get only one InvoiceMembership
per Source
. 如您所见,
InvoiceMembership
每个Source
仅获得一个InvoiceMembership
相比,您将总是加载(远远多于)所需的数据。
The most efficient way is to query the required data into a projection, so the predicate EndDate == null
can be included in the SQL query. 最有效的方法是将所需数据查询到投影中,因此谓词
EndDate == null
可以包含在SQL查询中。
We'll have to wait for NHibernate-style formula properties. 我们将不得不等待NHibernate样式的公式属性。
Expose your Foreign Key value directly in your InvoiceMembership
POCO (it must be there in the database anyway if the relation exists), and the entire query will be elegantly converted to SQL directly by L2E: 直接在
InvoiceMembership
POCO中公开您的外键值(如果存在关系,无论如何它都必须存在于数据库中),并且整个查询将通过L2E直接转换为SQL:
public class InvoiceMembership
{
public int ID { get; set; }
public int SourceId { get; set; }
[ForeignKey("SourceId")]
public virtual Source Source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
}
And in Source
: 并在
Source
:
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get
{
using (var context = new MedicalContext())
return context.InvoiceMemberships
.Where(m => m.SourceId == this.ID)
.Where(m => m.EndDate == null)
.Select(m => m.InvoiceTemplate)
.FirstOrDefault();
}
}
However, this has the drawback that - each time the property is accessed - the database will be queried. 但是,这样做的缺点是-每次访问该属性时-都会查询数据库。 It might be best to move this method to the
InvoiceMembership
class, where you know your InvoiceMembership
objects are getting loaded anyway, and make it a method: 最好将此方法移至
InvoiceMembership
类,在该类中您无论如何都将装入InvoiceMembership
对象,并使其成为一种方法:
public class InvoiceMembership
{
public int ID { get; set; }
public int SourceId { get; set; }
[ForeignKey("SourceId")]
public virtual Source Source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
static public InvoiceTemplate ReadCurrentTemplate(int sourceId)
{
using (var context = new MedicalContext())
return context.InvoiceMemberships
.Where(m => m.SourceId == sourceId)
.Where(m => m.EndDate == null)
.Select(m => m.InvoiceTemplate)
.FirstOrDefault();
}
}
So, now, you have a method , not a property, to no longer hide the fact that an action is being taken each time you access it ... the name Read CurrentTemplate tells you that. 因此,现在,您有了一个方法 (而不是属性),不再隐藏每次访问该操作时都会执行的事实……名称Read CurrentTemplate告诉您。 And why not make it
static
now? 为何现在不使其
static
呢? Furthermore, making it static
means you no longer have to worry about NotMappedAttribute
. 此外,将其设置为
static
意味着您不再需要担心NotMappedAttribute
。
But we'd still like to have access in Source
, wouldn't we (especially if dropping the ICollection<InvoiceMembership>
navigation property, as I see in your comments you wish to do)? 但是我们仍然想在
Source
进行访问,不是ICollection<InvoiceMembership>
(特别是如果删除ICollection<InvoiceMembership>
导航属性,正如我希望在您的评论中看到的那样)? This is now no longer an EF concern at all, but a regular, lazy-loading (if you wish it to be), concern in the Source
class: 现在,这不再是EF的关注点,而是
Source
类中的常规延迟加载(如果您愿意)的关注点:
readonly Lazy<InvoiceTemplate> _currentTemplate;
public Source()
{
_currentTemplate = new Lazy<InvoiceTemplate>(t => t = InvoiceMembership.ReadCurrentTemplate(ID));
}
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get { return _currentTemplate.Value; }
}
So, doing this, you're still going to take the hit of running the database query to get the value of CurrentTemplate
, but only once to evaluate the Lazy
private backer; 因此,这样做,您仍然需要运行数据库查询来获取
CurrentTemplate
的值,但是只有一次可以评估Lazy
私有支持者。 afterward, for the life of this Source
object, this property will be the same as the first time it was read. 之后,在此
Source
对象的生存期内,此属性将与第一次读取该属性相同。 This seemed to fit the model of how you were going to use it, considering it was only ever a read-only property in your examples. 考虑到示例中它只是一个只读属性,因此这似乎符合您将如何使用它的模型。 And it would only be for the life of the
Source
object, which should be in a context anyway. 而且仅在
Source
对象的生存期内,无论如何都应在上下文中。
If not, this should be method ReadCurrentTemplate
(non- static
, no parameter) on Source
as well, and simply return InvoiceMembership.ReadCurrentTemplate(ID)
directly, to indicate that each time it's called it's reading from the Database. 如果不是,这应该是方法
ReadCurrentTemplate
(非static
的,没有参数) Source
的欢迎,并简单地返回InvoiceMembership.ReadCurrentTemplate(ID)
直接,表示每次它被称为它是从数据库中读取。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.