一位朋友报告了计算列,实体框架和Breeze的问题
我们有一个表,其中包含由数据库计算的“FullName”列 . 在创建新Person时,Breeze会将FullName属性值发送到服务器,即使它根本没有设置,并且在尝试插入新的Person实例时会触发错误 . 数据库抛出此异常:
The column "FullName" cannot be modified because it is either a computed column or is the result of a UNION operator.
以下是SQL表定义的相关部分:
CREATE TABLE [dbo].[Person](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[FirstName] [varchar](100) NULL,
[MiddleName] [varchar](100) NULL,
[LastName] [varchar](100) NOT NULL,
[FullName] AS ((([Patient].[LastName]+',') + isnull(' '+[Patient].[FirstName],'')) + isnull(' '+[Patient].[MiddleName],'')),
...
我的朋友告诉我相应的“Code First”类看起来像这样:
public class Person {
public int ID {get; set;}
public string FirstName {get; set;}
public string MiddleName {get; set;}
public string LastName {get; set;}
public string FullName {get; set;}
...
}
这个问题的答案解释了问题并提供了解决方案 .
1 回答
设计问题
看着这个的每个人都想知道为什么有一个
FullName
的计算列,其次,为什么这个属性暴露给客户端 .让我们假设计算列有一个很好的理由,这是模型从表中获取值而不是计算值本身的一个很好的理由,并且有充分的理由将它发送到客户端而不是让客户端计算它 . 以下是他告诉我的事情;
生活有时会以这种方式运作 .
后果
请注意,
FullName
属性具有公共setter .Person
类的EF元数据生成器无法判断这是否为只读属性 .FullName
看起来就像LastName
. 元数据说"this is normal read/write property."微风也没有看到任何区别 . 客户端应用程序可能不会触及此属性,但Breeze必须在创建新的
Person
时为其发送值 . 回到服务器上,BreezeEFContextProvider
认为它应该在创建EF实体时传递该值 . 舞台是为灾难而设的 .如果(a)你可以改变模型的
FullName
属性定义,你能做什么?解决方案
EF需要你的帮助 . 你应该告诉EF这实际上是一个数据库计算属性 . 您可以使用EF流利界面或使用如下所示的属性:
添加此属性,EF知道此属性是只读的 . 它将生成适当的元数据,您可以干净地保存新的
Person
. 省略它,你会得到例外 .请注意,这仅适用于Code First . 如果他生成模型Database First,则EF知道该列已计算,并且不会尝试设置该列 .
请注意存储生成密钥的类似问题 . 整数键的默认值为"store-generated",但Guid键的默认值为"client generated" . 如果在您的表中,数据库实际设置了Guid,则必须使用[DatabaseGenerated(DatabaseGeneratedOption.Identity)]标记
ID
属性