• Entity Framework之问题收集


    本节讨论内容主要针对收集了上篇文章大家碰到问题的讨论解决,会持续收集扩充。
    • DbContext加载原值,当前值,数据库值,属性操作,对象复制,对象值复制(VO,DTO->POCO),复杂对象取值
    • DbContext Local Data与AsNoTracking无跟踪查询 如何提高效率
    • DbContext如何关闭延迟加载
    • DbContext如可使用延迟加载
    • DbContext如何控制并发
    • 解除属性映射到数据库中
    • 对象失去或没有被跟踪时处理
    • 多对多关系之扩展字段字段处理
    • 利用模板使模型继承基类
    • 如何对象模型中使用枚举值
    • 创建或使用代理对象
    1. DbContext加载原值,当前值,数据库值,属性操作,对象复制,对象值复制(VO,DTO->POCO),复杂对象取值
    复制代码
    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
    publicpartialclass RoRoWoDBEntities : DbContext
    {
    public RoRoWoDBEntities()
    :
    base("name=RoRoWoDBEntities")
    {
    this.Configuration.LazyLoadingEnabled =false;
    }

    publicoverrideint SaveChanges()
    {
    //在更新前清除掉Local中的数据
    this.Set<BlogArticle>().Local.Clear();
    returnbase.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修饰
    publicpartialclass BlogArticle
    {
    .......................
    publicvirtual BlogCategory BlogCategory { get; set; }
    publicvirtual ICollection<BlogComment> BlogComment { get; set; }
    publicvirtual 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
      publicclass Unicorn
      {
      publicint Id { get; set; }
      [NotMapped]
      publicstring Name { get; set; }

      [Timestamp]
      publicbyte[] Version { get; set; }

      publicint PrincessId { get; set; } // FK for Princess reference
      publicvirtual Princess Princess { get; set; }
      }

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

    View Code
    protectedoverridevoid 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
    {
    publicclass POCOEnity
    {
    //随便扩展操作属性
    //假删除
    publicbool IsFakeDelete { set; get; }
    //脏数据
    publicbool IsDirty { set; get; }
    }
    }
     
    再打DemoDB.tt 模版,找到这段代码(或者直接搜索partial),修改为:
    View Code
    <#=Accessibility.ForType(entity)#><#=code.SpaceAfter(code.AbstractOption(entity))#>partialclass<#=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;

    publicpartialclass BlogArticle:POCOEnity
    {
    public BlogArticle()
    {
    this.BlogComment =new HashSet<BlogComment>();
    this.BlogDigg =new HashSet<BlogDigg>();
    }

    publicint ArticleID { get; set; }
    publicstring Title { get; set; }
    publicstring Content { get; set; }
    publicstring Description { get; set; }
    publicstring ImageUrl { get; set; }
    publicstring Tag { get; set; }
    publicint Hits { get; set; }
    publicbool IsTop { get; set; }
    publicint State { get; set; }
    public Nullable<int> UserID { get; set; }
    publicstring UserName { get; set; }
    publicstring UserIP { get; set; }
    public Nullable<System.DateTime> CreateTime { get; set; }
    public Nullable<System.DateTime> PublishTime { get; set; }
    public Nullable<System.DateTime> UpdateTime { get; set; }
    publicstring Note { get; set; }
    publicint BlogCategory_CateID { get; set; }

    publicvirtual BlogCategory BlogCategory { get; set; }
    publicvirtual ICollection<BlogComment> BlogComment { get; set; }
    publicvirtual ICollection<BlogDigg> BlogDigg { get; set; }
    }
    }
     
    这个时候我们就可以控制DAL传入的泛型模型参数T的域范围了
    publicinterface IRepository<T>where T : POCOEnity, new()
    publicabstractclass RepositoryBase<T>:IRepository<T>where T :POCOEnity,new()
     
    9. 如何对象模型中使用枚举值
    昨晚整了一下,整个VS2010打完补丁后,所有模板都出现2个,程序集还不一致。-_-|||悲剧,看一下微软ADO.NET 团队博客的图
     
    想自己实现的朋友,可以参照ADO.NET TEAM的BLOG。我觉得枚举就是一个转换的过程,不一定非要通过枚举实现,毕竟整个项目用的枚举很多,而这些枚举肯定是存放在一张表里,而不是一个枚举一张表一个对象,若是这样处理起来肯定是给自己增加麻烦。

    10. 创建或使用代理对象
    首先开启代理会影响到实体对象的序列化操作(可能无法序列化),但是禁用代理又无法延迟加载导航数据。延迟加载无非是访问这个属性时会自动加载导航属性数据,如果项目需要对象序列化,我们可以禁用代理并闭延迟加载以提高效率,当需要使用导航属性可以使用DbContext.Entity 加载导航数据。
     
    禁用代理对象this.Configuration.ProxyCreationEnabled = false;
    当New一个模型时 BlogCategory cate = new BlogCategory(); 则是创建实体对象
    而通过DbSet<POCO>.Create() ,则是显示创建代理。
     
    按照MSDN解释,关闭代理就无法跟踪对象的变化了,但实际上我的测试结果是两种情况都可以跟踪到对象的状态变化。不知道MSDN确切指的是什么?
    这一节会持续收集问题更新,大家在使用中碰到的问题,可以拿出来一起讨论讨论。 
     
  • 相关阅读:
    PeaZip 4.7.3 发布,跨平台压缩工具
    Liferea 1.8.10 发布,Linux的RSS阅读
    PyParticles 0.2.1 发布,粒子模拟工具箱
    微软公布 Windows Phone 8 多项新特性
    SecureCRT 7.0.2 发布,支持 Windows 8 系统
    Qore PostgreSQL Module 2.0 发布
    libquickmail 0.1.6 发布,邮件发送包
    Mobile Lua 6.5 发布,MoSync 的 Lua 移植版本
    企业用户缘何抓住 Windows XP 不放
    Knockout.js 2.2 发布,JavaScript UI 库
  • 原文地址:https://www.cnblogs.com/linybo/p/13260435.html
Copyright © 2020-2023  润新知