简体   繁体   English

实体框架-从另一个表获取数据的自定义字段

[英]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. 不能在LINQ查询中直接使用CurrentTemplate :EF会抱怨该表达式无法转换为SQL。
  • You always have to 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 (在一个查询中)。
  • Without using 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM