Unikalne ograniczenia klucza dla wielu kolumn w ramach encji
Najpierw używam kodu Entity Framework 5.0;
public class Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string EntityId { get; set;}
public int FirstColumn { get; set;}
public int SecondColumn { get; set;}
}
Chcę, aby kombinacja pomiędzy FirstColumn
i SecondColumn
była unikalna.
Przykład:
Id FirstColumn SecondColumn
1 1 1 = OK
2 2 1 = OK
3 3 3 = OK
5 3 1 = THIS OK
4 3 3 = GRRRRR! HERE ERROR
Czy w ogóle można to zrobić? 9 answers
Z Entity Framework 6.1, możesz teraz zrobić to:
[Index("IX_FirstAndSecond", 1, IsUnique = true)]
public int FirstColumn { get; set; }
[Index("IX_FirstAndSecond", 2, IsUnique = true)]
public int SecondColumn { get; set; }
Drugi parametr w atrybucie to miejsce, w którym można określić kolejność kolumn w indeksie.
Więcej informacji: MSDN
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-06 17:05:01
Znalazłem trzy sposoby rozwiązania problemu.
Unikalne indeksy w rdzeniu EntityFramework:
Pierwsze podejście:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>()
.HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique();
}
Drugie podejście do tworzenia unikalnych ograniczeń za pomocą rdzenia EF za pomocą kluczy alternatywnych.
Przykłady
Jedna kolumna:
modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");
Wiele kolumn:
modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns");
EF 6 i poniżej:
Pierwsze podejście:
dbContext.Database.ExecuteSqlCommand(string.Format(
@"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})",
"Entitys", "FirstColumn, SecondColumn"));
To podejście jest bardzo szybkie i przydatne, ale głównym problemem jest to, że Entity Framework nie wie nic o tych zmianach!
Drugie podejście:
Znalazłem go w tym poście, ale nie próbowałem sam.
CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
true, "IX_Entitys");
Problem tego podejścia jest następujący: wymaga DbMigration więc co zrobić, jeśli go nie masz?
Trzecie podejście:
Myślę, że ten jest najlepszy, ale wymaga trochę czasu, aby to zrobić. Pokażę Ci, co za tym stoi.:
W ten link http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a
możesz znaleźć kod do unikalnej adnotacji kluczowych danych:
[UniqueKey] // Unique Key
public int FirstColumn { get; set;}
[UniqueKey] // Unique Key
public int SecondColumn { get; set;}
// The problem hier
1, 1 = OK
1 ,2 = NO OK 1 IS UNIQUE
Problem dla tego podejścia; Jak mogę je połączyć? Mam pomysł na rozszerzenie tej implementacji Microsoftu na przykład:
[UniqueKey, 1] // Unique Key
public int FirstColumn { get; set;}
[UniqueKey ,1] // Unique Key
public int SecondColumn { get; set;}
Później w IDatabaseInitializer, jak opisano w przykładzie Microsoft, można połączyć klucze zgodnie z podaną liczbą całkowitą. Należy jednak zauważyć jedną rzecz: jeśli unikalna właściwość jest wpisz string następnie musisz ustawić MaxLength.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 12:20:59
Jeśli używasz kodu-First, możesz zaimplementować niestandardowe rozszerzenie HasUniqueIndexAnnotation
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration.Configuration;
internal static class TypeConfigurationExtensions
{
public static PrimitivePropertyConfiguration HasUniqueIndexAnnotation(
this PrimitivePropertyConfiguration property,
string indexName,
int columnOrder)
{
var indexAttribute = new IndexAttribute(indexName, columnOrder) { IsUnique = true };
var indexAnnotation = new IndexAnnotation(indexAttribute);
return property.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
}
}
Następnie użyj go tak:
this.Property(t => t.Email)
.HasColumnName("Email")
.HasMaxLength(250)
.IsRequired()
.HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 0);
this.Property(t => t.ApplicationId)
.HasColumnName("ApplicationId")
.HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 1);
Co spowoduje migrację:
public override void Up()
{
CreateIndex("dbo.User", new[] { "Email", "ApplicationId" }, unique: true, name: "UQ_User_EmailPerApplication");
}
public override void Down()
{
DropIndex("dbo.User", "UQ_User_EmailPerApplication");
}
I ostatecznie kończy się w bazie jako:
CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
[Email] ASC,
[ApplicationId] ASC
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-04 16:33:11
ODPOWIEDŹ niahera stwierdzająca, że aby korzystać z fluent API, potrzebujesz niestandardowego rozszerzenia, mogła być poprawna w momencie pisania tego tekstu. Możesz teraz (EF core 2.1) używać fluent API w następujący sposób:
modelBuilder.Entity<ClassName>()
.HasIndex(a => new { a.Column1, a.Column2}).IsUnique();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-16 07:35:14
Musisz zdefiniować klucz złożony.
Z adnotacjami do danych wygląda to tak:
public class Entity
{
public string EntityId { get; set;}
[Key]
[Column(Order=0)]
public int FirstColumn { get; set;}
[Key]
[Column(Order=1)]
public int SecondColumn { get; set;}
}
Możesz to również zrobić za pomocą modelBuilder, gdy nadpisujesz OnModelCreating, podając:
modelBuilder.Entity<Entity>().HasKey(x => new { x.FirstColumn, x.SecondColumn });
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-19 08:52:50
Uzupełnianie odpowiedzi @ chuck za używanie indeksów złożonych z kluczami obcymi .
Musisz zdefiniować właściwość, która będzie zawierać wartość klucza obcego. Następnie można użyć tej właściwości wewnątrz definicji indeksu.
Na przykład, mamy firmę z pracownikami i tylko my mamy unikalne ograniczenie (nazwa, firma) dla każdego pracownika:
class Company
{
public Guid Id { get; set; }
}
class Employee
{
public Guid Id { get; set; }
[Required]
public String Name { get; set; }
public Company Company { get; set; }
[Required]
public Guid CompanyId { get; set; }
}
Teraz odwzorowanie klasy pracownika:
class EmployeeMap : EntityTypeConfiguration<Employee>
{
public EmployeeMap ()
{
ToTable("Employee");
Property(p => p.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(p => p.Name)
.HasUniqueIndexAnnotation("UK_Employee_Name_Company", 0);
Property(p => p.CompanyId )
.HasUniqueIndexAnnotation("UK_Employee_Name_Company", 1);
HasRequired(p => p.Company)
.WithMany()
.HasForeignKey(p => p.CompanyId)
.WillCascadeOnDelete(false);
}
}
Zauważ, że używałem również rozszerzenia @ niaher dla unikalna adnotacja indeksu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 15:05:40
W zaakceptowanej odpowiedzi przez @ chuck, jest komentarz mówiący, że to nie zadziała w przypadku FK.
U mnie zadziałało, przypadek EF6.Net4.7. 2
public class OnCallDay
{
public int Id { get; set; }
//[Key]
[Index("IX_OnCallDateEmployee", 1, IsUnique = true)]
public DateTime Date { get; set; }
[ForeignKey("Employee")]
[Index("IX_OnCallDateEmployee", 2, IsUnique = true)]
public string EmployeeId { get; set; }
public virtual ApplicationUser Employee{ get; set; }
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-10 12:05:51
Zakładam, że zawsze chcesz, aby EntityId
był kluczem podstawowym, więc zastąpienie go kluczem złożonym nie jest opcją (choćby dlatego, że klucze złożone są znacznie bardziej skomplikowane w pracy z i, ponieważ nie jest zbyt rozsądne posiadanie kluczy podstawowych, które również mają znaczenie w logice biznesowej).
Powinieneś przynajmniej utworzyć unikalny klucz na obu polach w bazie danych i dokładnie sprawdzić wyjątki naruszenia unikalnych kluczy podczas zapisywania zmian.
Dodatkowo ty może (powinien) sprawdzić unikalne wartości przed zapisaniem zmian. Najlepiej jest to zrobić za pomocą zapytania Any()
, ponieważ minimalizuje ilość przesyłanych danych:
if (context.Entities.Any(e => e.FirstColumn == value1
&& e.SecondColumn == value2))
{
// deal with duplicate values here.
}
Uważaj, że sam czek nigdy nie wystarczy. Zawsze jest pewne opóźnienie między sprawdzeniem a rzeczywistym zatwierdzeniem, więc zawsze będziesz potrzebował unikalnego ograniczenia + obsługi wyjątków.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-19 09:47:01
Ostatnio dodano klucz kompozytowy o wyjątkowości 2 kolumn przy użyciu podejścia zalecanego przez 'chuck', dzięki @ chuck. Tylko to wygladalo jak dla mnie czystsze:
public int groupId {get; set;}
[Index("IX_ClientGrouping", 1, IsUnique = true)]
public int ClientId { get; set; }
[Index("IX_ClientGrouping", 2, IsUnique = true)]
public int GroupName { get; set; }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-29 20:52:12