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

C# 实体框架-从另一个表获取数据的自定义字段,c#,entity-framework,C#,Entity Framework,我有这样一个场景: 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 I

我有这样一个场景:

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

是的,您可以,但是使用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;}
}
公共类源代码
{
公共int ID{get;set;}
公共字符串名称{get;set;}
公共字符串说明{get;set;}
[未映射]
公共发票模板CurrentTemplate
{ 
得到
{
退票会员资格
.其中(i=i.EndDate==null)
.Select(i=>i.InvoiceTemplate)
.FirstOrDefault();
}
}
公共虚拟ICollection InvoiceMemberships{get;set;}
}
在我看来,要实现这一目标,需要满足的条件太多了:

  • CurrentTemplate
    不能直接用于LINQ查询:EF会抱怨表达式无法转换为SQL
  • 您必须始终
    Include()
    InvoiceMemberships.InvoiceTemplate
    才能在释放上下文后访问内存中的
    CurrentTemplate
    。这将始终为每个
    对象(在一个查询中)加载所有
    InvoiceMemberships
    +
    InvoiceTemplate
  • 如果不使用
    Include()
    ,您只能在释放上下文之前访问
    CurrentTemplate
    。这将在单独的查询中为每个
    对象触发所有
    invoicemembership
    +
    InvoiceTemplate
    的延迟加载
如您所见,每次
源代码
只需加载一个
InvoiceMembership
所需的数据(远远)就可以了

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


我们必须等待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; }
}
源代码中

[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();
    }
}
所以,现在,你有了一个方法,而不是一个属性,不再隐藏这样一个事实,即每次你访问它时都会执行一个操作。。。ReadCurrentTemplate的名称告诉您这一点。为什么不现在就让它成为静态的呢?此外,将其设置为
静态
意味着您不再需要担心
NotMappedAttribute

但是我们仍然希望能够访问
源代码
,不是吗(特别是如果删除
ICollection
导航属性,正如我在您的评论中看到的,您希望这样做)?现在,这不再是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; }
}
readonly Lazy\u currentTemplate;
公共来源()
{
_currentTemplate=new Lazy(t=>t=InvoiceMembership.ReadCurrentTemplate(ID));
}
[未映射]
公共发票模板CurrentTemplate
{
获取{return\u currentTemplate.Value;}
}
因此,这样做,您仍然需要运行数据库查询来获取
CurrentTemplate
的值,但只需要一次来评估
懒惰的
私人支持者;之后,在该
对象的生命周期内,该属性将与第一次读取时相同。这似乎符合您将如何使用它的模型,考虑到在您的示例中它只是一个只读属性。它只适用于
对象的生命周期,该对象无论如何都应该在上下文中


如果不是,这也应该是
上的方法
ReadCurrentTemplate
(非
静态
,无参数),只需直接返回
InvoiceMembership.ReadCurrentTemplate(ID)
,指示每次调用时它都在从数据库中读取。

它需要在数据库中,还是只需要一个基于invoicememberships中的内容计算的字段?在这种情况下,只需创建一个成员并用[NotMapped]装饰它,我就可以编辑我的帖子@PhilipStuyck…我在数据库中不需要它谢谢@Gert Arnold。。。我还有一个问题,如果我想删除
实体中
InvoiceMembership
的导航,那么如何获取
CurrentTemplate
?@Mahdi如果出现新问题,最好问一个新问题。在StackOverflow,我们不像大多数论坛那样创建线程。我们如何在DTO中引用这个NotMapped字段<代码>公共发票模板CurrentTemplate{get;}
是这样吗?
readonly Lazy<InvoiceTemplate> _currentTemplate;
public Source()
{
    _currentTemplate = new Lazy<InvoiceTemplate>(t => t = InvoiceMembership.ReadCurrentTemplate(ID));
}
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
    get { return _currentTemplate.Value; }
}