Уникальное значение в таблице с использованием EF4 и блокировки - PullRequest
2 голосов
/ 09 июня 2010

В этом коде у меня есть модель Entity Framework 4 с одной сущностью Thing, в которой есть столбец Id и имя (строка). Я хотел бы убедиться, что когда я вызываю FindOrCreateThing (name) из нескольких потоков, только одна строка в таблице Things будет создана с данным именем.

В настоящее время я использую блокировки для достижения этой цели, и, похоже, это работает ... но какие есть лучшие способы? Как этот общий сценарий обрабатывается в других проектах?

Спасибо!

class Program
{
    private static string[] names = new string[] { "Alpha", "Beta", "Delta", "Gamma", "Zeta" };

    static void Main(string[] args)
    {
        // Multiple threads trying to create things, often with the same name,
        // but only allow one thread to actually create the record if it doesn't
        // exist.
        for (int i = 0; i < 100; i++)
        {
            Thread thread = new Thread(new ThreadStart(MakeThings));
            thread.Start();
        }
    }

    static void MakeThings()
    {
        try
        {
            foreach (var name in names)
            {
                Thing t = FindOrCreateThing(name);
                Console.WriteLine("Thing record returned: id={0}; name={1}", t.Id, t.Name);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    private static object createLock = new object();
    private static Thing FindOrCreateThing(string name)
    {
        using (EFModel context = new EFModel())
        {
            // Find the record. If it already exists, return it--we're done.
            var thing = (from t in context.Things
                         where t.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)
                         select t).SingleOrDefault();
            if (thing == null)
            {
                // The record does not exist, so wait for lock.
                // This will prevent multiple threads from trying to
                // create the same record simultaneously.
                lock (createLock)
                {
                    // Single thread is here... check if a thread before us
                    // has already created the record. (I hate having to do this
                    // same query twice!)
                    thing = (from t in context.Things
                             where t.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)
                             select t).SingleOrDefault();
                    if (thing == null)
                    {
                        // We're the first thread here, so create the record.
                        // This should mean that the record is unique in the table.
                        thing = new Thing { Name = name };
                        context.Things.AddObject(thing);
                        context.SaveChanges();
                    }
                }
            }
            return thing;
        }
    }
}

Ответы [ 2 ]

1 голос
/ 09 июня 2010

Просто наложите уникальное ограничение на столбец БД. Затем вы можете избавиться от всех блокировок и поймать (маловероятное, но возможное) исключение, которое вы получите, если будете искать, ничего не находить и создавать, тогда как другой поток делает то же самое. Если вы поймете это, просто повторите весь процесс.

0 голосов
/ 09 июня 2010

Если хранилище за сущностью - это некоторая СУБД, то, вероятно, было бы более эффективно создать уникальный индекс для столбца.Это уменьшит количество обращений к серверу, и вам не нужно будет блокировать на стороне клиента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...