Entity Framework 6 DB-First:仅针对 SQL Server 发出 InvalidOperationException
Posted
技术标签:
【中文标题】Entity Framework 6 DB-First:仅针对 SQL Server 发出 InvalidOperationException【英文标题】:Entity Framework 6 DB-First: issue InvalidOperationException only for SQL Server 【发布时间】:2021-10-21 01:32:26 【问题描述】:前提
我们在 .NET 4.7.2 中有一个使用 Entity Framework 6 db-first 的应用程序(带有一个 `.edmx` 文件)。到目前为止,我们只使用 Devart for Oracle 作为 EF 的 Provider,但现在我们需要处理 SQL Server 数据库。我们所有的 ASP.NET MVC 5 视图(数百个)都需要使用 `System.Data.SqlClient`。没问题,查询效果很好,除了...问题
该问题与外键和引用表有关。错误如下:
InvalidOperationException:转换为值类型“System.Int32”失败,因为具体化值为 null。结果类型的泛型参数或查询必须使用可为空的类型。
在 System.Data.Entity.Core.Common.Internal.Materialization.ErrorHandlingValueReader
1.GetValue(DbDataReader reader, Int32 ordinal) +177 at lambda_method(Closure , Shaper ) +1146 at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator
1.ReadNextElement(Shaper shaper) +384 在 System.Data.Entity.Core.Common.Internal.Materialization.SimpleEnumerator.MoveNext() +88 在 System.Linq.Buffer1..ctor(IEnumerable
1 来源)+284 在 System.Linq.Enumerable.ToArray(IEnumerable`1 源) +90 在 Medici.MediciService.GetMedici(GetMediciQuery 参数)
查询示例:
var medici = _dbContext.Medici
.Select(x => new MediciDto
Id = x.Id,
Nome = x.Nome,
Cognome = x.Cognome,
TipoMedico = new TipoMedico
Codice = x.TipoMedico.Id,
Descrizione = x.TipoMedico.Desc,
)
.ToArray();
这些是类:
public class MediciDto
public int Id get; set;
public string Nome get; set;
public string Cognome get; set;
public TipoMedico TipoMedico get; set;
public class TipoMedico
public int Id get; set;
public string Descrizione get; set;
问题与表Medici
中的外键可以为空,但引用表上的id 不能为空有关。 EF6 的 Devart 提供程序处理此设置的默认值(在本例中为零),而 SQL Server 提供程序运行到 InvalidOperationException
。
因此,总而言之,我们的问题是:有没有办法将与 Oracle Devart Provider 相同的行为配置为实体框架的默认设置?我们无法查看所有查询来处理检查空值。我们没有这么多时间。
【问题讨论】:
这个问题在topic解决了 @EnesKartal 这个解决方案涉及审查所有 EF 查询。这是我们应该做的最后一件事...... 你的ID字段是否可以为空? @EnesKartal 你是说 TipoMedico 的 ID 吗?不,它不能为空。但是在 Medico 中引用的 TipoMedico 的 ID (FK) 可以为空 将您的 ID 字段更改为“int?”类型。因为“int”不是可空类型而是“int?”可以为空。 【参考方案1】:我认为您可能不走运,因为该代码是基于 Oracle 提供程序在投射可空引用方面的特殊性的假设而构建的,而其他提供程序(如 SQL Server)不共享。
如果您最终接受需要重新审视您的预测,我知道有两种选择。要么在投影之前显式添加#null 检查,要么考虑利用 Automapper 来处理 Projections /w ProjectTo
,因为它确实可以处理 #null 引用,另外还有使 Linq 表达式更加紧凑的额外好处。这将涉及定义 Automapper 使用的映射规则。根据您的实体与 DTO 命名约定的一致性和可预测性,Automapper 可以通过约定解决相当多的问题。
.Select(x => new MediciDto
Id = x.Id,
Nome = x.Nome,
Cognome = x.Cognome,
TipoMedico = x.TipoMedico != null
? new TipoMedico
Codice = x.TipoMedico.Id,
Descrizione = x.TipoMedico.Desc,
: null
).ToArray();
或
.ProjectTo<MedicoDTO>(config)
.ToArray();
'config' 是一个 MapperConfiguration
,它可以是单个作用域的依赖项,也可以根据需要声明。如果 DTO 和实体的命名一致,则可以很简单:
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<Medico, MedicoDTO>();
cfg.CreateMap<TipoMedico, TipoMedicoDTO>();
);
但是,在您的示例中,它指的是 MedicoDTO DTO 中的“TipoMedico”,无论是拼写错误还是指缺少后缀的 DTO,或者指的是实体类本身的副本。 (希望不是最后一个,混合实体和 DTO)
当项目需求发生变化时,有时需要进行重构,这可以看作是整理和改进事物的机会。
【讨论】:
以上是关于Entity Framework 6 DB-First:仅针对 SQL Server 发出 InvalidOperationException的主要内容,如果未能解决你的问题,请参考以下文章
添加 [DataContract] 到 Entity Framework 6.0 POCO Template