可惜,不,你不能那样做。ComplexType 看起来很有希望,并且似乎是一个不错的“列组”——但 EF 团队只是将其设计为完全用于其他用途,并且与将其用作“列组”相关的大量功能完全缺失。
据我所知,“显然我可以做到以下几点:”是唯一可能的方法。您只需要在每个子列的基础上定义分组,或者通过投影到数据库将知道如何比较的某种类型。
核心问题是 EF 团队不将 ComplexType 视为一组列,这些列允许您使用许多字段来整理您的实体,但是,对于他们来说,它是另一种类型的实体,一种短暂的实体. ComplexType 旨在表示例如从存储过程(或表返回函数)返回的对象,该存储过程返回无法映射到任何正常现有表/类的 5 列结果集。例如,假设您有一个包含 50 个列/字段的表/类 Customer,其中大多数不为空并表示一些敏感数据。您创建了一个存储过程,它获取客户的 ID 并返回其{姓名、地址、联系电话、鞋码}。显然,拥有这样的修剪结果集,您无法将其解码/映射到具有大量不能为空的字段的 Customer 类。那么如何在 EF 中表示结果集?
第一个选项,相当蹩脚的一个,是简单地忽略 EF 并直接与数据库对话并手动读取/翻译/解压缩行/列。好吧,我们知道 SqlClient,我们可以做到这一点。
第二个选项,丑陋的一个,是在 EF 中定义一个存根表/视图,例如 CustomerView,它只包含 {name、address、contact phone、shoe size} 列,并将存储过程结果集映射到它。它会很好地工作,但如果你不小心从 EF 模型“生成数据库”,你也会得到那个额外的未使用的表。
救援的第三个选项是 ComplexType。无需尝试创建影子表或视图,您只需告诉 EF “有一些额外的数据类型”永远不会映射到自己的表,也永远不会定义 PrimaryKey,EF 将为您方便将其映射到与任何其他数据对象一样的类。现在,有了这样的类型,您可以从过程中返回它并将其作为类型良好的对象检索,您可以将其作为过程的参数传递等等。但是:您不能单独将其持久化。它没有自己的表映射,也没有自己的主键。它没有身份。
这很好,但是,为什么要从过程中返回这样的数据类型呢?可能该过程处理或生成了一些数据,并且您希望将它们存储在某个表中。你还没有定义一个完整的普通实体,所以那个东西只是一个“数据包”,所以你可能会将该结果值与其他数据一起写入该表。这就是为什么 EF 允许您将 ComplexType 映射到表的某些列:如果您从过程中获得了该临时结果对象,并且现在您想将其写入某处,则不必逐列手动进行,您只需将其映射到客户的“姓名、地址、联系电话、鞋码”列。
最重要的一点是,数据库服务器永远不知道存在这样的 ComplexType。正如我所说,它没有身份。当您尝试执行 LINQ 查询时
aset.Where(item => item.complexProperty == complexValue)
那么,complexValue 是一个没有映射到任何表的 CLR 对象,没有标识,因此没有 PK,因此 EF 完全没有想法如何检查“左手对象”是否与“右手对象”相同。如果对象定义了一些身份,它可以在服务器端检查 PK,或者在客户端进行对象引用比较,但是在这里 - 它只是失败了。
我完全同意这是 EF 中缺少的另一个该死的有用功能。我相信 EF 团队可以非常容易地逐列比较 ComplexType,但他们没有。他们根本没想到 ComplexTypes 可以用来整理表格。他们的意思是它是一个瞬态数据包,可以传递给存储过程和函数。可惜。