इकाई फ्रेमवर्क में कई कॉलमों के लिए अद्वितीय कुंजी बाधाएं


243

मैं एंटिटी फ्रेमवर्क 5.0 कोड फर्स्ट का उपयोग कर रहा हूं;

public class Entity
 {
   [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public string EntityId { get; set;}
   public int FirstColumn  { get; set;}
   public int SecondColumn  { get; set;}
 }

मैं FirstColumnऔर SecondColumnअद्वितीय के बीच संयोजन बनाना चाहता हूं ।

उदाहरण:

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

क्या उसे करने का कोई तरीका है?

जवाबों:


369

इकाई फ्रेमवर्क 6.1 के साथ, अब आप यह कर सकते हैं:

[Index("IX_FirstAndSecond", 1, IsUnique = true)]
public int FirstColumn { get; set; }

[Index("IX_FirstAndSecond", 2, IsUnique = true)]
public int SecondColumn { get; set; }

विशेषता में दूसरा पैरामीटर वह है जहां आप सूचकांक में कॉलम के क्रम को निर्दिष्ट कर सकते हैं।
अधिक जानकारी: MSDN


12
यह डेटा एनोटेशन के लिए सही है :), यदि आप धाराप्रवाह एपीआई का उपयोग करने के लिए उत्तर चाहते हैं, तो निओहेर के जवाब के नीचे देखें stackoverflow.com/a/25779348/2362036
tekiegirl

8
लेकिन मुझे विदेशी कुंजियों के लिए काम करने की आवश्यकता है! क्या आप मेरी मदद कर सकते हैं?
फीडक ०

2
@ 0xFEEDC0DE नीचे मेरा उत्तर देखें कि सूचकांकों में विदेशी कुंजी का उपयोग करें।
क्रिप्टोस

1
क्या आप पोस्ट कर सकते हैं कि इस सूचकांक को sql से sql में कैसे उपयोग किया जाए?
ब्लूबारन

4
@ जेजेएस - मुझे यह काम करने के लिए मिला, जहां एक गुण एक विदेशी कुंजी था .. किसी भी अवसर से आपकी कुंजी एक varchar या gvarchar है? इसकी लंबाई की सीमा है जिसका उपयोग एक अद्वितीय कुंजी के रूप में किया जा सकता है .. stackoverflow.com/questions/2863993/…
डेव लॉरेंस

129

मुझे समस्या को हल करने के तीन तरीके मिले।

EntityFramework Core में अद्वितीय सूचकांक:

पहले दृष्टिकोण:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<Entity>()
   .HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique();
}

वैकल्पिक कुंजी का उपयोग करके ईएफ कोर के साथ अद्वितीय बाधाओं को बनाने के लिए दूसरा दृष्टिकोण

उदाहरण

एक कॉलम:

modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");

कई कॉलम:

modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns");

EF 6 और नीचे:


पहले दृष्टिकोण:

dbContext.Database.ExecuteSqlCommand(string.Format(
                        @"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})", 
                                 "Entitys", "FirstColumn, SecondColumn"));

यह दृष्टिकोण बहुत तेज़ और उपयोगी है लेकिन मुख्य समस्या यह है कि एंटिटी फ्रेमवर्क उन परिवर्तनों के बारे में कुछ भी नहीं जानता है!


दूसरा तरीका:
मुझे यह इस पोस्ट में मिला लेकिन मैंने खुद कोशिश नहीं की।

CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
              true, "IX_Entitys");

इस दृष्टिकोण की समस्या निम्न है: इसे DbM माइग्रेशन की आवश्यकता है इसलिए यदि आपके पास यह नहीं है तो आप क्या करते हैं?


तीसरा दृष्टिकोण:
मुझे लगता है कि यह सबसे अच्छा है लेकिन इसे करने के लिए कुछ समय चाहिए। मैं आपको इसके पीछे का आइडिया दिखाऊंगा: इस लिंक http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a में आप अद्वितीय कुंजी डेटा एनोटेशन के लिए कोड पा सकते हैं:

[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

इस दृष्टिकोण के लिए समस्या; मैं उन्हें कैसे जोड़ सकता हूं? मेरे पास इस Microsoft कार्यान्वयन को उदाहरण के लिए विस्तारित करने का एक विचार है:

[UniqueKey, 1] // Unique Key 
public int FirstColumn  { get; set;}
[UniqueKey ,1] // Unique Key 
public int SecondColumn  { get; set;}

बाद में Microsoft उदाहरण में वर्णित IDatabaseInitializer में आप दिए गए पूर्णांक के अनुसार कुंजियों को जोड़ सकते हैं। हालांकि एक बात पर ध्यान दिया जाना चाहिए: यदि अनोखी संपत्ति टाइप स्ट्रिंग की है तो आपको मैक्सलेंथ सेट करना होगा।


1
(y) मुझे यह उत्तर बेहतर लगता है। एक और बात, तीसरा तरीका जरूरी नहीं कि सबसे अच्छा हो। (मैं वास्तव में पहले वाले को पसंद करता हूं।) मैं व्यक्तिगत रूप से अपनी इकाई कक्षाओं में किए गए किसी भी ईएफ कलाकृतियों को पसंद नहीं करता हूं।
नजीब

1
संभवतः, दूसरा दृष्टिकोण होना चाहिए CREATE UNIQUE INDEX ix_{1}_{2} ON {0} ({1}, {2}):? ( बीओएल देखें )
एके

2
मूर्खतापूर्ण प्रश्न: तो आप अपना सारा नाम "IX_" क्यों शुरू करते हैं?
बैस्टियन वंदाममे

1
@BastienVandamme यह एक अच्छा सवाल है। EF द्वारा ऑटो जेनरेट इंडेक्स IX_ से शुरू होता है। यह डिफ़ॉल्ट रूप से EF सूचकांक में एक सम्मेलन लगता है, सूचकांक का नाम IX_ {संपत्ति का नाम} होगा।
४ili में बासम आलगिली

1
हाँ यह होना चाहिए। धाराप्रवाह एपीआई कार्यान्वयन के लिए धन्यवाद। इस पर प्रलेखन की गंभीर कमी है
JSON

75

यदि आप कोड-प्रथम का उपयोग कर रहे हैं , तो आप एक कस्टम एक्सटेंशन 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);
    }
}

फिर इसे इस तरह उपयोग करें:

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);

जिसके परिणामस्वरूप यह प्रवास होगा:

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");
}

और अंततः डेटाबेस में समाप्त होता है:

CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
    [Email] ASC,
    [ApplicationId] ASC
)

3
लेकिन यह सूचकांक बाधा नहीं है!
रोमन पोक्रोव्सीज

3
आपके दूसरे कोड ब्लॉक ( this.Property(t => t.Email)) में, वह कौन सी क्लास है? (यानी: क्या है this)
जोबोरहॉउस

2
nvm। EntityTypeConfiguration<T>
जोब्रॉकहॉस

5
@RomanPokrovskij: एक अद्वितीय सूचकांक और एक अद्वितीय अवरोध के बीच का अंतर इस बात का प्रतीत होता है कि SQL सर्वर में इसका रिकॉर्ड कैसे रखा जाता है। देखें technet.microsoft.com/en-us/library/aa224827%28v=sql.80%29.aspx जानकारी के लिए।
मास डॉट नेट

1
@ हनिहार मैं आपके अच्छे विस्तार के तरीके की सराहना करता हूं
मोहसिन

18

आपको एक संयुक्त कुंजी को परिभाषित करने की आवश्यकता है।

डेटा एनोटेशन के साथ ऐसा दिखता है:

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;}
 }

जब आप निर्दिष्ट करके OnModelCreating को ओवरराइड कर रहे हों, तो आप इसे ModelBuilder के साथ भी कर सकते हैं:

modelBuilder.Entity<Entity>().HasKey(x => new { x.FirstColumn, x.SecondColumn });

2
लेकिन क्या मैं कुंजी नहीं हूं, मैं उन्हें केवल यूनीक बनाना चाहता हूं जैसे कि आईडी होना चाहिए? मैं मदद के लिए धन्यवाद धन्यवाद अद्यतन किया है!
बासम आलगिली

16

नियाहेर के उत्तर ने कहा कि धाराप्रवाह एपीआई का उपयोग करने के लिए आपको एक कस्टम एक्सटेंशन की आवश्यकता होती है जो लेखन के समय सही हो सकता है। अब आप (ईएफ कोर 2.1) धाराप्रवाह एपीआई का उपयोग इस प्रकार कर सकते हैं:

modelBuilder.Entity<ClassName>()
            .HasIndex(a => new { a.Column1, a.Column2}).IsUnique();

9

विदेशी कुंजियों के साथ समग्र सूचकांकों का उपयोग करने के लिए @chuck उत्तर को पूरा करना

आपको एक ऐसी संपत्ति को परिभाषित करने की आवश्यकता है जो विदेशी कुंजी का मूल्य रखती है। फिर आप इस संपत्ति को इंडेक्स परिभाषा के अंदर उपयोग कर सकते हैं।

उदाहरण के लिए, हमारे पास कर्मचारियों के साथ कंपनी है और केवल हमारे पास किसी कर्मचारी के लिए एक अद्वितीय बाधा (नाम, कंपनी) है:

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; }
}

अब कर्मचारी वर्ग की मैपिंग:

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);
    }
}

ध्यान दें कि मैंने अनन्य सूचकांक एनोटेशन के लिए @niaher एक्सटेंशन का भी उपयोग किया है।


1
इस उदाहरण में आपके पास कंपनी और CompanyId दोनों हैं। इसका मतलब है कि कॉलर एक को बदल सकता है लेकिन दूसरे को नहीं और गलत डेटा के साथ एक इकाई है।
लॉसमनोस

1
@LosManos आप किस कॉलर के बारे में बात कर रहे हैं? वर्ग एक डेटाबेस में डेटा का प्रतिनिधित्व करता है। प्रश्नों के माध्यम से मूल्य बदलने से स्थिरता सुनिश्चित होगी। आपके ग्राहक के आवेदन के आधार पर आपको चेक लागू करने की आवश्यकता हो सकती है लेकिन यह ओपी का दायरा नहीं है।
क्रिप्टोस

4

@Chuck द्वारा स्वीकृत उत्तर में, एक टिप्पणी है जो कहती है कि यह FK के मामले में काम नहीं करेगा।

इसने मेरे लिए काम किया, 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; }
}

लंबे समय तक। मान लें कि यह लंबे समय से पहले काम कर रहा है! अपडेट के लिए धन्यवाद @chuck जवाब में एक टिप्पणी जोड़ें। मुझे लगता है कि चक लंबे समय से पहले एसओ का उपयोग नहीं करता है।
बासम अलुगिली

क्या संपत्ति कर्मचारी यहां अनुक्रमित होने के लिए अपनी लंबाई को सीमित करने के लिए एक विशेषता की आवश्यकता है? VARCHAR (MAX) के साथ इसका बनाया हुआ पात्र जिसमें कोई सूचकांक नहीं हो सकता है? एट्रिब्यूट में जोड़ें [StringLength (255)]
भगवान डार्थ वाडर

कर्मचारी एक GUID है। कई ट्यूटोरियल गाइड के बजाय GUID को स्ट्रिंग करने के लिए मैप करने का सुझाव देते हैं, मुझे नहीं पता कि क्यों
डेलियो

3

मुझे लगता है कि आप हमेशा EntityIdप्राथमिक कुंजी बनना चाहते हैं , इसलिए एक समग्र कुंजी द्वारा इसे बदलना एक विकल्प नहीं है (यदि केवल इसलिए कि समग्र कुंजियों के साथ काम करने के लिए और अधिक जटिल हैं और क्योंकि प्राथमिक कुंजी होने के लिए बहुत समझदार नहीं है, जिसका अर्थ भी है व्यापार तर्क में)।

कम से कम आपको करना चाहिए कि डेटाबेस में दोनों क्षेत्रों पर एक अद्वितीय कुंजी बनाएं और विशेष रूप से परिवर्तनों को सहेजते समय अद्वितीय कुंजी उल्लंघन अपवादों की जांच करें।

इसके अतिरिक्त, आप परिवर्तनों को सहेजने से पहले अनूठे मूल्यों के लिए जांच कर सकते हैं। Any()क्वेरी द्वारा किया जाने वाला सबसे अच्छा तरीका है , क्योंकि यह स्थानांतरित डेटा की मात्रा को कम करता है:

if (context.Entities.Any(e => e.FirstColumn == value1 
                           && e.SecondColumn == value2))
{
    // deal with duplicate values here.
}

सावधान रहें कि यह जाँच कभी भी पर्याप्त नहीं है। चेक और वास्तविक प्रतिबद्ध के बीच हमेशा कुछ विलंब होता है, इसलिए आपको हमेशा अद्वितीय अवरोध + अपवाद हैंडलिंग की आवश्यकता होगी।


3
धन्यवाद @GertArnold जवाब के लिए, लेकिन मैं व्यापार परत पर विशिष्टता को मान्य नहीं करना चाहता हूं यह एक डेटाबेस काम है और यह डेटाबेस में किया जाएगा!
बासम आलगिली

2
ठीक है, तब अद्वितीय सूचकांक से चिपके रहें। लेकिन आपको किसी भी तरह से व्यापार परत में सूचकांक उल्लंघन से निपटना होगा।
गर्ट अर्नोल्ड

1
बाहर से जब मुझे इस तरह का अपवाद मिलेगा तो मैं पकड़ लूंगा और शायद त्रुटि की रिपोर्ट करने और प्रक्रिया को तोड़ने या एप्लिकेशन को बंद करने के लिए।
बासम अलुगिली

3
हां, ... क्या मुझे उस पर प्रतिक्रिया देने की आवश्यकता है? याद रखें कि मुझे आपके आवेदन का कुछ भी पता नहीं है, मैं आपको नहीं बता सकता कि इन अपवादों से निपटने का सबसे अच्छा तरीका क्या है, केवल यह कि आपको उनसे निपटना है।
गर्ट अर्नोल्ड

2
EF के साथ DB अद्वितीय बाधाओं से सावधान रहें। यदि आप ऐसा करते हैं और फिर आप एक ऐसे अपडेट को हवा देते हैं जो किसी एक स्तंभ के मान को फ्लिप-फ्लॉप करता है जो कि अद्वितीय कुंजी का हिस्सा है, तो तब तक सेविंग पर विफल रहेगा जब तक कि आप पूरी ट्रांजेक्शन लेयर नहीं जोड़ लेते। उदाहरण के लिए: पृष्ठ ऑब्जेक्ट में तत्वों का एक बच्चा संग्रह है। प्रत्येक तत्व में SortOrder है। आप चाहते हैं कि PageID और SortOrder का कॉम्बो अद्वितीय हो। सामने के अंत में, उपयोगकर्ता 1 और 2 के साथ तत्वों के फ़्लॉप फ़्लॉप ऑर्डर को क्रमबद्ध करता है। एंटिटी फ्रेमवर्क एक समय में एक बार में सॉर्टर्स को अपडेट करने की कोशिश कर रहा b / c को विफल कर देगा।
ईजीपी

1

हाल ही में 2 कॉलम की विशिष्टता के साथ एक समग्र कुंजी जोड़ा गया है, जो 'चक' की सिफारिश की गई है, धन्यवाद @chuck का उपयोग करते हुए। केवल इस दृष्टिकोण ने मुझे साफ देखा:

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; }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.