В этом коде у меня есть модель 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;
}
}
}