使用 EF Plus IncludeFilter 时出现 'Cannot create an instance of an interface' 错误
Getting an 'Cannot create an instance of an interface'-error with EF Plus IncludeFilter
有一个 IList<Guid>
,我希望从上下文中找到所有匹配项,然后包括一些相关的 table,其中不包括过时的数据。
由于数据的大小,我尝试使用 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
数据结构
数据库结构以包含 immutable Guid 的 Facts table 为中心,用于标识每个条目。所有其他 tables link 中的条目到此 guid 以绑定到一个条目。其他 table 包含一个 DateTime ValidTo
来跟踪更改。不会更新或删除任何条目。相反,在更改时使用 ValidTo = DateTime.MaxValue
创建一个新条目,并且正在更新的条目将 ValidTo
设置为 DateTime.Now
。
这确保所有更改都保留在历史记录中。
随着时间的推移,绝大多数数据都将是历史数据,因此我们可以在 SQL 查询中过滤掉这些数据至关重要。
Fact-table的数据结构是这样的:
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
}
所有其他 table 继承自 ModelBase,link将它们与事实 table 相结合。
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
}
Person 和 Patient 的示例 tables
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
开始简化。这是通过仅添加用户登录的公司的 Patients 来过滤的。然后应用搜索词。最后,在调用 AssignDataToPatientByFactId
.
之前,将参数转换为仅包含所需 guid 的列表
// 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 列表,在 Facts-table 中找到所有匹配项,然后添加来自其他 table 的条目,其中 ValidTo
时间戳是最大。因此不应包括所有其他条目。
将代码分成几个语句表明 IncludeFilter 似乎可以正常工作,但调用 ToListAsync 会产生错误。
免责声明:我是项目的所有者Entity Framework Plus
已发布 v2.0.8 版本修复此问题。
问题是因为 class Fact
使用了我们的库尚不支持的 IEnumerable<T>
属性。
Fiddle 现在按预期工作:https://dotnetfiddle.net/MYukHp
有一个 IList<Guid>
,我希望从上下文中找到所有匹配项,然后包括一些相关的 table,其中不包括过时的数据。
由于数据的大小,我尝试使用 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
数据结构
数据库结构以包含 immutable Guid 的 Facts table 为中心,用于标识每个条目。所有其他 tables link 中的条目到此 guid 以绑定到一个条目。其他 table 包含一个 DateTime ValidTo
来跟踪更改。不会更新或删除任何条目。相反,在更改时使用 ValidTo = DateTime.MaxValue
创建一个新条目,并且正在更新的条目将 ValidTo
设置为 DateTime.Now
。
这确保所有更改都保留在历史记录中。
随着时间的推移,绝大多数数据都将是历史数据,因此我们可以在 SQL 查询中过滤掉这些数据至关重要。
Fact-table的数据结构是这样的:
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
}
所有其他 table 继承自 ModelBase,link将它们与事实 table 相结合。
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
}
Person 和 Patient 的示例 tables
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
开始简化。这是通过仅添加用户登录的公司的 Patients 来过滤的。然后应用搜索词。最后,在调用 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 列表,在 Facts-table 中找到所有匹配项,然后添加来自其他 table 的条目,其中 ValidTo
时间戳是最大。因此不应包括所有其他条目。
将代码分成几个语句表明 IncludeFilter 似乎可以正常工作,但调用 ToListAsync 会产生错误。
免责声明:我是项目的所有者Entity Framework Plus
已发布 v2.0.8 版本修复此问题。
问题是因为 class Fact
使用了我们的库尚不支持的 IEnumerable<T>
属性。
Fiddle 现在按预期工作:https://dotnetfiddle.net/MYukHp