梦想成为现实:在Enitity Framework中以理想方式实现指定字段查询
2011-04-01 08:32
645 查看
在之前的随笔“博客园现代化建设—[Entity Framework]在LINQ查询中指定返回的字段”中,我们找到了问题的原因,却没有找到解决方法。
而对于理想中的解决方法,我们依然恋恋不忘,虽然很多次尝试都失败了,但我们相信,在代码世界一切皆有可能。
让我们先回顾一下理想中的LINQ查询代码:
再回顾一下错误信息:
还有BlogDbContext的代码:
把这三者联系起来,把问题精简为:如果一个类型(这里是BlogEntry)被注册到DbSet<T>,Enity Framework就会认为这个类型是complex type,在select new时引发异常。而建立一个具有同样属性的类型(前篇随笔中的BlogEntryClone),由于没有被注册到DbSet<T>,则不会引发异常。
本来很单纯的BlogEntry,从DbSet<T>进入Enity Framework,就变成复杂了;而从其他地方进入,依然单纯。很明显,DbSet<T>有问题。
可以猜测DbSet对BlogEntry动了手脚,可能是修改了BlogEntry的类型信息...
所以,要解决的问题变成:DbSet<T>与select new必须要使用不同的类型,而且要共享属性信息。
自然而然,就会想到继承,于是创建了一个继承自BlogEntry的类DerivedBlogEntry试试,代码如下:
然后,将之用于select new,代码如下:
结果出现同样的complex type错误。
稍作分析,就能理解为什么还会出现这个错误。由于DbSet<T>对BlogEntry的类型进行了改变,DerivedBlogEntry继承自BlogEntry,自然也会受到影响。
再把我们要解决的问题进一步提炼:DbSet<T>中的类型(BlogEntry1)与select new中的类型(BlogEntry2)要共享属性(必然要存在关联),而且对BlogEntry1类型的修改不能影响到BlogEntry2。
父类虽然可以与子类共享属性,但对父类类型的修改必然会子类,这就是上面遇到的情况。
似乎我们踏破铁鞋也找不到解决方法...
如果将父类与子类使用的位置交换一下,也就是在DbSet<T>中使用DerivedBlogEntry,在select new中使用BlogEntry...
当这个想法闪现出来的,就预感到这就是解决之道,当时真的很兴奋...迫不及待地用代码进行验证,代码如下:
BlogDbContext的代码:
LINQ查询代码:
当代码测试通过的时候,内心那种美妙的感觉无法用语言去表达...当时很想庆祝一下,于是,把写出这篇博客当作庆祝的方式!
而对于理想中的解决方法,我们依然恋恋不忘,虽然很多次尝试都失败了,但我们相信,在代码世界一切皆有可能。
让我们先回顾一下理想中的LINQ查询代码:
using (BlogDbContext context = new BlogDbContext()) { var result = (from e in context.BlogEntries join t in context.PostTexts on e.ID equals t.ID where e.ID == entryId select new BlogEntry() { Title = e.Title, Body = t.Text }) .FirstOrDefault(); }
再回顾一下错误信息:
System.NotSupportedException : The entity or complex type 'BlogServer.Data.Provider.BlogEntry' cannot be constructed in a LINQ to Entities query. at System.Data.Objects.ELinq.ExpressionConverter.CheckInitializerType(Type type)
还有BlogDbContext的代码:
public class BlogDbContext : DbContext { public DbSet<BlogEntry> BlogEntries { get; set; } }
把这三者联系起来,把问题精简为:如果一个类型(这里是BlogEntry)被注册到DbSet<T>,Enity Framework就会认为这个类型是complex type,在select new时引发异常。而建立一个具有同样属性的类型(前篇随笔中的BlogEntryClone),由于没有被注册到DbSet<T>,则不会引发异常。
本来很单纯的BlogEntry,从DbSet<T>进入Enity Framework,就变成复杂了;而从其他地方进入,依然单纯。很明显,DbSet<T>有问题。
可以猜测DbSet对BlogEntry动了手脚,可能是修改了BlogEntry的类型信息...
所以,要解决的问题变成:DbSet<T>与select new必须要使用不同的类型,而且要共享属性信息。
自然而然,就会想到继承,于是创建了一个继承自BlogEntry的类DerivedBlogEntry试试,代码如下:
public class DerivedBlogEntry : BlogEntry { }
然后,将之用于select new,代码如下:
using (BlogDbContext context = new BlogDbContext()) { var result = (from e in context.BlogEntries join t in context.PostTexts on e.ID equals t.ID where e.ID == entryId select new DerivedBlogEntry() { Title = e.Title, Body = t.Text } ).FirstOrDefault(); }
结果出现同样的complex type错误。
稍作分析,就能理解为什么还会出现这个错误。由于DbSet<T>对BlogEntry的类型进行了改变,DerivedBlogEntry继承自BlogEntry,自然也会受到影响。
再把我们要解决的问题进一步提炼:DbSet<T>中的类型(BlogEntry1)与select new中的类型(BlogEntry2)要共享属性(必然要存在关联),而且对BlogEntry1类型的修改不能影响到BlogEntry2。
父类虽然可以与子类共享属性,但对父类类型的修改必然会子类,这就是上面遇到的情况。
似乎我们踏破铁鞋也找不到解决方法...
如果将父类与子类使用的位置交换一下,也就是在DbSet<T>中使用DerivedBlogEntry,在select new中使用BlogEntry...
当这个想法闪现出来的,就预感到这就是解决之道,当时真的很兴奋...迫不及待地用代码进行验证,代码如下:
BlogDbContext的代码:
public class BlogDbContext : DbContext { public DbSet<DerivedBlogEntry> BlogEntries { get; set; } }
LINQ查询代码:
using (BlogDbContext context = new BlogDbContext()) { var result = (from e in context.BlogEntries join t in context.PostTexts on e.ID equals t.ID where e.ID == entryId select new BlogEntry() { Title = e.Title, Body = t.Text } ).FirstOrDefault(); }
当代码测试通过的时候,内心那种美妙的感觉无法用语言去表达...当时很想庆祝一下,于是,把写出这篇博客当作庆祝的方式!
相关文章推荐
- DB2字符串按照指定符号进行拆分成多个字段的实现方式
- 开发笔记:基于EntityFramework.Extended用EF实现指定字段的更新
- 拼音字母进行SQL查询指定字段的实现
- (转载)VB 查询Oracle中blob类型字段,并且把blob中的图片以流的方式显示在Image上
- OpenJweb平台中自定义组合查询条件窗口的实现方式(经典之作)
- MySQL查询指定字段
- 理想社会的中国实现方式----转载于…
- 六种方式实现hibernate查询,及IDE推荐
- 六种方式实现hibernate查询
- Hibernate高效查询,只查询部分/指定字段
- 浅析SQL查询语句未显式指定排序方式,无法保证同样的查询每次排序结果都一致的原因
- spring-data-mongodb查询结果返回指定字段
- Mybatis多参数查询与列表查询不同方式实现
- ibatis 查询部分字段的List的实现方法
- laravel 查询指定字段的值
- 通过字符字节流的桥梁实现读写字符,指定编码方式案例
- mysql查询字段时实现左右补零
- hibernate六种方式实现的查询
- 实现Google.cn Web方式下查询功能
- [mysql] mysql 在update中实现子查询的方式