Entity Framework 4中的唯一键

20

一个已存在的数据库架构具有独特的非主键和一些依赖它们的外键。

在实体框架 v4 中,是否可以定义不是主键的唯一键?如果可以,如何实现?

5个回答

17

实体框架6.1现在支持使用数据注释和流畅的API来定义唯一性。

数据注释 (参考文献)

public class MyEntityClass
{ 
    [Index(IsUnique = true)]
    [MaxLength(255)] // for code-first implementations
    public string MyUniqueProperty{ get; set; } 
}

流畅API (参考文献)

public class MyContext : DbContext
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder 
                .Entity<MyEntityClass>() 
                .Property(t => t.MyUniqueProperty) 
                .HasMaxLength(255) // for code-first implementations
                .HasColumnAnnotation( 
                    "Index",  
                    new IndexAnnotation(new[] 
                        { 
                            new IndexAttribute("Index") { IsUnique = true } 
                        })));
        }
    }
}

你需要应用索引并将 unique 属性设置为 true。根据文档,默认情况下,索引是非唯一的。

同时,你还需要在项目中安装 Entity Framework 6.1 NuGet 包,以便使用索引的新 API。

关于代码优先实现的注意事项: VARCHAR(MAX) 不能成为唯一约束的一部分。你必须在 Data Annotation 或 Fluent API 中指定最大长度。


这段代码在我的电脑上编译通过,但是运行时出现了一个错误:表'dbo.Users'中的列'Email'的类型无效,不能用作索引的键列。 Email 是一个公共字符串,就像 MyUniqueProperty 一样。 - djs
这里一切正常,我尝试了多次来重现你的错误,但是没有成功。你能否开一个新的问题并提供所有相关细节,包括EF版本、完整的堆栈跟踪和你的类?请在此处添加评论并附上链接,以便我进行调查。谢谢! - Evandro Pomatti
1
我的问题是因为我正在使用代码优先实现,它默认将“Email”列创建为“NVARCHAR(MAX)”。我进行了编辑以解决这个问题。你的解决方案现在对我很有效! - djs
@djs 这很奇怪。我在这里使用 SQL Compact 和 CF,采用了 drop-create 策略,但我没有遇到任何错误。也许是与数据库相关的问题?不过感谢你的更新。 - Evandro Pomatti
1
我正在使用SQL Server 2014。这可能是不同之处。 - djs

11

6
很不幸,下一个版本(EF 5.0和.NET 4.5)将不会是未来发布的版本。 - Ladislav Mrnka

10

我不久前遇到了同样的问题。

我得到了一个带有几个表的数据库(见下文)。

 public class ClinicDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Doctor> Doctors { get; set; }
    public DbSet<Patient> Patients { get; set; }
    public DbSet<Secretary> Secretarys { get; set; }
    public DbSet<Disease> Diseases { get; set; }
    public DbSet<Consultation> Consultations { get; set; }
    public DbSet<Administrator> Administrators { get; set; }
}

用户表的描述如下:

public class User
{
    [Key]
    public Guid UserId { get; set; }

    public string UserName { get; set; }

    public string Password { get; set; }

    public string Name { get; set; }
    public string Surname { get; set; }
    public string IdentityCardNumber { get; set; }
    public string PersonalNumericalCode { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

接下来,我被要求确保所有'UserName'属性都是唯一的。由于没有对此进行注释,我不得不想出一个解决方法。以下是我的解决方案:

首先,我将我的数据库上下文类更改为以下内容:

public class ClinicDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Doctor> Doctors { get; set; }
    public DbSet<Patient> Patients { get; set; }
    public DbSet<Secretary> Secretarys { get; set; }
    public DbSet<Disease> Diseases { get; set; }
    public DbSet<Consultation> Consultations { get; set; }
    public DbSet<Administrator> Administrators { get; set; }

    public class Initializer : IDatabaseInitializer<ClinicDbContext>
    {
        public void InitializeDatabase(ClinicDbContext context)
        {
            if (!context.Database.Exists() || !context.Database.CompatibleWithModel(false))
            {
                if (context.Database.Exists())
                {
                    context.Database.Delete();
                }
                context.Database.Create();

                context.Database.ExecuteSqlCommand("CREATE INDEX IX_Users_UserName ON dbo.Users ( UserName )");
            }
        }
    }
}
以上中重要的部分是SQL命令,它通过在所需的列(在我们的案例中为UserName)上强制实施唯一索引来更改表格。
例如,此方法可以从主类中调用:
返回结果:The important part from above is the sql command which alters the table by enforcing a unique index on our desired column -> UserName in our case. This method can be called from the main class for example:
class Program
{
    static void Main(string[] args)
    {
        Database.SetInitializer<ClinicDbContext>(new ClinicDbContext.Initializer());

        using (var ctx = new ClinicDbContext())
        {
            Console.WriteLine("{0} products exist in the database.", ctx.Users.Count());
        }

        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

尝试运行程序类时发生的最后一个问题是: 表中的列是无效类型,不适用于索引的键列

为了解决这个问题,我只需在UserName属性上添加[MaxLength(250)] 注释。

以下是最终的User类:

public class User
{
    [Key]
    public Guid UserId { get; set; }

    [MaxLength(250)]
    public string UserName { get; set; }

    public string Password { get; set; }

    public string Name { get; set; }
    public string Surname { get; set; }
    public string IdentityCardNumber { get; set; }
    public string PersonalNumericalCode { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

希望这也能解决你的问题!


8

我尝试定义以下表格:

  • 订单 [Id (主键,自增), 客户名称, 友好订单编号 (唯一)]
  • 订单项 [Id (主键,自增), 友好订单编号 (唯一), 项目名称]

并从OrderItems.FriendlyOrderNum(Mant)到Orders.FriendlyOrderNum(one)建立了一个外键映射。

如果可能使用唯一的非主键,则以下SSDL应该可以工作:

<Schema Namespace="EfUkFk_DbModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
    <EntityContainer Name="EfUkFk_DbModelStoreContainer">
      <EntitySet Name="OrderItems" EntityType="EfUkFk_DbModel.Store.OrderItems" store:Type="Tables" Schema="dbo" />
      <EntitySet Name="Orders" EntityType="EfUkFk_DbModel.Store.Orders" store:Type="Tables" Schema="dbo" />
    </EntityContainer>
    <EntityType Name="OrderItems">
      <Key>
        <PropertyRef Name="RowId" />
      </Key>
      <Property Name="RowId" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
      <Property Name="OrderNum" Type="char" Nullable="false" MaxLength="5" />
      <Property Name="ItemName" Type="varchar" MaxLength="100" />
    </EntityType>
    <!--Errors Found During Generation:
  warning 6035: The relationship 'FK_OrderItems_Orders' has columns that are not part of the key of the table on the primary side of the relationship. The relationship was excluded.
  -->
    <EntityType Name="Orders">
      <Key>
        <PropertyRef Name="RowId" />
      </Key>
      <Property Name="RowId" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
      <Property Name="ClientName" Type="varchar" MaxLength="100" />
      <Property Name="OrderNum" Type="char" Nullable="false" MaxLength="5" />
    </EntityType>

  <!-- AsafR -->
    <Association Name="FK_OrderItems_Orders">
      <End Role="Orders" Type="EfUkFk_DbModel.Store.Orders" Multiplicity="1">
      </End>
      <End Role="OrderItems" Type="EfUkFk_DbModel.Store.OrderItems" Multiplicity="*" />
      <ReferentialConstraint>
        <Principal Role="Orders">
          <PropertyRef Name="OrderNum" />
        </Principal>
        <Dependent Role="OrderItems">
          <PropertyRef Name="OrderNum" />
        </Dependent>
      </ReferentialConstraint>
    </Association>
  </Schema></edmx:StorageModels>

不行。在<EntityType>中也无法添加更多的<key>元素。

我的结论是EF 4不支持非主要唯一键。


3
你也可以使用DataAnnotations验证。
我创建了thisUniqueAttribute)类,它继承了ValidationAttribute类。当应用于属性时,在验证期间,该列的值将被检索并进行验证。
你可以从这里获取原始代码。

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