Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 先使用EF代码的唯一键_C#_Ef Code First_Entity Framework 4.1_Unique Key - Fatal编程技术网

C# 先使用EF代码的唯一键

C# 先使用EF代码的唯一键,c#,ef-code-first,entity-framework-4.1,unique-key,C#,Ef Code First,Entity Framework 4.1,Unique Key,我的项目中有以下模型 public class Category { public Guid ID { get; set; } [Required(ErrorMessage = "Title cannot be empty")] public string Title { get; set; } } 我试图将标题作为唯一键,我在谷歌上搜索了解决方案,但没有找到任何解决方案。 您能给我一些建议吗?不幸的是,您不能首先将其定义为代码中的唯一键,因为EF根本不支持唯一键

我的项目中有以下模型

public class Category
{   
    public Guid ID { get; set; }
    [Required(ErrorMessage = "Title cannot be empty")]
    public string Title { get; set; }
}
我试图将
标题
作为唯一键,我在谷歌上搜索了解决方案,但没有找到任何解决方案。
您能给我一些建议吗?

不幸的是,您不能首先将其定义为代码中的唯一键,因为EF根本不支持唯一键(希望计划在下一个主要版本中使用)。您可以通过调用SQL命令创建自定义数据库初始化器并手动添加唯一索引:

public class MyInitializer : CreateDatabaseIfNotExists<MyContext>
{
  protected override void Seed(MyContext context)
  {
    context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_Category_Title ON Categories (Title)");
  }
}

这是VB.Net版本-请注意泛型的实现在类级别上有点不同

Public Class MyInitializer(Of T As DbContext)
    Inherits CreateDatabaseIfNotExists(Of T)
    Protected Overrides Sub Seed(context As T)
        context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_Category_Title ON Categories (Title)")
    End Sub
End Class

首先创建自定义属性类:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class UniqueAttribute : ValidationAttribute
{
   public override Boolean IsValid(Object value)
    {
        // constraint implemented on database
        return true;
    }
}
然后添加到您的类中:

public class Email
{
    [Key]
    public int EmailID { get; set; }

    public int PersonId { get; set; }

    [Unique]
    [Required]
    [MaxLength(100)]
    public string EmailAddress { get; set; }
    public virtual bool IsDefault { get; set; }
    public virtual Boolean IsApprovedForLogin { get; set; }
    public virtual String ConfirmationToken { get; set; }

    [ForeignKey("PersonId")]
    public virtual Person Person { get; set; }
}
然后在DbContext上添加初始值设定项:

public class Initializer : IDatabaseInitializer<myEntities>
{
    public void InitializeDatabase(myEntities context)
    {
        if (System.Diagnostics.Debugger.IsAttached && context.Database.Exists() && !context.Database.CompatibleWithModel(false))
        {
            context.Database.Delete();
        }

        if (!context.Database.Exists())
        {
            context.Database.Create();

            var contextObject = context as System.Object;
            var contextType = contextObject.GetType();
            var properties = contextType.GetProperties();
            System.Type t = null;
            string tableName = null;
            string fieldName = null;
            foreach (var pi in properties)
            {
                if (pi.PropertyType.IsGenericType && pi.PropertyType.Name.Contains("DbSet"))
                {
                    t = pi.PropertyType.GetGenericArguments()[0];

                    var mytableName = t.GetCustomAttributes(typeof(TableAttribute), true);
                    if (mytableName.Length > 0)
                    {
                        TableAttribute mytable = mytableName[0] as TableAttribute;
                        tableName = mytable.Name;
                    }
                    else
                    {
                        tableName = pi.Name;
                    }

                    foreach (var piEntity in t.GetProperties())
                    {
                        if (piEntity.GetCustomAttributes(typeof(UniqueAttribute), true).Length > 0)
                        {
                            fieldName = piEntity.Name;
                            context.Database.ExecuteSqlCommand("ALTER TABLE " + tableName + " ADD CONSTRAINT con_Unique_" + tableName + "_" + fieldName + " UNIQUE (" + fieldName + ")");
                        }
                    }
                }
            }
        }
    }
}
公共类初始值设定项:IDatabaseInitializer
{
public void InitializeDatabase(myEntities上下文)
{
if(System.Diagnostics.Debugger.IsAttached&&context.Database.Exists()&&&context.Database.CompatibleWithModel(false))
{
context.Database.Delete();
}
如果(!context.Database.Exists())
{
context.Database.Create();
var contextObject=作为System.Object的上下文;
var contextType=contextObject.GetType();
var properties=contextType.GetProperties();
系统类型t=null;
字符串tableName=null;
字符串fieldName=null;
foreach(属性中的var pi)
{
if(pi.PropertyType.IsGenericType&&pi.PropertyType.Name.Contains(“DbSet”))
{
t=pi.PropertyType.GetGenericArguments()[0];
var mytableName=t.GetCustomAttributes(typeof(TableAttribute),true);
如果(mytableName.Length>0)
{
TableAttribute mytable=mytableName[0]作为TableAttribute;
tableName=mytable.Name;
}
其他的
{
tableName=pi.Name;
}
foreach(t.GetProperties()中的var piEntity)
{
if(piEntity.GetCustomAttributes(typeof(UniqueAttribute),true).Length>0)
{
fieldName=piEntity.Name;
context.Database.ExecuteSqlCommand(“ALTER TABLE”+tableName+“ADD con con_Unique”+tableName+“_”+fieldName+”Unique(“+fieldName+”));
}
}
}
}
}
}
}
最后,在Global.asax.cs中的Application_Start中添加初始值设定项

System.Data.Entity.Database.SetInitializer<MyApp.Models.DomainModels.myEntities>(new MyApp.Models.DomainModels.myEntities.Initializer());
System.Data.Entity.Database.SetInitializer(新的MyApp.Models.DomainModels.myenties.Initializer());
就这样。基于

处的vb代码,我创建了这个类(从另一个Stackoverflow答案中得到了增强),它允许我将SQL脚本放入目录中,并在每次需要时(种子或迁移)执行它们。在部署到生产环境之后,我不会让它保持打开状态,但是在开发过程中,每次重新创建数据库时都可以轻松地应用脚本

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//dll Microsoft.SqlServer.Smo
//dll Microsoft.SqlServer.Management.Sdk.Sfc
//dll Microsoft.SqlServer.ConnectionInfo
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Monitor.Common;

namespace MonitorDB.DataLayer.Migrations
{
  public class ExecuteSQLScripts :Monitor.Common.ExceptionHandling
  {
    public ExecuteSQLScripts()
    {
}

public bool ExecuteScriptsInDirectory(DBContext.SolArcMsgMonitorContext context, string scriptDirectory)
{
  bool Result = false;
  try
  {
    SqlConnection connection = new SqlConnection(context.Database.Connection.ConnectionString);
    Server server = new Server(new ServerConnection(connection));

    DirectoryInfo di = new DirectoryInfo(scriptDirectory);
    FileInfo[] rgFiles = di.GetFiles("*.sql");
    foreach (FileInfo fi in rgFiles)
    {

      FileInfo fileInfo = new FileInfo(fi.FullName);
      string script = fileInfo.OpenText().ReadToEnd();

      server.ConnectionContext.ExecuteNonQuery(script);
    }
    Result = true;
  }
  catch (Exception ex)
  {
    CatchException("ExecuteScriptsInDirectory", ex);
  }
  return Result;
}
} }

以下是VS解决方案的外观:


我找到了这个解决方案,它虽然没有在SQL级别创建唯一的键,但使用了DataAnnotations验证,请查看:


我使用MVC 3和EF 4,代码无法识别context.Database.ExecuteSqlCommand中的ExecuteSqlCommand(“创建唯一索引IX_Category_Title ON Categories(Title)”;这是关于版本还是其他方面的问题?@Saeid:这是针对DbContext API(EFv4.1)的。EFv4中没有数据库初始值设定项。ObjectContext API提供了自己的方法来直接执行SQL-
ExecuteSToRecommend
。另外,添加默认约束(例如GETDATE()等)的一个很好的方法是多次执行种子-这会不会作为索引(或函数/存储过程/或其他什么)出错数据库中已存在?@codputer:在这种情况下,
种子只执行一次,因为它不使用迁移。在迁移的情况下,您可以直接在
Up
方法中创建索引。1.在ExecuteSqlCommand 2期间,tableName应该用括号括起来。如果您使用的是非复数名称,请使用else{tableName=t.Name}哦,得了吧-对于具有完全相同问题的VB用户,添加简明的VB版本有什么不对?这难道不是这样的目的吗?不仅仅为原始海报提供资源吗?此外,如前所述,实现有些不同。
System.Data.Entity.Database.SetInitializer<MyApp.Models.DomainModels.myEntities>(new MyApp.Models.DomainModels.myEntities.Initializer());
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//dll Microsoft.SqlServer.Smo
//dll Microsoft.SqlServer.Management.Sdk.Sfc
//dll Microsoft.SqlServer.ConnectionInfo
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using Monitor.Common;

namespace MonitorDB.DataLayer.Migrations
{
  public class ExecuteSQLScripts :Monitor.Common.ExceptionHandling
  {
    public ExecuteSQLScripts()
    {
}

public bool ExecuteScriptsInDirectory(DBContext.SolArcMsgMonitorContext context, string scriptDirectory)
{
  bool Result = false;
  try
  {
    SqlConnection connection = new SqlConnection(context.Database.Connection.ConnectionString);
    Server server = new Server(new ServerConnection(connection));

    DirectoryInfo di = new DirectoryInfo(scriptDirectory);
    FileInfo[] rgFiles = di.GetFiles("*.sql");
    foreach (FileInfo fi in rgFiles)
    {

      FileInfo fileInfo = new FileInfo(fi.FullName);
      string script = fileInfo.OpenText().ReadToEnd();

      server.ConnectionContext.ExecuteNonQuery(script);
    }
    Result = true;
  }
  catch (Exception ex)
  {
    CatchException("ExecuteScriptsInDirectory", ex);
  }
  return Result;
}