在插入期间,标记为主键的Set属性不会发送到数据库

时间:2014-12-17 13:22:23

标签: entity-framework entity-framework-6 localdb

我有两个偶尔连接的数据库'数据库中的每个实体都使用Guid作为主键。这样可以更容易地将记录或整个对象图从数据库A添加到数据库B,同时保持正确的外键关系。

但是我现在有两种情况:

  1. 实体是全新的 - >它有Guid 0000-0000 ...等...在这种情况下,我希望数据库(或Ef6)为它计算一个新的Guid,当它存储在数据库中时

  2. 实体最初是在数据库A中创建的,它有一个像3419-5132这样的guid ...在这种情况下我希望它按原样添加到数据库B.

  3. 这种情况可能吗?目前,我已将[DatabaseGenerated(DatabaseGeneratedOption.Computed)]DefaultValueSql = "newid()"的主键归因于迁移/数据库创建中的设置。这导致数据库中的以下定义:

    [TimeSlotId]          UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL
    

    根据this answer,只有在未设置的情况下才应覆盖该值。但是,EF6似乎做了一些时髦的事情,所以每当我添加一个现有Guid的实体时,值仍会改变。

    通过使用DataContext.Database.Log,我能够找到EF6执行的确切SQL查询。您可以清楚地看到主键的现有值未发送到数据库。但是,是从具有生成密钥的特殊表中选择的。

    DECLARE @generated_keys table([TimeSlotId] uniqueidentifier)
    INSERT [dbo].[TimeSlots]([Day], [Hour], [Minute], [Schedule_ScheduleId])
    OUTPUT inserted.[TimeSlotId] INTO @generated_keys
    VALUES (@0, @1, @2, NULL)
    SELECT t.[TimeSlotId]
    FROM @generated_keys AS g JOIN [dbo].[TimeSlots] AS t ON g.[TimeSlotId] = t.[TimeSlotId]
    WHERE @@ROWCOUNT > 0
    -- @0: '5' (Type = Int32)
    
    -- @1: '4' (Type = Int32)
    
    -- @2: '33' (Type = Int32)
    

2 个答案:

答案 0 :(得分:1)

不,您正在寻找的是实体框架不支持的内容。 EF仅支持两种类型的数据库生成列:标识列和计算列。假定在插入新记录时由数据库设置标识列。在插入新记录或修改现有记录时,假定计算列由数据库设置。在这两种情况下,当添加新记录时,EF将确定它不需要将当前值(从您的程序)发送到数据库。

根据您的需求,可能适合您的方法是使用多个不同的上下文类。在其中一个类中,该列将是标识列。在另一个类中,该列不是标识列。您可以根据是否要手动选择新值来选择要使用的上下文。

另一种可能是删除数据库默认值,始终从代码中设置它。 SQL中的newid()与.NET中的Guid.NewGuid()具有相同的效果,因此您可以手动生成GUID。

答案 1 :(得分:0)

因为能够通过创建Query interceptor来拦截尝试将空Guid添加到数据库的所有NonQueries来解决此问题。可以使用DbInterception.Add(...)注册拦截器。

请注意,为此,您需要从密钥中删除[DatabaseGenerated(DatabaseGeneratedOption.Identity)](或.Computed)属性。

public class InsertGuidInterceptor : IDbCommandInterceptor
    {        

        public void NonQueryExecuting(
            DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
            for(int i = 0; i < command.Parameters.Count; i++)
            {
                var parameter = command.Parameters[i];                
                if(parameter.Value.Equals(Guid.Empty))
                {
                    //command.Parameters.Insert(i, Guid.NewGuid());
                    parameter.Value = Guid.NewGuid();
                }
            }
        }

        // Snip //
}