zoukankan      html  css  js  c++  java
  • mvc EF 执行SQL语句

    一.直接执行SQL语句

    通常来讲 EF 不用写SQL语句的  但是 在有些场合  比如对生成的SQL语句 觉得不满意 要做优化  或者做报表统计时 要写很变态的SQL语句

    再或者 批量操作等   这个时候 使用ORM的弱点就显露了出来 但是 做为优秀的ORM框架  EF 是支持原生态的SQL的   这里面 提供了三种方法

    1. DbSet.SqlQuery   有跟踪状态的查询 

    2. DbDatabase.SqlQuery   没有跟踪状态的查询  

    3. DbDatabase.SqlCommand 直接执行SQL语句 一般用来  批量 增删改

    下面 让我们来看下使用方法

    再上篇的公共资源库里  添加方法

    public virtual IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
            {
                return dbSet.SqlQuery(query, parameters);
            }
    
    
            public virtual IEnumerable<TEntity> GetwhithdbSql(string query, params object[] parameters)
            {
                return context.Database.SqlQuery<TEntity>(query, parameters);
            }
    View Code

    通过对比 来介绍下 第一个和第二个的区别~~

    在课程控制器下 分别执行这两个方法

    var query = "SELECT * FROM Course WHERE CourseID = @p0";
                Course course = unitofwork.CourseRepository.GetWithRawSql(query,id).Single();
                EntityState state = unitofwork.GetState(course);
                string name=course.Department.Name;
    
                Course coursetwo = unitofwork.CourseRepository.GetwhithdbSql(query, id).Single();
                EntityState statetwo = unitofwork.GetState(coursetwo);
                string nametwo = coursetwo.Department.Name;
    View Code

    执行后的结果

    1.先来看第一个  他的状态是unchanged  也就说 他加入了context上下文中  有跟踪状态

    并且下面顺利读取出course.Department.Name   也就是可以读取到导航属性

    2.再看来看第二个 他的状态是detached 并没有加入到 context上下文中  没有有跟踪状态

    所以 我们下面的读取导航属性的内容时  会报错~~

    3.再来说说DbDatabase.SqlCommand  这个比较简单 增删改用这个不错  下面说个批量更新的例子~

    假设有需求 更新所有的课程学分为N时   在这种批量操作时 用ORM 框架 就会不太方便了 这时我们可以用这个直接执行SQL语句

    这个业务属于课程的  所以继承通用资源库类  代码如下

    using System;
    using ContosoUniversity.Models;
    
    namespace ContosoUniversity.DAL
    {
        public class CourseRepository : GenericRepository<Course>
        {
            public CourseRepository(SchoolContext context)
                : base(context)
            {
            }
    
            public int UpdateCourseCredits(int multiplier)
            {
                return context.Database.ExecuteSqlCommand("UPDATE Course SET Credits = Credits * {0}", multiplier);
            }
    
        }
    }
    View Code

    修改上篇的UnitOfWork类

    private CourseRepository courseRepository;
    
    public CourseRepository CourseRepository
    {
        get
        {
    
            if (this.courseRepository == null)
            {
                this.courseRepository = new CourseRepository(context);
            }
            return courseRepository;
        }
    }
    View Code

    控制器如下

    public ActionResult UpdateCourseCredits(int? multiplier)
    {
        if (multiplier != null)
        {
            ViewBag.RowsAffected = unitOfWork.CourseRepository.UpdateCourseCredits(multiplier.Value);
        }
        return View();
    }
    View Code

    最后添加视图~~

    运行结果图

    二.无跟踪查询

    EF再默认的时候给我们开启了跟踪查询  这个跟踪的是什么呢? 先来介绍下这个  看下面的代码

    Department  duplicateDepartment = db.Departments
                        .Where(d => d.InstructorID == department.InstructorID)
                        .FirstOrDefault();
    
                   duplicateDepartment.Name = "wuhawuha";
    
             bool IsUpdate=db.Entry<Department>(duplicateDepartment).Property(p => p.Name).IsModified; //是否修改
             string now= db.Entry<Department>(duplicateDepartment).Property(p => p.Name).CurrentValue; //现在的值
             string before = db.Entry<Department>(duplicateDepartment).Property(p => p.Name).OriginalValue; //以前的值
    View Code

    我们先随便查出一个院系信息 修改他的名字 这是我们会通过上面的信息 得到现在的值  以前的值  这就是跟踪帮我们做的事  下面看下无跟踪的

    实现无跟踪很简单  在查询时 加上asNoTracking() 即可. 加上后我们发现 明明发生变化了 但是IsUpdate依然为false。而且在 获取以前的值时 抛出了异常。

    通常情况 我们并不需要跟踪这些状态 可以在查询时去掉跟踪查询,可以使得性能 得到提升~ 以前一直觉得去掉这个会影响导航属性的使用 但是测试后  并没有影响~

    这个跟踪查询 还会带来另一个问题    看下面的图

    这是在修改前  加了句  先查询这个修改的 再修改 这是就会报错  ObjectStateManager 中已存在具有同一键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象。

    遇到这个错误  在查询时 加上asNoTracking() 即可.  目前在用EF时 个人并不喜欢有跟踪状态~~ 喜欢加上asNoTracking()

    三.使用代理

    了解过ORM框架的  我们知道 导航属性 使用了代理模式 ,这个代理 也是跟导航属性有关的 。 禁用代理 可以提高序列化的速度 .

    关闭代理的代码为在继承 dbcontext 的类里  加上如下代码

     public SchoolContext()
            {
                 this.Configuration.ProxyCreationEnabled = false;
            }

    下面展示下有代理和没有代理的区别  先看有代理时

    我们的Administrator 是导航属性  我们看到 得到的是一串很长的数字和字母的组合  在下面 访问这个Administrator属性时 会通过延迟加载 才得到Administrator属性

    再来看代理关闭时的图

    看我们的导航属性 都为null 不能正确获得导航属性

    四.自动检测功能

    • DbSet.Find
    • DbSet.Local
    • DbSet.Remove
    • DbSet.Add
    • DbSet.Attach
    • DbContext.SaveChanges
    • DbContext.GetValidationErrors
    • DbContext.Entry
    • DbChangeTracker.Entries

    在上面的方法中 会调用自动检测功能。  这个功能默认是开启的  当我们在做批量操作时 可以关闭这个来提高性能 .例如

    using (var context = new UnicornsContext())
    {
        try
        {
            context.Configuration.AutoDetectChangesEnabled = false;
    
            // Make many calls in a loop
            foreach (var unicorn in myUnicorns)
            {
                context.Unicorns.Add(unicorn);
            }
        }
        finally
        {
            context.Configuration.AutoDetectChangesEnabled = true;
        }
    }

     http://my.oschina.net/wzzz/blog/113561

  • 相关阅读:
    递延收益为什么属于负债类科目
    java 环境变量脚本
    dotnet 执行命令常用代码
    centos安装nuget
    centos 安装nodejs redis
    linux git 记住密码
    libgit2-6311e88: cannot open shared object file: No such file or directory
    angular ng build 报错 Cannot read property 'default' of undefined
    java ObjectMapper json 与对象的相互转换
    java 流不能复用 stream has already been operated upon or closed 内存分页
  • 原文地址:https://www.cnblogs.com/love201314/p/4776628.html
Copyright © 2011-2022 走看看