使用EF4和锁定表中的唯一值 [英] Unique value in table using EF4 and locking

查看:131
本文介绍了使用EF4和锁定表中的唯一值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这段代码中,我有一个具有一个Thing实体的Entity Framework 4模型,它具有一个Id和一个Name(string)列。我想确保当我从多个线程调用FindOrCreateThing(name)时,将不会使用给定的名称创建Things表中的一行。



目前,我正在使用锁来实现这一点,它似乎工作...但是,有什么更好的方法?



谢谢!

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

static void Main(string [] args)
{
//尝试创建东西的多个线程,通常使用相同的名称,
//但只允许一个线程实际创建记录,如果它不
//存在。 (int i = 0; i <100; i ++)
{
线程=新线程(新的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());
}
}

私有静态对象createLock = new object();
private static Thing FindOrCreateThing(string name)
{
using(EFModel context = new EFModel())
{
//查找记录。如果它已经存在,返回 - 我们完成了。
var thing =(from t in context.Things
where t.Name.Equals(name,StringComparison.CurrentCultureIgnoreCase)
select t).SingleOrDefault();
if(thing == null)
{
//记录不存在,所以等待锁定。
//这将阻止多个线程尝试
//同时创建相同的记录。
lock(createLock)
{
//单线程在这里...检查我们之前的线程
//已经创建了记录。 (我讨厌这样做
//同一个查询两次!)
thing =(from t in context.Things
其中t.Name.Equals(name,StringComparison.CurrentCultureIgnoreCase)
select t).SingleOrDefault();
if(thing == null)
{
//我们是这里的第一个线程,所以创建记录。
//这应该意味着记录在表中是唯一的。
thing = new Thing {Name = name};
context.Things.AddObject(thing);
context.SaveChanges();
}
}
}
return thing;
}
}
}


解决方案

只需在DB列上放一个唯一约束。然后,您可以摆脱所有的锁定,并抓住(不太可能但可能)的异常,如果您搜索,找不到任何内容并创建,而另一个线程也会执行相同的操作。如果你抓住这个,只是重试整个过程。


In this code, I have an Entity Framework 4 model with a single "Thing" entity that has an Id and a Name(string) column. I would like to ensure that when I call FindOrCreateThing(name) from multiple threads, only one row in the Things table will ever be created with the given name.

Currently, I'm using locks to accomplish this, and it seems to work... but, what are some better ways? How is this common scenario handled in other projects?

Thanks!

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

解决方案

Just put a unique constraint on the DB column. You can then get rid of all of the locking, and catch the (unlikely, but possible) exception you'll get if you search, find nothing, and create, while another thread does the same thing. If you catch that, just retry the whole process.

这篇关于使用EF4和锁定表中的唯一值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆