博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Entity Framework之问题收集
阅读量:5093 次
发布时间:2019-06-13

本文共 9561 字,大约阅读时间需要 31 分钟。

From:http://blog.csdn.net/lubingda/article/details/7279678
本节讨论内容主要针对收集了上篇文章大家碰到问题的讨论解决,会持续收集扩充。
  • DbContext加载原值,当前值,数据库值,属性操作,对象复制,对象值复制(VO,DTO->POCO),复杂对象取值
  • DbContext Local Data与AsNoTracking无跟踪查询 如何提高效率
  • DbContext如何关闭延迟加载
  • DbContext如可使用延迟加载
  • DbContext如何控制并发
  • 解除属性映射到数据库中
  • 对象失去或没有被跟踪时处理
  • 多对多关系之扩展字段字段处理
  • 利用模板使模型继承基类
  • 如何对象模型中使用枚举值
  • 创建或使用代理对象
1. DbContext加载原值,当前值,数据库值,属性操作,对象复制,对象值复制(VO,DTO->POCO),复杂对象取值
View Code
RoRoWoDBEntities context 
=
 
new
 RoRoWoDBEntities();
BlogCategory cate 
=
 context.Set
<
BlogCategory
>
().Find(
3
);
BlogArticle arti 
=
 cate.BlogArticle.ToList().Find(o 
=>
 o.ArticleID 
==
 
2
);
BlogCategory cateOther 
=
 context.Set
<
BlogCategory
>
().Find(
4
);
//
取当前值
DbPropertyValues currentValues 
=
 context.Entry
<
BlogCategory
>
(cate).CurrentValues;
//
取原值
DbPropertyValues originalValues 
=
 context.Entry
<
BlogCategory
>
(cate).OriginalValues;
//
取数据库值
DbPropertyValues databaseValues 
=
 context.Entry
<
BlogCategory
>
(cate).GetDatabaseValues();
//
从数据库读值覆盖原值
context.Entry
<
BlogCategory
>
(cate).OriginalValues.SetValues(databaseValues);
//
重新加载对象(并发的时候重新从数据库加载对象,注意给当前值做备份)
context.Entry
<
BlogCategory
>
(cate).Reload();
//
给当前值赋值
context.Entry
<
BlogCategory
>
(cate).CurrentValues.SetValues(currentValues);
//
复制其它对象值(这一条很强大,支持任何类型,比如ViewObject,DTO与POCO可以直接映射传值)
context.Entry
<
BlogCategory
>
(cate).CurrentValues.SetValues(cateOther);
//
单独给每个对象的属性赋值,并且进行标记
context.Entry
<
BlogCategory
>
(cate).Property(o 
=>
 o.ArticleCount).CurrentValue 
=
 
10
;
context.Entry
<
BlogCategory
>
(cate).Property(o 
=>
 o.ArticleCount).IsModified 
=
 
true
;
//
复制对象
BlogCategory newCate 
=
(BlogCategory)context.Entry(cate).GetDatabaseValues().ToObject();
//
复杂对象 给子集合某个属性赋值
context.Entry(arti).ComplexProperty(o 
=>
 o.BlogCategory).Property(c 
=>
 c.CateName).CurrentValue 
=
 
"
test
"
;
//
更改对象状态
context.Entry(arti).State 
=
 EntityState.Modified;
 
2. DbContext Local Data与AsNoTracking无跟踪查询 提高效率
 Local Data是通过Load()方法将数据下载到本地,并与Context保持联系,以提高查询效率或者解决批量操作等问题。
View Code
RoRoWoDBEntities context 
=
 
new
 RoRoWoDBEntities();
DbSet
<
BlogArticle
>
 
set
 
=
 context.Set
<
BlogArticle
>
();
//
数据加载到本地(你也可以单独下载某一条数据到本地,Local是下载数据的容器)
set
.Load();
//
从本地查找指定数据
BlogArticle arti 
=
 
set
.Local.ToList().Find(o 
=>
 o.ArticleID 
==
 
7
);
//
由于Local Data与context保持联系,所以本地数据的增删改查一样会生效 
//
我们可通过重写SaveChanges避免这种问题发生,让Local Data成为一个查询集合
//
而所有修改均不更新到数据库存中
set
.Remove(arti);
context.SaveChanges();
 
重写DbContext SaveChanges方法阻止Local Data数据更新
View Code
public
 
partial
 
class
 RoRoWoDBEntities : DbContext
{
public
 RoRoWoDBEntities()
base
(
"
name=RoRoWoDBEntities
"
)
{
this
.Configuration.LazyLoadingEnabled 
=
 
false
;
}
public
 
override
 
int
 SaveChanges()
{
//
在更新前清除掉Local中的数据
this
.Set
<
BlogArticle
>
().Local.Clear();
return
 
base
.SaveChanges();
.......................
}
 
AsNoTracking无跟踪查询
View Code
//
实现无跟踪查询提高效率
DbQuery query 
=
 
set
.AsNoTracking();
var result 
=
 
set
.Where(o 
=>
 o.ArticleID 
==
 
7
).AsNoTracking().ToList();
 
批量操作关闭自动检测提高效率
View Code
RoRoWoDBEntities context 
=
 
new
 RoRoWoDBEntities();
DbSet
<
BlogArticle
>
 
set
 
=
 context.Set
<
BlogArticle
>
();
List
<
BlogArticle
>
 list 
=
 
new
 List
<
BlogArticle
>
();
list.Add(
new
 BlogArticle
{
BlogCategory_CateID 
=
 
3
,
Content 
=
 
"
小朋友
"
,
Title 
=
 
"
测试001
"
});
list.Add(
new
 BlogArticle
{
BlogCategory_CateID 
=
 
3
,
Content 
=
 
"
大朋友
"
,
Title 
=
 
"
测试002
"
});
try
{
//
批量操作前 关闭自动检测变化功能
context.Configuration.AutoDetectChangesEnabled 
=
 
false
;
//
批量操作
foreach
 (var blog 
in
 list)
{
set
.Add(blog);
}
}
finally
{
//
开启自动检测变化
context.Configuration.AutoDetectChangesEnabled 
=
 
true
;
context.SaveChanges();
}
 
3. DbContext如何关闭开启延迟加载
关闭方式
View Code
//
修改DbContext 配置属性 或者直接设置EDM文件,亦或乾修改tt模板
context.Configuration.LazyLoadingEnabled 
=
 
false
;
//
修改POCO 去掉导航属性的virtual修饰 
public
 
partial
 
class
 BlogArticle
{
....................... 
public
 
virtual
 BlogCategory BlogCategory { 
get
set
; }
public
 
virtual
 ICollection
<
BlogComment
>
 BlogComment { 
get
set
; }
public
 
virtual
 ICollection
<
BlogDigg
>
 BlogDigg { 
get
set
; }
}
 
加载方式
View Code
RoRoWoDBEntities context 
=
 
new
 RoRoWoDBEntities();
context.Configuration.LazyLoadingEnabled 
=
 
false
;
BlogArticle arti
=
context.Set
<
BlogArticle
>
().Find(
7
);
//
include方式
BlogCategory cate 
=
 context.Set
<
BlogCategory
>
().Include(o 
=>
 o.BlogArticle).ToList().Find(c 
=>
 c.CateID 
==
3
);
//
显示加载
context.Entry(cate).Collection(o 
=>
 o.BlogArticle).Load();
context.Entry(arti).Reference(o 
=>
 o.BlogCategory).Load();
 
4. DbContext如何控制并发
乐观并发的控制
View Code
bool
 saveFailed;
do
{
saveFailed 
=
 
false
;
var blog 
=
 context.Set
<
BlogArticle
>
().Find(
7
);
blog.Title 
=
 
"
并发修改名称
"
;
try
{
context.SaveChanges();
}
catch
 (DbUpdateConcurrencyException ex)
{
saveFailed 
=
 
true
;
//
1重新加载已变化的数据
//
ex.Entries.Single().Reload();
//
2如果页面会刷新 做好当前值备份
var entry 
=
 ex.Entries.Single();
DbPropertyValues values 
=
 entry.CurrentValues;
entry.Reload();
entry.CurrentValues.SetValues(values);
//
3或者给原值重设数据库值
//
var entry = ex.Entries.Single();
//
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
while
 (saveFailed);
即使使用Timestamp字段,我们一样也是通过乐观并发控制,然后较验timestamp字段是否一致,或者通过存储过程实现这一过程。
 
5. 解除属性映射到数据库中
EF 提供了一系列属性用于描述模型
  • KeyAttribute
  • StringLengthAttribute
  • MaxLengthAttribute
  • ConcurrencyCheckAttribute
  • RequiredAttribute
  • TimestampAttribute   
  • ComplexTypeAttribute
  • ColumnAttribute
  • TableAttribute
  • InversePropertyAttribute
  • ForeignKeyAttribute
  • DatabaseGeneratedAttribute
  • NotMappedAttribute 
    可以通过NotMappedAttribute标记模型某个属性可以使该属性不必映射到数据库。
    View Code
    public
     
    class
     Unicorn
    {
    public
     
    int
     Id { 
    get
    set
    ; }
    [NotMapped]
    public
     
    string
     Name { 
    get
    set
    ; }
    [Timestamp]
    public
     
    byte
    [] Version { 
    get
    set
    ; }
    public
     
    int
     PrincessId { 
    get
    set
    ; } 
    //
     FK for Princess reference
    public
     
    virtual
     Princess Princess { 
    get
    set
    ; }
    }

另外我们还可以通过代码的方式,在DbContext的OnModelCreating方法重载中实现,不知道用Code First的人为什么喜欢这种方式处理。DbContext默认是把EDMX文件当成EntityConfiguration加载进来的,和手动处理这些映射关系是一样的。但是手动处理大量的映射关系既不直观,也不容易维护,而EDMX这个模型视图浏览与维护都较之方便啊。

View Code
protected
 
override
 
void
 OnModelCreating(DbModelBuilder modelBuilder)
{
//
不映射到数据库中
modelBuilder.Entity
<
BlogArticle
>
().Ignore(p 
=>
 p.Title); 
}
 EDMX怎么解除映射还没有找到如何实现,删除表映射EDM验证无法通过,而对象属性又没提供NotMapped 选择。
我觉得如果一个属性不需要映射到数据库时,那这个POCO对象设计肯定是不合理的,可能他是一个合理VO对象,或者是数据库查询视图对象。
6. 对象失去或没有被跟踪时处理
context.Set<BlogArticle>().Attach()  加回上下文中,继续跟踪
7. 多对多关系之扩展字段字段处理
EF 处理多对多关系,是通过中间表主外键关联加载对应的主体表对象,这个中间表是个不存在的业务对象。而中间表如果除了主外键之外还有扩展的字段,就会导致中间表变成一个具体存在的业务对象,让它成为对应主体表的关联导航属性。因此这种中间表设计是不合理,但EF可以应对这种情况。看下图
 
 
 
Student<-Score(多对多中间表)->Subject  三个对象 由于Score不是一个合理的中间表,导致EF将其映射为一个具体的实体对象成为Student与Subject的导航属性。
而User<-UserProperty(多对多中间表)->Property  UserProperty仅是数据关系的体现,并不是一个具体的实体对象,User与Property是直接导航。
 
8. 利用模板使模型继承基类
由于我们的纯POCO模型没有基类限制领域,所以在我们的泛型传递POCO的对象给DAL时,就无法限制这个T 是POCO对象呢,还是其它的对象。因此为了将POCO对象与其它业务对象区分开来,可以通过使POCO对象继承一个基类来解决这个问题,当然肯定是模板解决这个问题了。
我们先立一个POCOEntity基类
View Code
using
 System;
using
 System.Collections.Generic;
using
 System.Linq;
using
 System.Text;
namespace
 EF.Model
{
public
 
class
 POCOEnity
{
//
随便扩展操作属性
//
假删除
public
 
bool
 IsFakeDelete { 
set
get
; }
//
脏数据
public
 
bool
 IsDirty { 
set
get
; } 
}
}
 
再打DemoDB.tt 模版,找到这段代码(或者直接搜索partial),修改为:
View Code
<
#
=
Accessibility.ForType(entity)#
>
 
<
#
=
code.SpaceAfter(code.AbstractOption(entity))#
>
partial
 
class
<
#
=
code.Escape(entity)#
><
#
=
code.StringBefore(
"
 : 
"
, code.Escape(entity.BaseType))#
>
:POCOEnity
即在模版生成类结构代码追加其继承POCOEnity基类,修改完成后,点击保存,所有模型类会重新生成,我们看一下生成后的代码
View Code
//
------------------------------------------------------------------------------
//
 <auto-generated>
//
 此代码是根据模板生成的。
//
//
 手动更改此文件可能会导致应用程序中发生异常行为。
//
 如果重新生成代码,则将覆盖对此文件的手动更改。
//
 </auto-generated>
//
------------------------------------------------------------------------------
namespace
 EF.Model
{
using
 System;
using
 System.Collections.Generic;
public
 
partial
 
class
 BlogArticle:POCOEnity
{
public
 BlogArticle()
{
this
.BlogComment 
=
 
new
 HashSet
<
BlogComment
>
();
this
.BlogDigg 
=
 
new
 HashSet
<
BlogDigg
>
();
}
public
 
int
 ArticleID { 
get
set
; }
public
 
string
 Title { 
get
set
; }
public
 
string
 Content { 
get
set
; }
public
 
string
 Description { 
get
set
; }
public
 
string
 ImageUrl { 
get
set
; }
public
 
string
 Tag { 
get
set
; }
public
 
int
 Hits { 
get
set
; }
public
 
bool
 IsTop { 
get
set
; }
public
 
int
 State { 
get
set
; }
public
 Nullable
<
int
>
 UserID { 
get
set
; }
public
 
string
 UserName { 
get
set
; }
public
 
string
 UserIP { 
get
set
; }
public
 Nullable
<
System.DateTime
>
 CreateTime { 
get
set
; }
public
 Nullable
<
System.DateTime
>
 PublishTime { 
get
set
; }
public
 Nullable
<
System.DateTime
>
 UpdateTime { 
get
set
; }
public
 
string
 Note { 
get
set
; }
public
 
int
 BlogCategory_CateID { 
get
set
; }
public
 
virtual
 BlogCategory BlogCategory { 
get
set
; }
public
 
virtual
 ICollection
<
BlogComment
>
 BlogComment { 
get
set
; }
public
 
virtual
 ICollection
<
BlogDigg
>
 BlogDigg { 
get
set
; }
}
}
 
这个时候我们就可以控制DAL传入的泛型模型参数T的域范围了
public
 
interface
 IRepository
<
T
>
 
where
 T : POCOEnity, 
new
()
public
 
abstract
 
class
 RepositoryBase
<
T
>
:IRepository
<
T
>
 
where
 T :POCOEnity,
new
()
 
9. 如何对象模型中使用枚举值
插件: 
昨晚整了一下,整个VS2010打完补丁后,所有模板都出现2个,程序集还不一致。-_-|||悲剧,看一下微软ADO.NET 团队博客的图
想自己实现的朋友,可以参照 。我觉得枚举就是一个转换的过程,不一定非要通过枚举实现,毕竟整个项目用的枚举很多,而这些枚举肯定是存放在一张表里,而不是一个枚举一张表一个对象,若是这样处理起来肯定是给自己增加麻烦。
10. 创建或使用代理对象
首先开启代理会影响到实体对象的序列化操作(可能无法序列化),但是禁用代理又无法延迟加载导航数据。延迟加载无非是访问这个属性时会自动加载导航属性数据,如果项目需要对象序列化,我们可以禁用代理并闭延迟加载以提高效率,当需要使用导航属性可以使用DbContext.Entity 加载导航数据。
 
禁用代理对象 this.Configuration.ProxyCreationEnabled = false;
当New一个模型时 BlogCategory cate = new BlogCategory(); 则是创建实体对象
而通过DbSet<POCO>.Create() ,则是显示创建代理。
 
按照MSDN解释,关闭代理就无法跟踪对象的变化了,但实际上我的测试结果是两种情况都可以跟踪到对象的状态变化。不知道MSDN确切指的是什么?
这一节会持续收集问题更新,大家在使用中碰到的问题,可以拿出来一起讨论讨论。 

转载于:https://www.cnblogs.com/norsd/archive/2013/02/05/6359440.html

你可能感兴趣的文章
Unity3D研究院之打开Activity与调用JAVA代码传递参数(十八)【转】
查看>>
python asyncio 异步实现mongodb数据转xls文件
查看>>
TestNG入门
查看>>
【ul开发攻略】HTML5/CSS3菜单代码 阴影+发光+圆角
查看>>
IOS-图片操作集合
查看>>
IO—》Properties类&序列化流与反序列化流
查看>>
测试计划
查看>>
Mysql与Oracle 的对比
查看>>
jquery实现限制textarea输入字数
查看>>
Codeforces 719B Anatoly and Cockroaches
查看>>
jenkins常用插件汇总
查看>>
c# 泛型+反射
查看>>
第九章 前后查找
查看>>
Python学习资料
查看>>
jQuery 自定义函数
查看>>
jquery datagrid 后台获取datatable处理成正确的json字符串
查看>>
ActiveMQ与spring整合
查看>>
web服务器
查看>>
第一阶段冲刺06
查看>>
EOS生产区块:解析插件producer_plugin
查看>>