使用EF4和锁定的表中的唯一值

时间:2010-06-09 13:23:40

标签: multithreading entity-framework unique

在此代码中,我有一个Entity Framework 4模型,其中包含一个具有Id和Name(字符串)列的“Thing”实体。我想确保当我从多个线程调用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 个答案:

答案 0 :(得分:1)

只需在DB列上添加一个唯一约束即可。然后你可以摆脱所有的锁定,并捕获(如果你搜索,找不到任何东西,并创建)你将获得的(不太可能,但可能)异常,而另一个线程做同样的事情。如果你抓住了,只需重试整个过程。

答案 1 :(得分:0)

如果实体后面的商店是某个RDBMS,那么在列上创建唯一索引可能会更有效。它会减少到服务器的往返,你不需要在客户端进行锁定。