Mik*_*ion 22 c# sql-server entity-framework default-constraint
我是EF的新手(这是我的第一周),但对数据库或编程并不陌生.其他人也提出了类似的问题,但我觉得它没有被问到正确的细节,或者解释得非常需要解释,所以我走了.
问题: 如何让Entity Framework正确处理数据库中执行INSERT时定义了DEFAULT CONSTRAINT的列?这意味着,如果我在插入操作期间没有在模型中提供值,如何让EF从其生成的TSQL INSERT命令中排除该列,以便数据库定义的DEFAULT CONSTRAINT可以工作?
背景
我有一个我创建的简单表,只是为了测试Entity Framework 6(EF6)及其与SQL Server能够更新的列的交互.这使用了IDENTITY,TIMESTAMP,COMPUTED和一些应用了DEFAULT CONSTRAINT的列.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[DBUpdateTest](
[RowID] [int] IDENTITY(200,1) NOT NULL,
[UserValue] [int] NOT NULL,
[DefValue1] [int] NOT NULL,
[DefValue2null] [int] NULL,
[DefSecond] [int] NOT NULL,
[CalcValue] AS
(((([rowid]+[uservalue])+[defvalue1])+[defvalue2null])*[defsecond]),
[RowTimestamp] [timestamp] NULL,
CONSTRAINT [PK_DBUpdateTest] PRIMARY KEY CLUSTERED
(
[RowID] ASC
)
WITH
(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
GO
ALTER TABLE [dbo].[DBUpdateTest]
ADD CONSTRAINT [DF_DBUpdateTest_DefValue1]
DEFAULT ((200)) FOR [DefValue1]
GO
ALTER TABLE [dbo].[DBUpdateTest]
ADD CONSTRAINT [DF_DBUpdateTest_DefValue2null]
DEFAULT ((30)) FOR [DefValue2null]
GO
ALTER TABLE [dbo].[DBUpdateTest]
ADD CONSTRAINT [DF_DBUpdateTest_DefSecond]
DEFAULT (datepart(second,getdate())) FOR [DefSecond]
GO
Run Code Online (Sandbox Code Playgroud)
EF6完美地处理IDENTITY,TIMESTAMP和COMPUTED列,这意味着在INSERT或UPDATE(通过context.SaveChanges()
)之后,EF将新值读回实体对象以供立即使用.
但是,对于具有DEFAULT CONSTRAINT的列,不会发生这种情况.从我所知道的,这是因为当EF生成TSQL来执行INSERT时,它为可空或不可空类型提供公共默认值,就好像该列没有定义DEFAULT CONSTRAINT一样.所以很明显,EF完全忽略了DEFAULT CONSTRAINT的可能性.
这是我的插入DBUpdateTest记录的EF代码(我只更新一个列):
DBUpdateTest myVal = new DBUpdateTest();
myVal.UserValue = RND.Next(20, 90);
DB.DBUpdateTests.Add(myVal);
DB.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
这是在INSERT到DBUpdateTest期间EF生成的SQL(它尽职尽责地更新所有可能的列):
exec sp_executesql
N'INSERT [dbo].[DBUpdateTest]([UserValue], [DefValue1], [DefValue2null],
[DefSecond])
VALUES (@0, @1, NULL, @2)
SELECT [RowID], [CalcValue], [RowTimestamp]
FROM [dbo].[DBUpdateTest]
WHERE @@ROWCOUNT > 0 AND [RowID] = scope_identity()',
N'@0 int,@1 int,@2 int',@0=86,@1=0,@2=54
Run Code Online (Sandbox Code Playgroud)
请注意,它非常清楚地提供了INT NOT NULL(0)和INT NULL(null)的默认值,它完全克服了DEFAULT CONSTRAINT.
这是执行EF INSERT命令时发生的情况,它为可空列提供NULL,为INT列提供ZERO
RowID UserValue DefValue1 DefValue2null DefSecond CalcValue
=========================================================================
211 100 200 NULL 0 NULL
Run Code Online (Sandbox Code Playgroud)
另一方面,如果我执行此声明:
insert into DBUpdateTest (UserValue) values (100)
Run Code Online (Sandbox Code Playgroud)
我会得到一个这样的记录
RowID UserValue DefValue1 DefValue2null DefSecond CalcValue
=========================================================================
211 100 200 30 7 3787
Run Code Online (Sandbox Code Playgroud)
由于一个原因,这可以正常工作:TSQL INSERT命令没有为具有已定义的DEFAULT CONSTRAINT的任何列提供值.
因此,我想要做的是让EF从INSERT TSQL中排除DEFAULT CONSTRAINT列,如果我没有在模型对象中明确设置它们的值.
我已经尝试过的事情
1.认识默认约束? SO:如何让EF处理默认约束
在OnModelCreating()
我的DbContext
类的方法中,我建议我告诉EF,DEFAULT CONSTRAINT的列是COMPUTED字段,而不是.但是,我想知道在INSERT之后是否会让EF至少读取值(更别提它也可能使我无法为该列赋值,这只是我做的更多)不想):
modelBuilder.Entity<DBUpdateTest>()
.Property(e => e.DefValue1)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
Run Code Online (Sandbox Code Playgroud)
这不起作用,事实上似乎没有什么不同(ED:实际上它确实有效,见第2点).EF仍然生成相同的TSQL,为列提供默认值并在此过程中破坏数据库.
是否有我缺少的标志,我忘记设置的配置项,我可以使用的函数属性,我可以创建一些继承的类代码,以使EF"正确处理DEFAULT CONSTRAINT列?"
2.获取OnModelCreating()来执行? SO:OnModelCreating未调用
Janesh(下面)向我表明EF 将消除从其产生TSQL INSERT命令参数如果列标有DatabaseGeneratedOption.Computed
.它只是不适合我,因为显然我使用了错误的连接字符串(!!!).
这是我的App.config,这是<connectionStrings>
我显示"坏"和"好"连接字符串的部分:
<connectionStrings>
<add name="TEST_EF6Entities_NO_WORKY" providerName="System.Data.EntityClient" connectionString="metadata=res://*/TCXModel.csdl|res://*/TCXModel.ssdl|res://*/TCXModel.msl;provider=System.Data.SqlClient;provider connection string="data source=...ConnectStringHere...;App=EntityFramework"" />
<add name="TEST_EF6Entities_IT_WORKS" providerName="System.Data.SqlClient" connectionString="data source=...ConnectStringHere...;App=EntityFramework;" />
</connectionStrings>
Run Code Online (Sandbox Code Playgroud)
不同的是:该作品人使用的ProviderName的System.Data.SqlClient
,一个不工作的用途System.Data.EntityClient
.显然,SqlClient提供程序允许OnModelCreating()
调用该方法,这允许我使用它DatabaseGeneratedOption.Computed
来产生效果.
==========没有解决==========
列上的DEFAULT CONSTRAINTS的目的是允许我提供(或不提供)值,并且最终仍然在数据库端获得有效值.我不必知道SQL Server正在这样做,也不必知道默认值是什么或应该是什么.这完全超出了我的控制或知识.
关键是,我可以选择不提供价值.我可以提供它,或者我可能无法提供它,如果需要,我可以为每个INSERT执行不同的操作.
DatabaseGeneratedOption.Computed
对于这种情况,使用实际上不是一个有效的选项,因为它强制选择:"你总是可以提供一个值(因此永远不会利用数据库默认机制),或者你永远不能提供一个值(因此总是利用数据库默认机制) )".
此外,该选项显然仅用于实际的计算列,而不是用于具有DEFAULT CONSTRAINT的列,因为一旦应用,模型属性实际上变为READ-ONLY用于INSERT和UPDATE - 因为这是一个真正的计算列会工作.显然,这阻碍了我选择提供或不提供数据库的价值.
所以,我仍然会问:如何让EF正确地使用定义了DEFAULT CONSTRAINT的数据库列?
这个位是你问题的关键:
因此,我要做的是,如果我没有在对象中明确设置它们的值,那么EF NOT不会在其INSERT TSQL中包含DEFAULT CONSTRAINT列.
实体框架不会为您做到这一点.字段要么始终计算,要么始终包含在插入和更新中.但是你可以编写类以按照你描述的方式运行.您必须将字段(显式)设置为构造函数中的默认值,或使用支持字段.
public class DBUpdateTest
/* public partial class DBUpdateTest*/ //version for database first
{
private _DefValue1 = 200;
private _DefValue2 = 30;
public DbUpdateTest()
{
DefSecond = DateTime.Second;
}
public DefSecond { get; set; }
public DefValue1
{
get { return _DefValue1; }
set { _DefValue1 = value; }
}
public DefValue2
{
get { return _DefValue2; }
set { _DefValue2 = value; }
}
}
Run Code Online (Sandbox Code Playgroud)
如果你总是使用这些类插入,那么你可能不需要在数据库中设置默认值,但如果你从其他地方使用sql插入,那么你也必须将默认约束添加到数据库中
归档时间: |
|
查看次数: |
7957 次 |
最近记录: |