SQL Server Compact不支持服务器生成的键和服务器生成的值。

35

我刚开始使用实体框架,所以决定将其连接到我的现有 SQL Server CE 数据库。我有一个带有 IDENTITY(1,1) 主键的表,但是当我尝试添加实体时,我遇到了上述错误。

据我从微软 Technet 文章中学到,

当使用 Entity Framework 时,SQL Server Compact 不支持具有服务器生成键或值的实体。 实体的键可以被标记为服务器生成,这使得数据库可以在插入或创建实体时为键生成一个值。此外,一个实体的零个或多个属性可以被标记为服务器生成的值。有关更多信息,请参见 Entity Framework 文档中的 Store Generated Pattern 主题。 当与 Entity Framework 一起使用时,SQL Server Compact 不支持具有服务器生成键或值的实体,尽管 Entity Framework 允许您定义具有服务器生成键或值的实体类型。对具有服务器生成值的实体进行数据操作会抛出“不支持”的异常。

现在我有几个问题:

  • 如果服务器不支持并会抛出异常,为什么要将键标记为服务器生成?从引用段落中很难理解。
  • 当我尝试将 StoreGeneratedPattern="Identity" 添加到我的实体属性时,Visual Studio 报错说不允许。我做错了什么?
  • 这个限制的最佳解决方法是什么(包括切换到另一个数据库)?我的限制是零安装和使用实体框架。

2
NHibernate在同样的情况下运行良好,顺便说一句。 - Sergey Aldoukhov
1
应该是 StoreGeneratedPattern="None" - Mikhail Poda
7个回答

24

当我遇到这个限制时,我将类型更改为唯一标识符(uniqueidentifier)


奇怪,它似乎对我不起作用。将列类型更改为uniquidentifier,刷新模型,无论StoreGeneratedPattern = None还是Identity都会失败。 - arviman
1
如果您正在使用VS2010,则可能需要删除模型,然后从数据库刷新模型。一位同事帮助我解决了这个问题。虽然仍然很愚蠢,但至少它可以正常工作了。但是这可能会破坏您已有的链接... - teynon

17

使用uniqueidentifier或手动生成bigint/int键值是您的最佳选择。

也许像这样...

    private static object lockObject = new object();

    private static long nextID = -1;

    public static long GetNextID()
    {
        lock (lockObject)
        {
            if (nextID == -1) nextID = DateTime.UtcNow.Ticks; else nextID++;
            return nextID;
        }
    }

这假设在应用程序运行期间不会生成超过一个记录(加上停止和重新启动的时间)。我认为这是一个合理的假设,但如果你想要一个完全可靠(但更复杂)的解决方案,请阅读数据库中的最高ID并从那里递增。


我会选择读取最大ID + 1的解决方案,避免使用复杂的构造或GUID。 - Jeroen
所提出的“防弹”解决方案甚至都不正确,更别说防弹了。其他进程(即使是在 Web Farm 上下文中使用相同的代码)也不会尊重锁定。只有一个地方是有意义进行同步的,那就是在数据库本身中。当微软决定不支持这一点时,我不知道他们在想什么。到目前为止,实体框架并没有给我留下深刻印象。 - The Dag

6

SQL CE 4.0版本通过其实体框架提供程序解决了这个问题。


1
这是什么,该如何安装?我通过谷歌搜索无法找到任何相关信息。 - Kyeotic
1
@Tyrsius,希望你现在已经找到了,不过这是链接:http://blogs.msdn.com/b/sqlservercompact/archive/2011/01/12/microsoft-sql-server-compact-4-0-is-available-for-download.aspx - Nelson Reis
@NelsonReis 我已经看过了,但还是谢谢你的跟进。我相信有人会觉得这很有用。 - Kyeotic
3
我不同意链接帖子的观点。现在是2013年,我正在使用VS2012中的EF5,在保存更改时添加实体时,唯一标识符(无论作为ROWGUID还是其他类型)都无法正常工作,除非EF用于SqlCompact的NuGets已经过时。会抛出异常:The column 'DeviceId' has type 'SqlServerCe.uniqueidentifier', which is not a valid type for an identity column. - timmi4sa
即使使用Entity Framework,这个问题仍然没有解决。我正在使用版本6.2.0的EntityFramework.SqlServerCompact。我仍然会收到这个错误:只有标识列支持服务器生成的键。列“ID”的类型为“SqlServerCe.uniqueidentifier”,这不是标识列的有效类型。 - sky91

4
在我的情况下,我所有的类都将主键命名为“ID”。
我创建了一个接口。
public class IID
{
    public Int32 ID { get; set; }
}

然后我创建了一个扩展方法

public static Int32 GetNextID<T>(this ObjectSet<T> objects)
    where T : class, IID
    {
        T entry = objects.OrderByDescending(u => u.ID).FirstOrDefault();
        if (entry == default(T))
            return 1;
        return entry.ID + 1;
    }

然后当我需要一个新的ID时,我只需这样做:
MyObject myobj = new MyObject();
myobj.ID = entities.MyTable.GetNextID();

2
在多个并发操作的情况下,这种方法是行不通的。如果两个操作在保存记录之前都调用了 GetNextID,它们将会拥有相同的 ID。 - Ladislav Mrnka
如果GetNextID方法将select语句包装在一个ReadUncommited事务中会怎样? - BenCr

4
我也遇到了这个问题... mostlytech 的答案可能是最好的选择,GUID 非常容易使用,键冲突的风险非常低(虽然不是不存在)。
为什么如果不支持会抛出异常,你会将键标记为服务器生成的?从引用段落中很难理解它的含义。
因为 SQL Server(不是 Compact 版本)支持它,其他第三方也可能支持它...Entity Framework 不仅适用于 SQL Server Compact ;)

2
GUIDs虽然可以用作主键和索引,但由于它们不是连续的,因此效果很差。最好使用bigint/long并使用基于时间的值(类似于hightechrider的方法)。 - mlibby

1

另一个选择是在具有标识列的表上使用SqlCeResultSet。


0

我有一个名为ID的主键,数据类型为INT32,并且具有自增列

只需要这样做

MyEntity Entity = new MyEntity();

String Command;

command = "Insert into Message(Created,Message,MsgType)values('12/1/2014','Hello World',5); Entity.ExecuteStoreCommand(command);

--在插入语句中排除主键

--因为SQLCE不支持系统生成的键

--不要使用LINQ,因为它为数据类型为INT的主键提供默认值0


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接