簡體   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