繁体   English   中英

使用LINQ(LINQ2SQL)避免ObjectDisposedException

[英]Avoiding ObjectDisposedException with LINQ (LINQ2SQL)

我遇到了Linq的“懒惰IO问题”,我还没有找到一个我很满意的解决方案

设置问题

假设我们有SQL表,看起来像

create table Person (
    id int primary key not null,
    name text not null,
)

create table Dog (
    name text primary key not null,
    ownerid text primary key not null references Person(name)
)

在C#中,我们希望使用LINQ和Entity Framework来处理这个问题。 实体框架类被定义为partial因此我们可以扩展它们以添加.Get(string)方法,这使得代码非常干净。

public partial class Dog 
{
    public static Dog Get(string dogname) 
    {
        using (var db = new MyDataContext())
        {
            // LINQ is lazy and doesn't load the referenced Person
            return db.Dogs.Single(d => d.name == dogname);
        }
    }
}

问题发生的地方

现在我们尝试使用Dog对象

public string DogJson(string dogname) 
{
    var dog = Dog.Get(dogname);
    return JsonConvert.SerializeObject(dog);
}

由于我们的实例dog包含dog.Owner因为外键, JsonConvert当然会尝试将它包含在json字符串中。 但是由于DataContext被ObjectDisposedException且LINQ是惰性的,因此当然会引发ObjectDisposedException ,因为在我们处理DataContext时没有评估dog.Person

在这种情况下,我们根本不关心Owner对象,我们只想将Dog序列化为json。 没有这种方法最好的方法是什么?

我的解决方案

我有一个解决方案,但我不是特别喜欢它。 使用投影到一个不祥的对象并回滚到Dog ,因为我们不允许在查询中显式构造一个Dog

public static Dog Get(string dogname)
{
    using (var db = new MyDataContext())
    {
        var tmpdog = db.Dogs.Where(d => d.name == dogname)
            .Select(d => new { name = d.name, ownerid = d.ownerid}).Single();
        return new Dog() { name = tmpdog.name, ownerid = tmpdog.ownerid};
    }
}

我不喜欢这种解决方案,因为它不能很好地扩展。 这个例子只有两个属性,这很快就会失控。 LINQ通常会生成非常优雅的代码,这根本不是优雅的。 它也很容易受到程序员的影响

有点像我在这里采取错误的方法。

我之前也遇到过这个问题,但幸运的是实体框架提供了一种简单的方法。 您可以在查询之前禁用延迟加载和动态代理的创建。 这将允许json序列化器无异常运行。

public static Dog Get(string dogname) 
{
    using (var db = new MyDataContext())
    {
        db.Configuration.ProxyCreationEnabled = false;
        db.Configuration.LazyLoadingEnabled = false;
        return db.Dogs.Single(d => d.name == dogname);
    }
}

真正的问题是你的调用者应该决定DbContext的生命周期,而不是被调用者,因为DogJson方法定义了工作单元。 理想情况下,您应该将DbContext实例传递给静态Get方法。

所以,你的Get代码看起来应该更像这样:

public static Dog Get(string dogname, MyDataContext db)
{
    var result = db.Dogs.SingleOrDefault(d => d.name == dogname);
    return result;
}

然后,您可以在调用者中执行所有DTO修改,因为这确实是您的工作单元:

public string DogJson(string dogname) 
{
    using (var db = new MyDataContext())
    {
        var dog = Dog.Get(dogname, db);
        var dogDTO = new Dog { name = dog.name, ownerid = dog.ownerid };
        return JsonConvert.SerializeObject(dogDTO);
    }
}

在Newtonsoft中序列化对象时,请看这个关于忽略属性的问题 ,我相信它是包含JsonConvert.SerializeObject函数的库。

总结一下, 最流行的答案是将[JsonIgnore]属性添加到您不希望序列化的字段中。 在你的情况下,那是Owner所以代码将是:

[JsonIgnore]
public Person Owner{ get; set; }

原始海报最终使用了他们自己的答案中提到的虚拟属性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM