繁体   English   中英

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

[英]Entity Framework - Custom Field that get data from another Table

我有这种情况:

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

如何在相关InvoiceMembership行中具有EndDate=null Source实体中具有InvoiceTemplate类型的名为CurrentTemplate的字段?
编辑:
我使用波纹管代码,但这不是真正的方法!

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

是的,您可以,但是使用EF计算属性非常麻烦。

假设您有:

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

(我认为)要完成这项工作需要满足太多条件:

  • 不能在LINQ查询中直接使用CurrentTemplate :EF会抱怨该表达式无法转换为SQL。
  • 处置上下文后,始终必须Include() InvoiceMemberships.InvoiceTemplate才能访问内存中的CurrentTemplate 这将始终为每个Source对象加载所有 InvoiceMemberships + InvoiceTemplate (在一个查询中)。
  • 如果不使用Include() ,则只能在处置上下文之前访问CurrentTemplate 这将在单独的查询中触发每个Source对象的所有 InvoiceMemberships + InvoiceTemplate延迟加载。

如您所见, InvoiceMembership每个Source仅获得一个InvoiceMembership相比,您将总是加载(远远多于)所需的数据。

最有效的方法是将所需数据查询到投影中,因此谓词EndDate == null可以包含在SQL查询中。

我们将不得不等待NHibernate样式的公式属性。

直接在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; }
}

并在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();
    }
}

但是,这样做的缺点是-每次访问该属性时-都会查询数据库。 最好将此方法移至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();
    }
}

CurrentTemplate tells you that. 因此,现在,您有了一个方法 (而不是属性),不再隐藏每次访问该操作时都会执行的事实……名称 CurrentTemplate告诉您。 为何现在不使其static呢? 此外,将其设置为static意味着您不再需要担心NotMappedAttribute

但是我们仍然想在Source进行访问,不是ICollection<InvoiceMembership> (特别是如果删除ICollection<InvoiceMembership>导航属性,正如我希望在您的评论中看到的那样)? 现在,这不再是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; }
}

因此,这样做,您仍然需要运行数据库查询来获取CurrentTemplate的值,但是只有一次可以评估Lazy私有支持者。 之后,在此Source对象的生存期内,此属性将与第一次读取该属性相同。 考虑到示例中它只是一个只读属性,因此这似乎符合您将如何使用它的模型。 而且仅在Source对象的生存期内,无论如何都应在上下文中。

如果不是,这应该是方法ReadCurrentTemplate (非static的,没有参数) Source的欢迎,并简单地返回InvoiceMembership.ReadCurrentTemplate(ID)直接,表示每次它被称为它是从数据库中读取。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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