如何正确使用LINQ2SQL DataContext对象;每次使用都会导致头痛

时间:2011-02-24 07:01:07

标签: c# wpf linq-to-sql

所以,在学习LINQ2SQL时(请不要告诉我这是一个死技术,我不是为简单的应用程序切换到EF)我已多次读过DataContext类经常被创建和销毁。伟大的,一个轻量级的对象,我可以用来访问我的数据库。然而,在实践中,这似乎只会带来麻烦。例如,如果我加载一个DataGrid,那么似乎没有跟踪对实体对象的更改,并且调用DataContext.SubmitChanges没有效果:

void LoadUI()
{
    using( var db = new TestDataContext() )
    {
        // use DataLoadOptions.LoadWith<T> if we need 
        // access to foreign key/ deferred objects.
        masterGrid.ItemsSource = db.Customers.ToList();
        detailsGrid.ItemsSource = // this is always set to a collection of Settings
                                  // for the currently selected user in masterGrid.
                                  // just a simple foreign key relationship
    }
}

void UpdateName( string newName )
{
    using( var db = new TestDataContext() )
    {
        var customer = ((Customer)masterGrid.SelectedItem);
        customer.Name = newName;
        db.SubmitChanges() // !!! database is not updated !!!    
    }
}

那是什么给出的?在访问数据库时创建一个新的DataContext对象似乎是可接受/首选的做法,但它似乎并不适用于最微不足道的情况。似乎对象跟踪在这种情况下不起作用,那么在实践中如何可能使用这样的DataContext类?我在这里错过了什么吗? (注意:我真的希望我实际上缺少一些简单的东西。

我必须假设情况确实如此,但我不知道答案是什么。目前我只是在应用程序的生命周期中保留一个DC,我知道这可能不是一个好主意)。如果您有任何建议,请提前致谢。

顺便说一句,如果你们中的任何人都知道一篇全面的文章/系列文章,它们描述了DataContext课程的细节,我很乐意阅读它。我没有找到任何超出简单更新/插入/删除的例子。

3 个答案:

答案 0 :(得分:2)

答案是你有2种方法(还有更多!)来解决这个问题。

网格中的对象实例属于DataContext#1。在UpdateName方法中,您将创建一个新的DataContext#2。 DataContext#2如何知道DataContext#1中的对象已更改?决不。所以:使用DataContext#2从数据库中检索Customer对象,并使用检索到的Object来更改属性。

另一种方法是使用DTO。这种方式很难描述,但原则是你创建了一个类,它拥有客户拥有的所有属性,但没有别的(=&gt;这个类不再连接到你的DataContext)并且在更改了它的属性之后你将类重新连接到DataContext并保存。

希望我能帮到你。

void LoadUI()
{
    using( var db = new TestDataContext() )
    {
        // use DataLoadOptions.LoadWith<T> if we need 
        // access to foreign key/ deferred objects.
        masterGrid.ItemsSource = db.Customers.ToList();
        detailsGrid.ItemsSource = // this is always set to a collection of Settings
                                  // for the currently selected user in masterGrid.
                                  // just a simple foreign key relationship
    }
}

void UpdateName( string newName )
{
    using( var db = new TestDataContext() )
    {
        var customer = ((Customer)masterGrid.SelectedItem);
        var freshLoadedCustomer = db.Customers.FirstOrDefault(cust=>cust.Id == customer.Id);
        if(freshLoadedCustomer != null)
        { freshLoadedCustomer.Name = newName; db.SubmitChanges();}
    }
}

答案 1 :(得分:1)

更改的对象只能使用创建它们的datacontext进行更新。通常为每个工作单元创建一次datacontext(例如,获取某些对象的页面或表单,使用它做一些事情并在数据库中更新它们)。

编辑2,对FooWombat评论的回应:有关更真实的例子,请阅读Rick Strahl博客上的Datacontext Lifetime Management。我深入了解如何实现处理datacontext以便一切都在幕后发生,你可以选择/更新/删除你的linq2sql对象,而不用考虑(很多)它。

编辑1:此外,如果你稍微谷歌,你还会找到一个名为Attach的方法。此方法旨在用于已反序列化然后序列化的对象。它提到不要尝试从另一个datacontext附加对象。但是,在这个blogpost中你有一个例子(ab)使用Attach,这样你就可以做到这一点。如果你处于紧要关头并且真的需要将对象从一个datacontext转移到另一个datacontext,我只会使用它。这不是一个真正推荐的做法。

来自博文:

public partial class Employee
{
   public void Detach()
   {
       this.PropertyChanged = null; this.PropertyChanging = null;
       // Assuming there's a foreign key from Employee to Boss
       this.Boss = default(EntityRef<Boss>);
       // Similarly set child objects to default as well
       this.Subordinates = default(EntitySet<Subordinate>);
   }
}

您可以使用此Detach方法执行此操作:

public static void UpdateEmployee(Employee employee)
{
    using (HRDataContext dataContext = new HRDataContext())
    {
        //attach to datacontext
        employee.Detach();
        dataContext.Employees.Attach(employee);
        //save changes
        dataContext.SubmitChanges();
    }
}

答案 2 :(得分:1)

神奇的词汇是“数据绑定”。这就是显示数据所需的内容,也是将数据保存回数据库所需的内容。

上述代码不起作用的原因是它提供了读取数据绑定路径,但不提供写入数据绑定路径。换句话说,您已经从DataContext创建了一个只读列表,但是您没有提供将更改写回数据库的实际方法。

最简单的解决方案可能是为数据网格创建可写数据路径,而不是尝试手动跟踪和保存数据网格中的更改。有很多方法可以做到这一点。我知道的一种方法是使用ObservableCollection. ObservableCollection在添加,删除项目或刷新整个列表时提供通知。这允许您将代码挂钩到这些事件中以提供保存功能。

但是,最初您最好的选择可能是this search.