我正在进入"懒惰的IO问题"在Linq,我还没有找到一个我满意的解决方案
假设我们有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被释放且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通常会生成非常优雅的代码,这根本不是优雅的。它也倾向于程序员
有点像我在这里采取错误的方法。
答案 0 :(得分:2)
我之前也遇到过这个问题,但幸运的是实体框架提供了一种简单的方法。您可以在查询之前禁用延迟加载和动态代理的创建。这将允许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);
}
}
答案 1 :(得分:1)
真正的问题是你的调用者应该指定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);
}
}
答案 2 :(得分:1)
在Newtonsoft中序列化对象时,请参阅this question有关忽略属性的信息,我相信这是包含JsonConvert.SerializeObject
函数的库。
总结most popular answer是将[JsonIgnore]
属性添加到您不希望序列化的字段中。在您的情况下,这是Owner
所以代码将是:
[JsonIgnore]
public Person Owner{ get; set; }
原始海报最终使用了own answer中所述的虚拟属性。