使用EF Plus IncludeFilter遇到“无法创建接口实例”错误

时间:2019-09-16 11:37:06

标签: c# asp.net-core-2.2 entity-framework-plus

我有一个IList<Guid>,我希望从上下文中找到所有匹配项,然后包括一些相关的表,其中不包括过时的数据。 由于数据的大小,我尝试使用EF Plus IncludeFilter,以避免将所有内容加载到内存中并在其中执行过滤。 当我在查询上调用ToListAsync()时,会发生问题。然后,IncludeFilter(据我所知)会引发System.MissingMethodException : Cannot create an instance of an interface异常。

该项目在.NET Core 2.2中完成,我使用Z.EntityFramework.Plus.EFCore 2.0.7

示例项目

此示例重现了该问题:https://dotnetfiddle.net/MYukHp

数据结构

数据库结构以事实表为中心,事实表包含一个不变的Guid,用于标识每个条目。所有其他表中的条目都链接到该guid,以将其绑定到一个条目。其他表包含DateTime ValidTo以跟踪更改。没有条目被更新或删除。相反,在更改时,将使用ValidTo = DateTime.MaxValue创建一个新条目,并且将要更新的条目的ValidTo设置为DateTime.Now。 这样可以确保所有更改都保持历史记录。

随着时间的流逝,绝大多数数据都是历史数据,因此至关重要的是我们必须在SQL查询中将其过滤掉。

事实表的数据结构如下:

public class FactModel
{
    public Guid FactId { get; set; }
    public DateTime ValidFrom { get; set; }
    public DateTime ValidTo { get; set; }

    // Navigation properties
    public IEnumerable<PersonModel> Persons { get; set; }
    // Repeat for all other tables
}

所有其他表都继承自ModelBase,并将它们链接到Fact表。

public class ModelBase
{
    public Guid FactId { get; set; }    // Link to the Fact
    public FactModel Fact { get; set; }  // Navigation property
    public DateTime ValidFrom { get; set; } 
    public DateTime ValidTo { get; set; }   // ValidTo == DateTime.MaxValue -> active record
}

人和病人的示例表

public class PersonModel : ModelBase
{
    public Guid PersonId { get; set; }    // Key - A new is created on every update
    public string FirstName { get; set; }  // data
    public string LastName { get; set; }   // data
}

public class PatientModel : ModelBase
{
    public Guid PatientId { get; set; }  // Key - A new is created on every update
    public Guid ActiveCompanyId { get; set; }  // Data
    public int HealthInsuranceGroup { get; set; } // Data
    public PatientStatusType Status { get; set; } // Data
}

将参数更改为IQueryable会产生一个新错误: System.InvalidCastException : Unable to cast object of type System.Guid' to type 'System.String'

通话顺序

调用顺序相当复杂,但为简化起见,我们首先声明了调用参数IQueryable<FactModel> facts。仅通过添加用户登录公司的患者可以对此进行过滤。然后应用搜索词。最后,在调用AssignDataToPatientByFactId之前,该参数将转换为仅包含需要指导的列表。

// Performing a search for an Address

IQueryable<FactModel> facts = null;
facts = _context.Facts.AsNoTracking().Where(p => p.Patients.Any(a => a.ActiveCompanyId == _identificationHandler.Identification.CompanyId));
facts = facts.AsNoTracking().Where(p => p.Addresses.Where(z => z.ValidTo == DateTime.MaxValue).Any(q => q.Street.Any() && q.Street.StartsWith(searchDTO.SearchString)));
return await AssignDataToPatient(facts.Select(x => x.FactId).ToList()), cancel);
public async Task<List<FactModel>> AssignDataToPatientByFactId(IList<Guid> factIds, CancellationToken cancel)
{
    return await _context.Facts.Where(x => factIds.Contains(x.FactId))
        .IncludeFilter(x => x.Patients.Where(c => c.ValidTo == DateTime.MaxValue))
        .IncludeFilter(x => x.Persons.Where(c => c.ValidTo == DateTime.MaxValue))
        .IncludeFilter(x => x.Communications.Where(c => c.ValidTo == DateTime.MaxValue))
        .IncludeFilter(x => x.Addresses.Where(c => c.ValidTo == DateTime.MaxValue))
        .ToListAsync(cancel);
}

因此,AssignDataToPatientByFactId提取了一个Guid列表,在事实表中找到所有匹配项,然后添加其他表中的条目,其中ValidTo时间戳为Max。因此,不应包括所有其他条目。

将代码分成几条语句表明IncludeFilter似乎有效,但是调用ToListAsync会产生错误。

1 个答案:

答案 0 :(得分:1)

免责声明:我是项目Entity Framework Plus

的所有者

v2.0.8版本已发布,解决了此问题。

此问题是由于类Fact使用的IEnumerable<T>属性尚未得到我们的库支持而引起的。

小提琴现在可以按预期工作:https://dotnetfiddle.net/MYukHp