সত্তা ফ্রেমওয়ার্ক কোডে প্রথমে অনন্য প্রতিবন্ধকতা


125

প্রশ্ন

অনর্গল সিনট্যাক্স বা কোনও গুণাবলী ব্যবহার করে কোনও সম্পত্তির কোনও অনন্য বাধা সংজ্ঞা দেওয়া কি সম্ভব? যদি তা না হয় তবে কাজের ক্ষেত্রগুলি কী?

আমার কাছে একটি প্রাথমিক কী সহ একটি ব্যবহারকারী শ্রেণি রয়েছে, তবে আমি নিশ্চিত করতে চাই যে ইমেল ঠিকানাটিও অনন্য। এটি সরাসরি ডাটাবেস সম্পাদনা না করে সম্ভব?

সমাধান (ম্যাট এর উত্তরের ভিত্তিতে)

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

1
মনে রাখবেন যে এটি করা আপনার অ্যাপ্লিকেশনটিকে কেবল সেই ডাটাবেসের মধ্যে সীমাবদ্ধ করে যা সেই সঠিক বাক্য গঠন গ্রহণ করে - এই ক্ষেত্রে এসকিউএল সার্ভার। আপনি যদি নিজের অ্যাপ্লিকেশনটি কোনও ওরাকল সরবরাহকারীর সাথে চালান তবে এটি ব্যর্থ হবে।
দামেঞ্জ

1
এই পরিস্থিতিতে আমার কেবলমাত্র একটি নতুন আরম্ভকারী ক্লাস তৈরি করা দরকার তবে এটি একটি বৈধ পয়েন্ট।
কিম 3 ই


হ্যাঁ, এটি এখন EF 6.1 থেকে সমর্থিত ।
ইভানড্রো পোমাটি

উত্তর:


61

আমি যতদূর বলতে পারি, এই মুহুর্তে সত্তা ফ্রেমওয়ার্ক দিয়ে এটি করার কোনও উপায় নেই। তবে এটি কেবল অনন্য বাধা নিয়েই সমস্যা নয় ... আপনি সূচি তৈরি করতে, সীমাবদ্ধতাগুলি পরীক্ষা করতে এবং সম্ভবত ট্রিগার এবং অন্যান্য নির্মাণগুলিও করতে চাইতে পারেন। আপনার কোড-প্রথম সেটআপটি ব্যবহার করার জন্য এখানে একটি সাধারণ প্যাটার্নটি দেওয়া হয়েছে , যদিও স্বীকার করেছেন যে এটি ডেটাবেস অজ্ঞেহিত নয়:

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

অন্য বিকল্পটি হ'ল যদি আপনার ডোমেন মডেলটি আপনার ডাটাবেসে ডেটা সন্নিবেশ / আপডেট করার একমাত্র পদ্ধতি হয় তবে আপনি স্বতন্ত্রতা প্রয়োজনীয়তাটি নিজে প্রয়োগ করতে পারেন এবং এটি থেকে ডাটাবেসটি বাইরে রেখে যেতে পারেন। এটি একটি আরও বহনযোগ্য সমাধান এবং আপনাকে আপনার কোডটিতে আপনার ব্যবসায়ের বিধি সম্পর্কে পরিষ্কার হতে বাধ্য করে, তবে আপনার ডাটাবেসটিকে অবৈধ তথ্য ব্যাক ডোরড করার জন্য ছেড়ে দেয়।


আমার ডিবিটি ড্রামের মতো শক্ত হওয়া পছন্দ করে, যুক্তিটি ব্যবসায়ের স্তরে প্রতিলিপি করা হয়। আপনি উত্তরটি কেবল সিটিপি 4 দিয়ে কাজ করেন তবে আমাকে সঠিক ট্র্যাকে পেয়েছেন, আমি এমন একটি সমাধান সরবরাহ করেছি যা আমার মূল প্রশ্নের নীচে সিটিপি 5 এর সাথে সামঞ্জস্যপূর্ণ। অনেক ধন্যবাদ!
কিম 3 ই

23
আপনার অ্যাপটি একক ব্যবহারকারী না হলে আমি বিশ্বাস করি যে একটি অনন্য প্রতিবন্ধকতা এমন একটি জিনিস যা আপনি কেবল কোড দিয়ে প্রয়োগ করতে পারবেন না । আপনি কোডে লঙ্ঘনের সম্ভাবনা নাটকীয়ভাবে হ্রাস করতে পারেন (কল করার আগে স্বতন্ত্রতা পরীক্ষা করে SaveChanges()), তবে স্বতন্ত্রতা পরীক্ষা করার সময় এবং সময়ের মধ্যে অন্য সন্নিবেশ / আপডেট স্লিপ হওয়ার সম্ভাবনা রয়েছে SaveChanges()। সুতরাং, অ্যাপটি কীভাবে মিশনকে সমালোচনা করে এবং একটি স্বতন্ত্রতা লঙ্ঘনের স্বল্পতার উপর নির্ভর করে ডাটাবেসে সীমাবদ্ধতা যুক্ত করা ভাল।
ডিভাক্সার

1
স্বতন্ত্রতার জন্য আপনার চেকটি আপনার সেভচেনজগুলির মতো একই লেনদেনের অংশ হতে হবে। আপনার ডাটাবেসটিকে অ্যাসিডের সাথে সঙ্গতিপূর্ণ বলে ধরে নেওয়া আপনার পক্ষে একেবারে স্বতন্ত্রতা প্রয়োগ করতে সক্ষম হওয়া উচিত। এখন EF আপনাকে এইভাবে লেনদেনের জীবনচক্রটি সঠিকভাবে পরিচালনা করতে দেয় কিনা তা অন্য একটি প্রশ্ন।
mattmc3

1
@ mattmc3 এটি আপনার লেনদেনের বিচ্ছিন্নতার স্তরের উপর নির্ভর করে। কেবলমাত্র serializable isolation level(বা কাস্টম টেবিল লকিং, উঘ) আপনাকে আপনার কোডটিতে স্বতন্ত্রতার গ্যারান্টি দেওয়ার অনুমতি দেয়। তবে বেশিরভাগ লোকেরা serializable isolation levelপারফরম্যান্সের কারণে ব্যবহার করেন না । এমএস এসকিউএল সার্ভারে ডিফল্ট read committed। 4 অংশের সিরিজটি এখানে শুরু দেখুন: মাইকেলজসওয়ার্ট.com
নাথান

3
এন্টিফ্রেমওয়ার্ক 6.1.0 এর সূচিপত্রের জন্য এখন সমর্থন রয়েছে যা আপনি মূলত এটি বৈশিষ্ট্যের শীর্ষে যুক্ত করতে পারেন।
সটন

45

EF 6.1 দিয়ে শুরু করা এখন এটি সম্ভব:

[Index(IsUnique = true)]
public string EmailAddress { get; set; }

কঠোরভাবে বলতে গেলে এটি আপনাকে অনন্য বাধার পরিবর্তে একটি অনন্য সূচক পাবে। বেশিরভাগ ব্যবহারিক উদ্দেশ্যে তারা একই রকম


5
@ ডেভ: কেবলমাত্র সংশ্লিষ্ট সূত্রের ( উত্স ) বৈশিষ্ট্যগুলিতে একই সূচক নামটি ব্যবহার করুন ।
মিহকেল মের

নোট করুন এটি অনন্য কন্ট্রিন্টের চেয়ে একটি অনন্য সূচক তৈরি করে । প্রায় একই রকম হলেও তারা একেবারে এক নয় (যেমন আমি বুঝতে পারি যে অনন্য প্রতিবন্ধকতাগুলি এফকে লক্ষ্য হিসাবে ব্যবহার করা যেতে পারে)। সীমাবদ্ধতার জন্য আপনার এসকিউএল চালানো দরকার।
রিচার্ড

(শেষ মন্তব্য অনুসরণ করে) অন্যান্য উত্সগুলি পরামর্শ দেয় যে এসকিউএল সার্ভারের আরও সাম্প্রতিক সংস্করণগুলিতে এই সীমাবদ্ধতাটি সরানো হয়েছে ... তবে বিওএল সম্পূর্ণরূপে সামঞ্জস্যপূর্ণ নয়।
রিচার্ড

@Richard: অ্যাট্রিবিউট ভিত্তিক অনন্য সীমাবদ্ধতার সম্ভব হয় (দেখুন আমার দ্বিতীয় উত্তর , যদিও না বাক্সের বাইরে)।
মিহ্কেল মের

1
@exSnake: এসকিউএল সার্ভার ২০০৮ সাল থেকে, অনন্য সূচক ডিফল্টরূপে কলামে একক নুল মান সমর্থন করে। যদি একাধিক NUL গুলি সমর্থন প্রয়োজন হয়, অন্য একটি প্রশ্ন দেখতে একটি ফিল্টার সূচক প্রয়োজন হবে ।
মিহকেল মার

28

সত্যিই এর সাথে সম্পর্কিত নয় তবে এটি কিছু ক্ষেত্রে সহায়তা করতে পারে।

যদি আপনি একটি অনন্য যৌগিক সূচক তৈরি করতে চান তবে 2 টি কলাম বলুন যা আপনার টেবিলের জন্য সীমাবদ্ধতা হিসাবে কাজ করবে, তবে সংস্করণ 4.3 হিসাবে আপনি এটি অর্জনের জন্য নতুন মাইগ্রেশন প্রক্রিয়াটি ব্যবহার করতে পারেন:

মূলত আপনার নিজের মাইগ্রেশন স্ক্রিপ্টগুলির মধ্যে একটিতে callোকানো দরকার:

CreateIndex("TableName", new string[2] { "Column1", "Column2" }, true, "IX_UniqueColumn1AndColumn2");

এরকম কিছু:

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

ইএফ দেখে খুব ভাল লাগল যে রেল স্টাইলের স্থানান্তর হয়েছে। এখন যদি আমি এটি মনোতে চালাতে পারতাম।
kim3er

2
ডাউন () পদ্ধতিতে আপনার কী ড্রপ ইন্ডেক্স থাকা উচিত নয়? DropIndex("TableName", new[] { "Column1", "Column2" });
মাইকেল বিসবার্গ

5

আমি যখন ডেটাবেস তৈরি হচ্ছে তখন এসকিউএলকে কার্যকর করতে একটি সম্পূর্ণ হ্যাক করি। আমি আমার নিজস্ব ডেটাবেসআইনিটিয়ালাইজার তৈরি করি এবং প্রদত্ত আরম্ভকারীদের একজনের উত্তরাধিকারী।

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

আমার এসকিউএল স্টেটমেন্টগুলিতে আমি কেবল এটিই খুঁজে পেতে পারি।

এটি সিটিপি 4 এর। আমি জানি না এটি সিটিপি 5 এ কীভাবে কাজ করে।


ধন্যবাদ কেলি! আমি ইভেন্ট ইভেন্টটির সম্পর্কে জানতাম না। আমার শেষ সমাধানটি এসকিউএলকে ইনিশিয়াল ডেটাবেস পদ্ধতিতে রাখে places
কিম 3 ই

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

তারপরে আমার ক্লাসে আমি এটি যুক্ত করব:

[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]
}

পরিশেষে, আমি আমার সংগ্রহস্থলটিতে একটি অ্যাড পদ্ধতিতে বা এই জাতীয় পরিবর্তনগুলি সংরক্ষণ করার সময় একটি পদ্ধতি যুক্ত করব:

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

প্রতিচ্ছবি উপর নির্ভর করা প্রয়োজন হিসাবে খুব সুন্দর না কিন্তু এটি এখন পর্যন্ত আমার জন্য কাজ করে যে পদ্ধতির! = D:


5

এছাড়াও 6.1 এ আপনি @ মিহকেলমুরের উত্তরটির অনর্গল সিনট্যাক্স সংস্করণটি এর মতো ব্যবহার করতে পারেন:

Property(s => s.EmailAddress).HasColumnAnnotation(IndexAnnotation.AnnotationName,
new IndexAnnotation(
    new IndexAttribute("IX_UniqueEmail") { IsUnique = true }));

সাবলীল পদ্ধতি নিখুঁত আইএমও নয় তবে কমপক্ষে এখন এটি সম্ভব।

আর্থার ভিকার্স ব্লগ http://blog.oneunicorn.com/2014/02/15/ef-6-1-creating-indexes-with-indexattribute/ এ আরও ডিট


4

EF5 কোড প্রথম স্থানান্তর ব্যবহার করে ভিজ্যুয়াল বেসিকের একটি সহজ উপায়

পাবলিক ক্লাসের নমুনা

    Public Property SampleId As Integer

    <Required>
    <MinLength(1),MaxLength(200)>

    Public Property Code() As String

শেষ ক্লাস

ম্যাক্সেলংথ বৈশিষ্ট্যটি স্ট্রিং ধরণের অনন্য সূচকের জন্য খুব গুরুত্বপূর্ণ

সিএমডি চালান: আপডেট-ডাটাবেস -ভারবস

সেমিডেমি রান করার পরে: অ্যাড-মাইগ্রেশন 1

উত্পন্ন ফাইল মধ্যে

Public Partial Class _1
    Inherits DbMigration

    Public Overrides Sub Up()
        CreateIndex("dbo.Sample", "Code", unique:=True, name:="IX_Sample_Code")
    End Sub

    Public Overrides Sub Down()
        'DropIndex if you need it
    End Sub

End Class

এটি আসলে কাস্টম ডিবি প্রারম্ভকালের চেয়ে আরও উপযুক্ত উত্তর।
শন উইলসন

4

টোবিয়াস শিটকোভস্কির জবাবের মতো তবে সি # এবং এই সীমাবদ্ধতায় একাধিক ক্ষেত্র রাখার ক্ষমতা রয়েছে।

এটি ব্যবহার করতে, আপনি অনন্য হতে চান এমন যে কোনও ক্ষেত্রে কেবল একটি [অনন্য] রাখুন। স্ট্রিংগুলির জন্য, আপনাকে এমন কিছু করতে হবে (ম্যাক্সেলংথ বৈশিষ্ট্যটি নোট করুন):

[Unique]
[MaxLength(450)] // nvarchar(450) is max allowed to be in a key
public string Name { get; set; }

কারণ ডিফল্ট স্ট্রিং ফিল্ডটি nvarchar (সর্বাধিক) এবং এটি কোনও কীতে অনুমোদিত হবে না।

সীমাবদ্ধতার একাধিক ক্ষেত্রের জন্য আপনি এটি করতে পারেন:

[Unique(Name="UniqueValuePairConstraint", Position=1)]
public int Value1 { get; set; }
[Unique(Name="UniqueValuePairConstraint", Position=2)]
public int Value2 { get; set; }

প্রথমত, ইউনিকঅ্যাট্রিবিউট:

/// <summary>
/// The unique attribute. Use to mark a field as unique. The
/// <see cref="DatabaseInitializer"/> looks for this attribute to 
/// create unique constraints in tables.
/// </summary>
internal class UniqueAttribute : Attribute
{
    /// <summary>
    /// Gets or sets the name of the unique constraint. A name will be 
    /// created for unnamed unique constraints. You must name your
    /// constraint if you want multiple fields in the constraint. If your 
    /// constraint has only one field, then this property can be ignored.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Gets or sets the position of the field in the constraint, lower 
    /// numbers come first. The order is undefined for two fields with 
    /// the same position. The default position is 0.
    /// </summary>
    public int Position { get; set; }
}

তারপরে, কোনও ধরণের থেকে ডাটাবেস সারণির নাম পেতে একটি দরকারী এক্সটেনশন অন্তর্ভুক্ত করুন:

public static class Extensions
{
    /// <summary>
    /// Get a table name for a class using a DbContext.
    /// </summary>
    /// <param name="context">
    /// The context.
    /// </param>
    /// <param name="type">
    /// The class to look up the table name for.
    /// </param>
    /// <returns>
    /// The table name; null on failure;
    /// </returns>
    /// <remarks>
    /// <para>
    /// Like:
    /// <code>
    ///   DbContext context = ...;
    ///   string table = context.GetTableName&lt;Foo&gt;();
    /// </code>
    /// </para>
    /// <para>
    /// This code uses ObjectQuery.ToTraceString to generate an SQL 
    /// select statement for an entity, and then extract the table
    /// name from that statement.
    /// </para>
    /// </remarks>
    public static string GetTableName(this DbContext context, Type type)
    {
        return ((IObjectContextAdapter)context)
               .ObjectContext.GetTableName(type);
    }

    /// <summary>
    /// Get a table name for a class using an ObjectContext.
    /// </summary>
    /// <param name="context">
    /// The context.
    /// </param>
    /// <param name="type">
    /// The class to look up the table name for.
    /// </param>
    /// <returns>
    /// The table name; null on failure;
    /// </returns>
    /// <remarks>
    /// <para>
    /// Like:
    /// <code>
    ///   ObjectContext context = ...;
    ///   string table = context.GetTableName&lt;Foo&gt;();
    /// </code>
    /// </para>
    /// <para>
    /// This code uses ObjectQuery.ToTraceString to generate an SQL 
    /// select statement for an entity, and then extract the table
    /// name from that statement.
    /// </para>
    /// </remarks>
    public static string GetTableName(this ObjectContext context, Type type)
    {
        var genericTypes = new[] { type };
        var takesNoParameters = new Type[0];
        var noParams = new object[0];
        object objectSet = context.GetType()
                            .GetMethod("CreateObjectSet", takesNoParameters)
                            .MakeGenericMethod(genericTypes)
                            .Invoke(context, noParams);
        var sql = (string)objectSet.GetType()
                  .GetMethod("ToTraceString", takesNoParameters)
                  .Invoke(objectSet, noParams);
        Match match = 
            Regex.Match(sql, @"FROM\s+(.*)\s+AS", RegexOptions.IgnoreCase);
        return match.Success ? match.Groups[1].Value : null;
    }
}

তারপরে, ডাটাবেস সূচনা:

/// <summary>
///     The database initializer.
/// </summary>
public class DatabaseInitializer : IDatabaseInitializer<PedContext>
{
    /// <summary>
    /// Initialize the database.
    /// </summary>
    /// <param name="context">
    /// The context.
    /// </param>
    public void InitializeDatabase(FooContext context)
    {
        // if the database has changed, recreate it.
        if (context.Database.Exists()
            && !context.Database.CompatibleWithModel(false))
        {
            context.Database.Delete();
        }

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

            // Look for database tables in the context. Tables are of
            // type DbSet<>.
            foreach (PropertyInfo contextPropertyInfo in 
                     context.GetType().GetProperties())
            {
                var contextPropertyType = contextPropertyInfo.PropertyType;
                if (contextPropertyType.IsGenericType
                    && contextPropertyType.Name.Equals("DbSet`1"))
                {
                    Type tableType = 
                        contextPropertyType.GetGenericArguments()[0];
                    var tableName = context.GetTableName(tableType);
                    foreach (var uc in UniqueConstraints(tableType, tableName))
                    {
                        context.Database.ExecuteSqlCommand(uc);
                    }
                }
            }

            // this is a good place to seed the database
            context.SaveChanges();
        }
    }

    /// <summary>
    /// Get a list of TSQL commands to create unique constraints on the given 
    /// table. Looks through the table for fields with the UniqueAttribute
    /// and uses those and the table name to build the TSQL strings.
    /// </summary>
    /// <param name="tableClass">
    /// The class that expresses the database table.
    /// </param>
    /// <param name="tableName">
    /// The table name in the database.
    /// </param>
    /// <returns>
    /// The list of TSQL statements for altering the table to include unique 
    /// constraints.
    /// </returns>
    private static IEnumerable<string> UniqueConstraints(
        Type tableClass, string tableName)
    {
        // the key is the name of the constraint and the value is a list 
        // of (position,field) pairs kept in order of position - the entry
        // with the lowest position is first.
        var uniqueConstraints = 
            new Dictionary<string, List<Tuple<int, string>>>();
        foreach (PropertyInfo entityPropertyInfo in tableClass.GetProperties())
        {
            var unique = entityPropertyInfo.GetCustomAttributes(true)
                         .OfType<UniqueAttribute>().FirstOrDefault();
            if (unique != null)
            {
                string fieldName = entityPropertyInfo.Name;

                // use the name field in the UniqueAttribute or create a
                // name if none is given
                string constraintName = unique.Name
                                        ?? string.Format(
                                            "constraint_{0}_unique_{1}",
                                            tableName
                                               .Replace("[", string.Empty)
                                               .Replace("]", string.Empty)
                                               .Replace(".", "_"),
                                            fieldName);

                List<Tuple<int, string>> constraintEntry;
                if (!uniqueConstraints.TryGetValue(
                        constraintName, out constraintEntry))
                {
                    uniqueConstraints.Add(
                        constraintName, 
                        new List<Tuple<int, string>> 
                        {
                            new Tuple<int, string>(
                                unique.Position, fieldName) 
                        });
                }
                else
                {
                    // keep the list of fields in order of position
                    for (int i = 0; ; ++i)
                    {
                        if (i == constraintEntry.Count)
                        {
                            constraintEntry.Add(
                                new Tuple<int, string>(
                                    unique.Position, fieldName));
                            break;
                        }

                        if (unique.Position < constraintEntry[i].Item1)
                        {
                            constraintEntry.Insert(
                                i, 
                                new Tuple<int, string>(
                                    unique.Position, fieldName));
                            break;
                        }
                    }
                }
            }
        }

        return
            uniqueConstraints.Select(
                uc =>
                string.Format(
                    "ALTER TABLE {0} ADD CONSTRAINT {1} UNIQUE ({2})",
                    tableName,
                    uc.Key,
                    string.Join(",", uc.Value.Select(v => v.Item2))));
    }
}

2

আমি প্রতিবিম্ব দ্বারা সমস্যাটি সমাধান করেছি (দুঃখিত, ভাবেন, ভিবি.নেট ...)

প্রথমে ইউনিক অ্যাট্রিবিউটের একটি বৈশিষ্ট্য নির্ধারণ করুন:

<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=True)> _
Public Class UniqueAttribute
    Inherits Attribute

End Class

তারপরে, আপনার মডেলটিকে পছন্দ করুন

<Table("Person")> _
Public Class Person

    <Unique()> _
    Public Property Username() As String

End Class

অবশেষে, একটি কাস্টম ডেটাবেসআইনিটিয়ালাইজার তৈরি করুন (আমার সংস্করণে, আমি ডিবি পরিবর্তন করলে ডিবি পরিবর্তন করি তবেই ডিবাগ মোডে ...)। এই ডাটাবেসআইনিটিয়ালাইজারে সূচকগুলি স্বতন্ত্র বৈশিষ্ট্যের উপর ভিত্তি করে তৈরি করা হয়:

Imports System.Data.Entity
Imports System.Reflection
Imports System.Linq
Imports System.ComponentModel.DataAnnotations

Public Class DatabaseInitializer
    Implements IDatabaseInitializer(Of DBContext)

    Public Sub InitializeDatabase(context As DBContext) Implements IDatabaseInitializer(Of DBContext).InitializeDatabase
        Dim t As Type
        Dim tableName As String
        Dim fieldName As String

        If Debugger.IsAttached AndAlso context.Database.Exists AndAlso Not context.Database.CompatibleWithModel(False) Then
            context.Database.Delete()
        End If

        If Not context.Database.Exists Then
            context.Database.Create()

            For Each pi As PropertyInfo In GetType(DBContext).GetProperties
                If pi.PropertyType.IsGenericType AndAlso _
                    pi.PropertyType.Name.Contains("DbSet") Then

                    t = pi.PropertyType.GetGenericArguments(0)

                    tableName = t.GetCustomAttributes(True).OfType(Of TableAttribute).FirstOrDefault.Name
                    For Each piEntity In t.GetProperties
                        If piEntity.GetCustomAttributes(True).OfType(Of Model.UniqueAttribute).Any Then

                            fieldName = piEntity.Name
                            context.Database.ExecuteSqlCommand("ALTER TABLE " & tableName & " ADD CONSTRAINT con_Unique_" & tableName & "_" & fieldName & " UNIQUE (" & fieldName & ")")

                        End If
                    Next
                End If
            Next

        End If

    End Sub

End Class

সম্ভবত এটি সাহায্য করে ...


1

আপনি যদি আপনার DbContext শ্রেণিতে ValidateEntity পদ্ধতিটি ওভাররাইড করে থাকেন তবে আপনি সেখানে যুক্তিও যুক্ত করতে পারেন। এখানে সুবিধাটি হ'ল আপনার সমস্ত ডিবিসেটের সম্পূর্ণ অ্যাক্সেস থাকবে। এখানে একটি উদাহরণ:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Data.Entity.Validation;
using System.Linq;

namespace MvcEfClient.Models
{
    public class Location
    {
        [Key]
        public int LocationId { get; set; }

        [Required]
        [StringLength(50)]
        public string Name { get; set; }
    }

    public class CommitteeMeetingContext : DbContext
    {
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }

        protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
        {
            List<DbValidationError> validationErrors = new List<DbValidationError>();

            // Check for duplicate location names

            if (entityEntry.Entity is Location)
            {
                Location location = entityEntry.Entity as Location;

                // Select the existing location

                var existingLocation = (from l in Locations
                                        where l.Name == location.Name && l.LocationId != location.LocationId
                                        select l).FirstOrDefault();

                // If there is an existing location, throw an error

                if (existingLocation != null)
                {
                    validationErrors.Add(new DbValidationError("Name", "There is already a location with the name '" + location.Name + "'"));
                    return new DbEntityValidationResult(entityEntry, validationErrors);
                }
            }

            return base.ValidateEntity(entityEntry, items);
        }

        public DbSet<Location> Locations { get; set; }
    }
}

1

আপনি যদি EF5 ব্যবহার করছেন এবং এখনও এই প্রশ্নটি থাকে তবে নীচের সমাধানটি আমার জন্য এটি সমাধান করে।

আমি কোডটি প্রথম পদ্ধতির ব্যবহার করছি, অতএব:

this.Sql("CREATE UNIQUE NONCLUSTERED INDEX idx_unique_username ON dbo.Users(Username) WHERE Username IS NOT NULL;");

মাইগ্রেশন স্ক্রিপ্টে কাজটি ভালভাবে করেছে। এটি নাল মানকেও অনুমোদন করে!


1

EF কোড প্রথম পদ্ধতির সাথে, কেউ নিম্নলিখিত কৌশলটি ব্যবহার করে অ্যাট্রিবিউট-ভিত্তিক অনন্য বাধা সমর্থন বাস্তবায়ন করতে পারে।

একটি চিহ্নিতকারী বৈশিষ্ট্য তৈরি করুন

[AttributeUsage(AttributeTargets.Property)]
public class UniqueAttribute : System.Attribute { }

সত্তা হিসাবে আপনি বৈশিষ্ট্য অনন্য হতে চান চিহ্নিত করুন

[Unique]
public string EmailAddress { get; set; }

একটি অনন্য সীমাবদ্ধতা তৈরি করতে একটি ডাটাবেস ইনিশিয়ালাইজার তৈরি করুন বা বিদ্যমান ব্যবহার করুন

public class DbInitializer : IDatabaseInitializer<DbContext>
{
    public void InitializeDatabase(DbContext db)
    {
        if (db.Database.Exists() && !db.Database.CompatibleWithModel(false))
        {
            db.Database.Delete();
        }

        if (!db.Database.Exists())
        {
            db.Database.Create();
            CreateUniqueIndexes(db);
        }
    }

    private static void CreateUniqueIndexes(DbContext db)
    {
        var props = from p in typeof(AppDbContext).GetProperties()
                    where p.PropertyType.IsGenericType
                       && p.PropertyType.GetGenericTypeDefinition()
                       == typeof(DbSet<>)
                    select p;

        foreach (var prop in props)
        {
            var type = prop.PropertyType.GetGenericArguments()[0];
            var fields = from p in type.GetProperties()
                         where p.GetCustomAttributes(typeof(UniqueAttribute),
                                                     true).Any()
                         select p.Name;

            foreach (var field in fields)
            {
                const string sql = "ALTER TABLE dbo.[{0}] ADD CONSTRAINT"
                                 + " [UK_dbo.{0}_{1}] UNIQUE ([{1}])";
                var command = String.Format(sql, type.Name, field);
                db.Database.ExecuteSqlCommand(command);
            }
        }
    }   
}

প্রারম্ভিকাল কোডটিতে এই ইনিশিয়ালাইজারটি ব্যবহার করতে আপনার ডাটাবেস প্রসঙ্গটি সেট করুন (যেমন main()বা এর মধ্যে Application_Start())

Database.SetInitializer(new DbInitializer());

সংমিশ্রণ কীগুলি সমর্থন না করার সরলকরণ সহ সমাধানটি মহিমানের সমান। EF 5.0+ ব্যবহার করতে হবে।


1

সাবলীল এপি সমাধান:

modelBuilder.Entity<User>(entity =>
{
    entity.HasIndex(e => e.UserId)
          .HasName("IX_User")
          .IsUnique();

    entity.HasAlternateKey(u => u.Email);

    entity.HasIndex(e => e.Email)
          .HasName("IX_Email")
          .IsUnique();
});

0

আমি আজ সেই সমস্যার মুখোমুখি হয়েছি এবং শেষ পর্যন্ত আমি এটি সমাধান করতে সক্ষম হয়েছি। আমি জানি না যে সঠিক পন্থা কিনা তবে কমপক্ষে আমি চালিয়ে যেতে পারি:

public class Person : IValidatableObject
{
    public virtual int ID { get; set; }
    public virtual string Name { get; set; }


    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var field = new[] { "Name" }; // Must be the same as the property

        PFContext db = new PFContext();

        Person person = validationContext.ObjectInstance as Person;

        var existingPerson = db.Persons.FirstOrDefault(a => a.Name == person.Name);

        if (existingPerson != null)
        {
            yield return new ValidationResult("That name is already in the db", field);
        }
    }
}

0

একটি অনন্য সম্পত্তি বৈধকারক ব্যবহার করুন।

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) {
   var validation_state = base.ValidateEntity(entityEntry, items);
   if (entityEntry.Entity is User) {
       var entity = (User)entityEntry.Entity;
       var set = Users;

       //check name unique
       if (!(set.Any(any_entity => any_entity.Name == entity.Name))) {} else {
           validation_state.ValidationErrors.Add(new DbValidationError("Name", "The Name field must be unique."));
       }
   }
   return validation_state;
}

ValidateEntityএকই ডাটাবেস লেনদেনের মধ্যে বলা হয় না। অতএব, ডাটাবেসে অন্যান্য সত্তার সাথে জাতিগুলির শর্ত থাকতে পারে। SaveChanges(এবং তাই, ValidateEntity) চারপাশে লেনদেন জোর করতে আপনাকে কিছুটা EF হ্যাক করতে হবে । DBContextসরাসরি সংযোগটি খুলতে পারে না, তবে ObjectContextপারে।

using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required)) {
   ((IObjectContextAdapter)data_context).ObjectContext.Connection.Open();
   data_context.SaveChanges();
   transaction.Complete();
}


0

এই প্রশ্নটি পড়ার পরে আমার নিজস্ব প্রশ্ন ছিল মিহ্কেল মুরস , টোবিয়াস শিটকভস্কি এবং মহিমানের উত্তরগুলির মতো অনন্য কী হিসাবে বৈশিষ্ট্যগুলি নির্ধারণের জন্য একটি বৈশিষ্ট্য প্রয়োগ করার চেষ্টা করার সময় : মানচিত্র সত্তা ফ্রেমওয়ার্ক কোড বৈশিষ্ট্যগুলি ডাটাবেস কলামগুলিতে (এসএসস্পেসে সিএস স্পেস)

অবশেষে আমি এই উত্তরে এসে পৌঁছলাম যা স্কেলার এবং নেভিগেশন উভয় বৈশিষ্ট্যকে ডেটাবেস কলামগুলিতে ম্যাপ করতে এবং বৈশিষ্ট্যের উপর ভিত্তি করে নির্দিষ্ট সিকোয়েন্সে একটি অনন্য সূচক তৈরি করতে পারে। এই কোডটি ধরে নেওয়া হয়েছে যে আপনি একটি সিকোয়েন্স সম্পত্তি নিয়ে একটি অনন্যঅ্যাট্রিবিউট প্রয়োগ করেছেন এবং এটি EF সত্তা শ্রেণীর বৈশিষ্ট্যে প্রয়োগ করেছেন যা সত্তার অনন্য কী (প্রাথমিক কী ব্যতীত) উপস্থাপন করবে should

দ্রষ্টব্য: এই কোডটি EF সংস্করণ 6.1 (বা পরে) এর উপর নির্ভর করে যা EntityContainerMappingপূর্ববর্তী সংস্করণগুলিতে উপলব্ধ নয়।

Public Sub InitializeDatabase(context As MyDB) Implements IDatabaseInitializer(Of MyDB).InitializeDatabase
    If context.Database.CreateIfNotExists Then
        Dim ws = DirectCast(context, System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext.MetadataWorkspace
        Dim oSpace = ws.GetItemCollection(Core.Metadata.Edm.DataSpace.OSpace)
        Dim entityTypes = oSpace.GetItems(Of EntityType)()
        Dim entityContainer = ws.GetItems(Of EntityContainer)(DataSpace.CSpace).Single()
        Dim entityMapping = ws.GetItems(Of EntityContainerMapping)(DataSpace.CSSpace).Single.EntitySetMappings
        Dim associations = ws.GetItems(Of EntityContainerMapping)(DataSpace.CSSpace).Single.AssociationSetMappings
        For Each setType In entityTypes
           Dim cSpaceEntitySet = entityContainer.EntitySets.SingleOrDefault( _
              Function(t) t.ElementType.Name = setType.Name)
           If cSpaceEntitySet Is Nothing Then Continue For ' Derived entities will be skipped
           Dim sSpaceEntitySet = entityMapping.Single(Function(t) t.EntitySet Is cSpaceEntitySet)
           Dim tableInfo As MappingFragment
           If sSpaceEntitySet.EntityTypeMappings.Count = 1 Then
              tableInfo = sSpaceEntitySet.EntityTypeMappings.Single.Fragments.Single
           Else
              ' Select only the mapping (esp. PropertyMappings) for the base class
              tableInfo = sSpaceEntitySet.EntityTypeMappings.Where(Function(m) m.IsOfEntityTypes.Count _
                 = 1 AndAlso m.IsOfEntityTypes.Single.Name Is setType.Name).Single().Fragments.Single
           End If
           Dim tableName = If(tableInfo.StoreEntitySet.Table, tableInfo.StoreEntitySet.Name)
           Dim schema = tableInfo.StoreEntitySet.Schema
           Dim clrType = Type.GetType(setType.FullName)
           Dim uniqueCols As IList(Of String) = Nothing
           For Each propMap In tableInfo.PropertyMappings.OfType(Of ScalarPropertyMapping)()
              Dim clrProp = clrType.GetProperty(propMap.Property.Name)
              If Attribute.GetCustomAttribute(clrProp, GetType(UniqueAttribute)) IsNot Nothing Then
                 If uniqueCols Is Nothing Then uniqueCols = New List(Of String)
                 uniqueCols.Add(propMap.Column.Name)
              End If
           Next
           For Each navProp In setType.NavigationProperties
              Dim clrProp = clrType.GetProperty(navProp.Name)
              If Attribute.GetCustomAttribute(clrProp, GetType(UniqueAttribute)) IsNot Nothing Then
                 Dim assocMap = associations.SingleOrDefault(Function(a) _
                    a.AssociationSet.ElementType.FullName = navProp.RelationshipType.FullName)
                 Dim sProp = assocMap.Conditions.Single
                 If uniqueCols Is Nothing Then uniqueCols = New List(Of String)
                 uniqueCols.Add(sProp.Column.Name)
              End If
           Next
           If uniqueCols IsNot Nothing Then
              Dim propList = uniqueCols.ToArray()
              context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_" & tableName & "_" & String.Join("_", propList) _
                 & " ON " & schema & "." & tableName & "(" & String.Join(",", propList) & ")")
           End If
        Next
    End If
End Sub

0

কোডগুলি প্রথম কনফিগারেশন ব্যবহার করে তাদের জন্য আপনি ইনডেক্সট্রিবিউট অবজেক্টটি কলামআনোটেশন হিসাবে ব্যবহার করতে পারেন এবং এর ইসুনিক সম্পত্তিটিকে সত্য হিসাবে সেট করতে পারেন।

উদাহরণে:

var indexAttribute = new IndexAttribute("IX_name", 1) {IsUnique = true};

Property(i => i.Name).HasColumnAnnotation("Index",new IndexAnnotation(indexAttribute));

এটি নাম কলামে আইএক্স_নাম নামের একটি অনন্য সূচক তৈরি করবে।


0

দেরিতে উত্তরের জন্য দুঃখিত তবে আমি আপনার সাথে এটি শেপ করে ভাল লাগলাম

আমি কোড প্রকল্পে এ সম্পর্কে পোস্ট করেছি

সাধারণভাবে, এটি আপনার অনন্য সূচকগুলি তৈরি করতে আপনি ক্লাসগুলিতে যে বৈশিষ্ট্যগুলি রেখেছিলেন তার উপর নির্ভর করে

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.