Question
是否可以使用流畅的语法或属性在属性上定义唯一约束?如果没有,有哪些解决方法?
我有一个带有主键的用户类,但我想确保电子邮件地址也是唯一的 . 这是否可以直接编辑数据库?
Solution (based on Matt's answer)
public class MyContext : DbContext {
public DbSet<User> Users { get; set; }
public override int SaveChanges() {
foreach (var item in ChangeTracker.Entries<IModel>())
item.Entity.Modified = DateTime.Now;
return base.SaveChanges();
}
public class Initializer : IDatabaseInitializer<MyContext> {
public void InitializeDatabase(MyContext context) {
if (context.Database.Exists() && !context.Database.CompatibleWithModel(false))
context.Database.Delete();
if (!context.Database.Exists()) {
context.Database.Create();
context.Database.ExecuteSqlCommand("alter table Users add constraint UniqueUserEmail unique (Email)");
}
}
}
}
19 回答
据我所知,只有一个问题是独特的约束......你可能想要创建索引,检查约束,以及可能的触发器和其他构造 . Here's a simple pattern you can use使用代码优先设置,但不可否认它不是数据库不可知的:
另一种选择是,如果您的域模型是在数据库中插入/更新数据的唯一方法,您可以自己实现唯一性要求并将数据库从中删除 . 这是一个更便携的解决方案,迫使您在代码中清楚地了解业务规则,但会使数据库处于打开无效数据的状态 .
从EF 6.1开始,现在可以:
严格来说,这将为您提供唯一的索引,而不是唯一约束 . 对于大多数实际用途they are the same .
与此无关,但在某些情况下可能会有所帮助 .
如果您要创建一个唯一的复合索引,我们说2列将作为您的表的约束,那么从版本4.3开始,您可以使用新的迁移机制来实现它:
http://msdn.microsoft.com/en-us/library/hh770484(v=vs.103).aspx
http://blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx
基本上你需要在你的一个迁移脚本中插入这样的调用:
像这样的东西:
在创建数据库时,我做了一个完整的hack来执行SQL . 我创建自己的DatabaseInitializer并从其中一个提供的初始化程序继承 .
这是我在SQL语句中找到的唯一可以找到的地方 .
这是来自CTP4 . 我不知道它在CTP5中是如何工作的 .
只是试图找出是否有办法做到这一点,只有我发现到目前为止自己执行的方式,我创建了一个属性,要添加到每个类,您需要提供您需要唯一的字段的名称:
然后在我的课上我会添加它:
最后,我将在我的存储库中添加一个方法,在Add方法中或者像这样保存更改时:
不太好,因为我们需要依靠反射,但到目前为止这是适合我的方法! = d
同样在6.1中你可以使用@ mihkelmuur的流利语法版本的答案,如下所示:
流畅的方法不是完美的IMO,但至少现在可能 .
关于亚瑟维克斯博客的更多信息http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/
使用EF5 Code First Migrations在visual basic中的一种简单方法
公共类样本
结束班
MaxLength属性对于字符串类型的唯一索引非常重要
运行cmd:update-database -verbose
运行cmd后:add-migration 1
在生成的文件中
类似于Tobias Schittkowski的回答,但是C#并且有能力在constrtaints中有多个字段 .
要使用此功能,只需在您希望独特的任何字段上放置[唯一]即可 . 对于字符串,您必须执行类似的操作(请注意MaxLength属性):
因为默认字符串字段是nvarchar(max),并且密钥中不允许这样做 .
对于约束中的多个字段,您可以:
首先,UniqueAttribute:
然后,包含一个有用的扩展来从类型中获取数据库表名:
然后,数据库初始化程序:
如果重写DbContext类中的ValidateEntity方法,也可以将逻辑放在那里 . 这里的优点是您可以完全访问所有DbSet . 这是一个例子:
我通过反思解决了这个问题(抱歉,伙计,VB.Net ......)
首先,定义一个属性UniqueAttribute:
然后,像你一样增强你的模型
最后,创建一个自定义的DatabaseInitializer(在我的版本中,我只在调试模式下重新创建数据库更改...) . 在此DatabaseInitializer中,将根据Unique-Attributes自动创建索引:
也许这有助于......
如果你正在使用EF5并且仍然有这个问题,下面的解决方案为我解决了它 .
我使用代码第一种方法,因此:
在迁移脚本中完成了这项工作 . 它还允许NULL值!
使用EF代码第一种方法,可以使用以下技术实现基于属性的唯一约束支持 .
创建标记属性
标记您希望在实体上唯一的属性,例如
创建数据库初始化程序或使用现有数据库初始化程序创建唯一约束
设置数据库上下文以在启动代码中使用此初始化程序(例如,在
main()
或Application_Start()
中)解决方案类似于mheyman,简化了不支持复合键的方法 . 与EF 5.0一起使用 .
我今天遇到了这个问题,最后我能够解决它 . 我不知道是否是正确的方法,但至少我可以坚持下去:
使用唯一的属性验证器 .
ValidateEntity
未在同一数据库事务中调用 . 因此,数据库中可能存在与其他实体的竞争条件 . 你必须在某种程度上破解EF以强制围绕SaveChanges
(因此,ValidateEntity
)进行交易 .DBContext
无法直接打开连接,但ObjectContext
可以 .由于没有内置注释,我想出了一个解决方法 . 请参阅此链接了解更多信息https://stackoverflow.com/a/16496291/1873113
根据http://blogs.msdn.com/b/adonet/archive/2014/02/11/ef-6-1-0-beta-1-available.aspx,EF 6.1将有一个IndexAttribute来帮助我们 .
在阅读了这个问题后,我在尝试实现属性指定属性的过程中有了自己的问题,如Mihkel Müür's,Tobias Schittkowski's和mheyman's答案提示:Map Entity Framework code properties to database columns (CSpace to SSpace)
我终于得出了这个答案,它可以将标量和导航属性映射到数据库列,并在属性上指定的特定序列中创建唯一索引 . 此代码假定您已实现具有Sequence属性的UniqueAttribute,并将其应用于EF实体类属性,该属性应表示实体的唯一键(主键除外) .
Note: 此代码依赖于EF版本6.1(或更高版本),它暴露
EntityContainerMapping
在以前的版本中不可用 .对于使用代码优先配置的用户,您还可以将IndexAttribute对象用作ColumnAnnotation,并将其IsUnique属性设置为true .
例如:
这将在Name列上创建名为IX_name的唯一索引 .
对不起,迟到的答案,但我觉得很高兴与你共事
I have posted about this at code project
通常,它取决于您在类上生成唯一索引的属性