kim*_*3er 125 entity-framework unique-constraint code-first
题
是否可以使用流畅的语法或属性在属性上定义唯一约束?如果没有,有哪些解决方法?
我有一个带有主键的用户类,但我想确保电子邮件地址也是唯一的.这是否可以直接编辑数据库?
解决方案(基于马特的答案)
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)");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
mat*_*mc3 61
据我所知,目前没有办法用Entity Framework做到这一点.但是,这不仅仅是一个独特约束的问题......您可能还想创建索引,检查约束,以及可能的触发器和其他构造. 这是一个简单的模式,您可以使用代码优先设置,但不可否认,它不是数据库不可知的:
public class MyRepository : DbContext {
public DbSet<Whatever> Whatevers { get; set; }
public class Initializer : IDatabaseInitializer<MyRepository> {
public void InitializeDatabase(MyRepository context) {
if (!context.Database.Exists() || !context.Database.ModelMatchesDatabase()) {
context.Database.DeleteIfExists();
context.Database.Create();
context.ObjectContext.ExecuteStoreCommand("CREATE UNIQUE CONSTRAINT...");
context.ObjectContext.ExecuteStoreCommand("CREATE INDEX...");
context.ObjectContext.ExecuteStoreCommand("ETC...");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是,如果您的域模型是在数据库中插入/更新数据的唯一方法,您可以自己实现唯一性要求并将数据库从中删除.这是一个更便携的解决方案,迫使您在代码中清楚地了解业务规则,但会使数据库处于打开无效数据的状态.
Mih*_*üür 44
从EF 6.1开始,现在可以:
[Index(IsUnique = true)]
public string EmailAddress { get; set; }
Run Code Online (Sandbox Code Playgroud)
严格来说,这将为您提供唯一的索引,而不是唯一约束.对于大多数实际目的,它们是相同的.
lna*_*aie 28
与此无关,但在某些情况下可能会有所帮助.
如果您要创建一个唯一的复合索引,我们说2列将作为您的表的约束,那么从版本4.3开始,您可以使用新的迁移机制来实现它:
基本上你需要在你的一个迁移脚本中插入这样的调用:
CreateIndex("TableName", new string[2] { "Column1", "Column2" }, true, "IX_UniqueColumn1AndColumn2");
Run Code Online (Sandbox Code Playgroud)
像这样的东西:
namespace Sample.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class TableName_SetUniqueCompositeIndex : DbMigration
{
public override void Up()
{
CreateIndex("TableName", new[] { "Column1", "Column2" }, true, "IX_UniqueColumn1AndColumn2");
}
public override void Down()
{
DropIndex("TableName", new[] { "Column1", "Column2" });
}
}
}
Run Code Online (Sandbox Code Playgroud)
在创建数据库时,我做了一个完整的hack来执行SQL.我创建自己的DatabaseInitializer并从其中一个提供的初始化程序继承.
public class MyDatabaseInitializer : RecreateDatabaseIfModelChanges<MyDbContext>
{
protected override void Seed(MyDbContext context)
{
base.Seed(context);
context.Database.Connection.StateChange += new StateChangeEventHandler(Connection_StateChange);
}
void Connection_StateChange(object sender, StateChangeEventArgs e)
{
DbConnection cnn = sender as DbConnection;
if (e.CurrentState == ConnectionState.Open)
{
// execute SQL to create indexes and such
}
cnn.StateChange -= Connection_StateChange;
}
}
Run Code Online (Sandbox Code Playgroud)
这是我在SQL语句中找到的唯一可以找到的地方.
这是来自CTP4.我不知道它在CTP5中是如何工作的.
小智 5
只是试图找出是否有办法做到这一点,只有我发现到目前为止自己执行的方式,我创建了一个属性,要添加到每个类,您需要提供您需要唯一的字段的名称:
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=false,Inherited=true)]
public class UniqueAttribute:System.Attribute
{
private string[] _atts;
public string[] KeyFields
{
get
{
return _atts;
}
}
public UniqueAttribute(string keyFields)
{
this._atts = keyFields.Split(new char[]{','}, StringSplitOptions.RemoveEmptyEntries);
}
}
Run Code Online (Sandbox Code Playgroud)
然后在我的课上我会添加它:
[CustomAttributes.Unique("Name")]
public class Item: BasePOCO
{
public string Name{get;set;}
[StringLength(250)]
public string Description { get; set; }
[Required]
public String Category { get; set; }
[Required]
public string UOM { get; set; }
[Required]
}
Run Code Online (Sandbox Code Playgroud)
最后,我将在我的存储库中添加一个方法,在Add方法中或者像这样保存更改时:
private void ValidateDuplicatedKeys(T entity)
{
var atts = typeof(T).GetCustomAttributes(typeof(UniqueAttribute), true);
if (atts == null || atts.Count() < 1)
{
return;
}
foreach (var att in atts)
{
UniqueAttribute uniqueAtt = (UniqueAttribute)att;
var newkeyValues = from pi in entity.GetType().GetProperties()
join k in uniqueAtt.KeyFields on pi.Name equals k
select new { KeyField = k, Value = pi.GetValue(entity, null).ToString() };
foreach (var item in _objectSet)
{
var keyValues = from pi in item.GetType().GetProperties()
join k in uniqueAtt.KeyFields on pi.Name equals k
select new { KeyField = k, Value = pi.GetValue(item, null).ToString() };
var exists = keyValues.SequenceEqual(newkeyValues);
if (exists)
{
throw new System.Exception("Duplicated Entry found");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
不太好,因为我们需要依靠反射,但到目前为止这是适合我的方法!= d
同样在6.1中你可以使用@ mihkelmuur的流利语法版本的答案,如下所示:
Property(s => s.EmailAddress).HasColumnAnnotation(IndexAnnotation.AnnotationName,
new IndexAnnotation(
new IndexAttribute("IX_UniqueEmail") { IsUnique = true }));
Run Code Online (Sandbox Code Playgroud)
流畅的方法不是完美的IMO,但至少现在可能.
关于亚瑟维克斯博客的更多信息http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/
| 归档时间: |
|
| 查看次数: |
58909 次 |
| 最近记录: |