Linq到SQL和并发与Rob Conery存储库模式 [英] Linq to SQL and concurrency with Rob Conery repository pattern

查看:164
本文介绍了Linq到SQL和并发与Rob Conery存储库模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用Rob Conery在储存库模式(从MVC Storefront项目)中实现了一个DAL,其中使用Linq将数据库对象映射到域对象,并使用Linq to SQL实际获取数据。

I have implemented a DAL using Rob Conery's spin on the repository pattern (from the MVC Storefront project) where I map database objects to domain objects using Linq and use Linq to SQL to actually get the data.

这一切都在奇妙地让我完全控制我想要的域对象的形状,但我遇到了一个并发问题,我想我会在这里问。我有并发工作,但解决方案感觉像是可能是错误的(只是其中一个gitchy感觉)。

This is all working wonderfully giving me the full control over the shape of my domain objects that I want, but I have hit a problem with concurrency that I thought I'd ask about here. I have concurrency working but the solution feels like it might be wrong (just one of those gitchy feelings).

基本模式是:

private MyDataContext _datacontext
private Table _tasks;

public Repository(MyDataContext datacontext)
{
    _dataContext = datacontext;
}

public void GetTasks()
{
    _tasks = from t in _dataContext.Tasks;

    return from t in _tasks
        select new Domain.Task
        {
            Name = t.Name,
            Id = t.TaskId,
            Description = t.Description                              
        };
}

public void SaveTask(Domain.Task task)
{
    Task dbTask = null;

    // Logic for new tasks omitted...

    dbTask = (from t in _tasks
        where t.TaskId == task.Id
        select t).SingleOrDefault();

    dbTask.Description = task.Description,
        dbTask.Name = task.Name,

    _dataContext.SubmitChanges();
} 

所以使用这个实现我丢失了并发跟踪,因为映射到域任务。我通过存储私有表,它是在获得原始任务时任务的数据上下文列表,我得到它。

So with that implementation I've lost concurrency tracking because of the mapping to the domain task. I get it back by storing the private Table which is my datacontext list of tasks at the time of getting the original task.

然后我从这个存储的表更新任务,保存我更新的

I then update the tasks from this stored Table and save what I've updated

这是工作 - 我遇到变化冲突异常时出现并发冲突,正如我想要的。

This is working - I get change conflict exceptions raised when there are concurrency violations, just as I want.

但是,它只是尖叫着我,我错过了一个把戏。

However, it just screams to me that I've missed a trick.

这样做吗?

我已经看过了数据报文上的.Attach方法,但似乎需要以类似的方式存储原来的版本, 。

I've looked at the .Attach method on the datacontext but that appears to require storing the original version in a similar way to what I'm already doing.

我也知道我可以通过删除域对象,让Linq到SQL生成的对象一直到我的堆栈,避免所有这一切 - 但我

I also know that I could avoid all this by doing away with the domain objects and letting the Linq to SQL generated objects all the way up my stack - but I dislike that just as much as I dislike the way I'm handling concurrency.

推荐答案

我通过这个工作,发现了以下解决方案。

I worked through this and found the following solution. It works in all the test cases I (and more importantly, my testers!) can think of.

我使用的是 .Attach() 方法,以及一个TimeStamp列。这工作正常,你第一次将一个特定的主键保存回数据库,但我发现datacontext抛出一个 System.Data.Linq.DuplicateKeyException 无法添加

I am using the .Attach() method on the datacontext, and a TimeStamp column. This works fine for the first time that you save a particular primary key back to the database but I found that the datacontext throws a System.Data.Linq.DuplicateKeyException "Cannot add an entity with a key that is already in use."

我创建的工作是添加一个字典,存储我第一次附加的项目,然后每次我保存我重用该项。

The work around for this I created was to add a dictionary that stored the item I attach the first time around and then every subsequent time I save I reuse that item.

示例代码如下,我想知道是否我错过了任何技巧 - 并发是相当根本的,

Example code is below, I do wonder if I've missed any tricks - concurrency is pretty fundamental so the hoops I'm jumping through seem a little excessive.

希望以下证明是有用的,或者有人可以指示我更好的实现!

Hopefully the below proves useful, or someone can point me towards a better implementation!

private Dictionary<int, Payment> _attachedPayments;

public void SavePayments(IList<Domain.Payment> payments)
    {
        Dictionary<Payment, Domain.Payment> savedPayments =
            new Dictionary<Payment, Domain.Payment>();

        // Items with a zero id are new
        foreach (Domain.Payment p in payments.Where(p => p.PaymentId != 0))
        {
            // The list of attached payments that works around the linq datacontext  
            // duplicatekey exception
            if (_attachedPayments.ContainsKey(p.PaymentId)) // Already attached
            {
                Payment dbPayment = _attachedPayments[p.PaymentId];                    
                // Just a method that maps domain to datacontext types
                MapDomainPaymentToDBPayment(p, dbPayment, false);
                savedPayments.Add(dbPayment, p);
            }
            else // Attach this payment to the datacontext
            {
                Payment dbPayment = new Payment();
                MapDomainPaymentToDBPayment(p, dbPayment, true);
                _dataContext.Payments.Attach(dbPayment, true);
                savedPayments.Add(dbPayment, p);
            }
        }

        // There is some code snipped but this is just brand new payments
        foreach (var payment in newPayments)
        {
            Domain.Payment payment1 = payment;
            Payment newPayment = new Payment();
            MapDomainPaymentToDBPayment(payment1, newPayment, false);
            _dataContext.Payments.InsertOnSubmit(newPayment);
            savedPayments.Add(newPayment, payment);
        }

        try
        {
            _dataContext.SubmitChanges();
            // Grab the Timestamp into the domain object
            foreach (Payment p in savedPayments.Keys)
            {
                savedPayments[p].PaymentId = p.PaymentId;
                savedPayments[p].Timestamp = p.Timestamp;
                _attachedPayments[savedPayments[p].PaymentId] = p;
            }
        }
        catch (ChangeConflictException ex)
        {
            foreach (ObjectChangeConflict occ in _dataContext.ChangeConflicts)
            {
                Payment entityInConflict = (Payment) occ.Object;

                // Use the datacontext refresh so that I can display the new values
                _dataContext.Refresh(RefreshMode.OverwriteCurrentValues, entityInConflict);
                _attachedPayments[entityInConflict.PaymentId] = entityInConflict;

            }
            throw;
        }

    }

这篇关于Linq到SQL和并发与Rob Conery存储库模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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