Angular $injector:unpr Unknown provider 问题原因汇总_大V的博客-CSDN博客

mikel阅读(602)

来源: Angular $injector:unpr Unknown provider 问题原因汇总_大V的博客-CSDN博客

Unknown provider: tProvider <- t 对于很多Angular 1.x 的使用者来说,是非常头疼的一件事情,因为使用者根本不知道原因在哪里。

本文总结了迄今为止所有导致这一个问题的可能原因,希望大家遇到此问题时,不要慌!跟着下面内容一步步排查:

问题描述:

Unknown provider 一般是由于无法解析注入的一个依赖所导致的。原因一般有如下这几种可能:

1. 依赖没有定义

  1. angular.module(‘myApp’, [])
  2. .controller(‘MyController’, [‘myService’, function (myService) {
  3. // Do something with myService
  4. }]);

上述代码,由于之前没有定义myService这个依赖,会报错 [$injector:unpr] Unknown provider。如果发现有没定义的依赖,首先要提前定义好这个依赖。

  1. angular.module(‘myApp’, [])
  2. .service(‘myService’, function () { /* … */ })
  3. .controller(‘MyController’, [‘myService’, function (myService) {
  4. // Do something with myService
  5. }]);

2. 重新使用了angular.module

  1. angular.module(‘myModule’, [])
  2. .service(‘myCoolService’, function () { /* … */ });
  3. angular.module(‘myModule’, [])
  4. // myModule has already been created! This is not what you want!
  5. .directive(‘myDirective’, [‘myCoolService’, function (myCoolService) {
  6. // This directive definition throws unknown provider, because myCoolService
  7. // has been destroyed.
  8. }]);

上述代码中,重复使用了angular.module(‘myModule’, []),会让angular重新定义myModule这个模块,导致报错。angular.module(‘myModule’, []) 这个语法一般是再次定义module,为了避免这个问题,在定义module之后直接使用angular.module(‘myModule’)或者使用一个变量代替之即可。

  1. angular.module(‘myModule’, []);
  2. .service(‘myCoolService’, function () { /* … */ });
  3. angular.module(‘myModule’)
  4. .directive(‘myDirective’, [‘myCoolService’, function (myCoolService) {
  5. // This directive definition does not throw unknown provider.
  6. }]);
  7. 或者
  8. var app = angular.module(‘myModule’, []);
  9. app.service(‘myCoolService’, function () { /* … */ });
  10. app.directive(‘myDirective’, [‘myCoolService’, function (myCoolService) {
  11. // This directive definition does not throw unknown provider.
  12. }]);

3. 把一个controller当依赖注入到另外一个controller中

  1. angular.module(‘myModule’, [])
  2. .controller(‘MyFirstController’, function() { /* … */ })
  3. .controller(‘MySecondController’, [‘MyFirstController’, function(MyFirstController) {
  4. // This controller throws an unknown provider error because
  5. // MyFirstController cannot be injected.
  6. }]);

其实如果要实例化controller,可以使用$controller服务(后续博客会更新)

4. 把$scope注入到不是controller或者directive的组件中

  1. angular.module(‘myModule’, [])
  2. .service(‘MyController’, [‘$scope’, function($scope) {
  3. // This controller throws an unknown provider error because
  4. // a scope object cannot be injected into a service.
  5. }]);

发生这个情况,也很好排查,只要牢记,只有controller和directive才能注入$scope作为依赖。

5. 使用angular压缩版导致报错

可以使用ngStrictDi

好了,希望本文能帮助到大家,遇此问题千万别慌了神!

MongoDB had an unspecified uncaught exception. 报错_异度社区

mikel阅读(579)

来源: MongoDB had an unspecified uncaught exception. 报错_异度社区

MongoDB had an unspecified uncaught exception. 报错

MongoDB had an unspecified uncaught exception.
This can be caused by MongoDB being unable to write to a local database.
Check that you have permissions to write to .meteor/local. MongoDB does
not support filesystems like NFS that do not allow file locking.

在启动前type:

export LC_ALL="en_US.UTF-8"

meteor报错之:MongoDB had an unspecified uncaught exception._weixin_30407613的博客-CSDN博客

mikel阅读(554)

来源: meteor报错之:MongoDB had an unspecified uncaught exception._weixin_30407613的博客-CSDN博客

今天测试的时候meteor报了个错

如下:

  1. MongoDB had an unspecified uncaught exception.
  2. This can be caused by MongoDB being unable to write to a local database.
  3. Check that you have permissions to write to .meteor/local. MongoDB does
  4. not support filesystems like NFS that do not allow file locking.

纠结了好久终于知道答案了,请确保你的硬盘有大于50G的预留空间,然后不要在内存小的磁盘分区里测试,要在内存充足的磁盘测试,(如果你报了这个错误,尝试换个磁盘去测试)

转载于:https://www.cnblogs.com/millent/p/5371322.html

Angular 源码解析系列 - 掘金

mikel阅读(1268)

来源: Angular 源码解析系列 – 掘金

加载应用依赖模块以及内置的ng模块等,就像之前说的类似这样:['ng', [$provide, function($provide){...}], 'xx']
执行每个模块的_runBlocks,可以理解injector创建完后模块的初始化(通过myModule.run(...)注册的)

  function loadModules(modulesToLoad){
    var runBlocks = [], moduleFn;

    // 循环加载每个module,
    // 1. 注册每个模块上挂载的service(也就是_invokeQueue)
    // 2. 执行每个模块的自身的回调(也就是_configBlocks)
    // 3. 通过递归搜集所有(依赖)模块的_runBlocks,并返回
    forEach(modulesToLoad, function(module) {

      // 判断模块是否已经加载过
      if (loadedModules.get(module)) return;

      // 设置模块已经加载过
      loadedModules.put(module, true);

      function runInvokeQueue(queue) {
        var i, ii;
        for(i = 0, ii = queue.length; i < ii; i++) {

          var invokeArgs = queue[i],
              provider = providerInjector.get(invokeArgs[0]);

          // 通过providerInjector获取指定服务(类),传递参数并执行指定方法
          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
        }
      }

      // 模块可以是以下三种情况:
      // 1. 字符串表示模块名(注册过的模块),如:'ng'模块
      // 2. 普通函数(也可以是隐式声明依赖的函数),如:function($provider) {...}
      // 3. 数组(即声明依赖的函数)如:[$provide, function($provide){...}
      try {
        if (isString(module)) {
          // 获取通过模块名获取模块对象
          moduleFn = angularModule(module);
          // 通过递归加载所有依赖模块,并且获取所有依赖模块(包括自身)的_runBlocks
          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
          // 遍历_invokeQueue数组依次执行$provider服务的指定方法(如:factory,value等)
          runInvokeQueue(moduleFn._invokeQueue);
          // 遍历_configBlocks数组依次执行$injector服务的invoke方法(即依赖注入并执行回调)
          runInvokeQueue(moduleFn._configBlocks);

        // 如果module是函数或者数组(可认为是匿名模块),那么依赖注入后直接执行
        // 并将返回值保存到runBlocks(可能是函数,又将继续执行)
        } else if (isFunction(module)) {
            runBlocks.push(providerInjector.invoke(module));
        } else if (isArray(module)) {
            runBlocks.push(providerInjector.invoke(module));
        } else {
          assertArgFn(module, 'module');
        }
      } catch (e) {
        if (isArray(module)) {
          module = module[module.length - 1];
        }
        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
          e = e.message + '\n' + e.stack;
        }
        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
                  module, e.stack || e.message || e);
      }
    });
    return runBlocks;
  }
复制代码

到这里在注册ng模块时的回调,在runInvokeQueue(moduleFn._configBlocks);已经执行过了,也就意味着许许多多的内置模块已经存入providerCache中了,所以在后面的依赖注入中我们可以随意调用。

最后

如果不对的地方,欢迎留言指正,新浪微博 – Lovesueee

作者:小鱼二
链接:https://juejin.im/post/6844903440217276423
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Angular系列学习笔记(一)—— 聊聊angular的模块化 - 简书

mikel阅读(780)

来源: Angular系列学习笔记(一)—— 聊聊angular的模块化 – 简书

前言

近来换工作了,由于团队技术需要,不得不从vue.js迁移到angular.js,不过这也是一个学习angular的机会,顺便也将这两个框架做一些对比,为了方便其他小伙伴,将学习过程中的内容记录下来。由开始的不太习惯到现在也能够习惯angular的写法,着实在思维上有很大的改变,所以这个系列的文章会记录一下自己的学习过程,本文会由浅及深一步步去解读vue.js和angular.js的区别。由于时间关系,这篇文章断断续续耗时几周,不求什么,只希望在这个过程中将angular相关的一些特性去梳理一下,由于是系列开篇,文章并没有深入探讨源码和原理,后续会写更多个人的思考和探索。

vuejs vs angular之初相见

对于vue.js我们都需要通过创建一个Vue实例指定作用范围,对于angular只需要ng-app指令就可以。

vue.js的姿势:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
<div id="app">
    {{ message }}
    <button v-on:click="changeView">changeView</button>
</div>
<script src="//cdn.bootcss.com/vue/2.1.10/vue.js"></script>
<script type="text/javascript">
    var app = new Vue({
        el: '#app',
        data: {
            message: 'Hello world!'
        },
        methods: {
            changeView: function(){
                this.message = 'hello vuejs!';
                // 或 app.$data.message = 'hello vuejs!'
            }
        }
    })
</script>
</body>
</html>

angular的姿势:

<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8">
</head>
<body>
    <div ng-controller="helloCtrl">
        {{message}}
        <button ng-click="changeView()">changeView</button>
    </div>
    <script src="//cdn.bootcss.com/angular.js/1.6.0/angular.js"></script>
    <script type="application/javascript">
        var app = angular.module('app', []);
        app.controller('helloCtrl', function ($scope) {
            $scope.message = 'hello world';
            $scope.changeView = function () {
                $scope.message = 'hello angular.js!';
            }
        })
    </script>
</body>
</html>

这个例子虽然再简单不过,对于项目开发没有半点用处,但是对于理解vue.js和angular.js的区别有一定的借鉴意义,vue.js中总体的思路是实例化Vue对象,我们通过el属性指定控制的dom范围,data是数据模型,可以通过实例化后的对象app.$data访问数据,在方法中或者生命周期钩子如mounted中可以使用this获取实例上下文,vuejs中没有控制器的概念,属于典型的MVVM架构。然而我们这篇文章不是讲vue.js,因为如果你使用vue.js,那么直接看文档很好理解,无需我多言,这里我们想重点说说angular。

经常听人说vue.js语法优雅,我想应该是由于vue.js结构更加清晰吧,数据模型和操作分得更清晰,就从命名出发更简单吧,$scope是什么鬼。好吧,查了一下:scope(范围),我们姑且先从字面含义这么理解吧,但是从这例子中发现$scope多次使用,另外不能被改名,不然不能用,我们姑且认为是个神奇的玩意。angular中ng-click的指令和vue.js无大的区别,这里不多言,这里重点想谈谈模块化和angular的依赖注入。

vuejs vs angular之数据模型共享

在vue.js中是通过组件树实现模块化的,而angular通过作用域$scope实现模块化。对于vue.js中模块化的最佳实践是:

** .vue单文件组件 + 模块化标准(common.js或者es6 module) + webpack(或Browserify)。**

而其中核心部分就是组件,用官方的话说就是,组件(Component)是 Vue.js 最强大的功能之一,组件可以扩展 HTML 元素,封装可重用的代码,在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能,在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展。

如何构建一个组件,组件之前数据如何共享,这是开发中必须要学习的内容。vue.js组件实例的作用域是孤立的,这意味着不能并且不应该在子组件的模板内直接引用父组件的数据,可以使用 props 把数据传给子组件。另外vue.js中提倡单向数据流:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。当子组件需要更新父组件的状态时,我们可以通过事件触发。下面是一个利用props传递数据和使用事件触发父组件状态的典型例子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <div id="app">
        {{ message }}
        <button v-on:click="parentAtion">parentAtion</button>
        <child v-bind:param="message"  v-on:childfn="fromChild"></child>
    </div>
    
    <script type="text/x-template" id="child-tpl">
        <div>
            <span>{{param}}</span>
            <button v-on:click="childAtion">childAtion</button>
        </div>
    </script>
    
    <script src="//cdn.bootcss.com/vue/2.1.10/vue.js"></script>
    <script type="text/javascript">
        Vue.component('child', {
            template: '#child-tpl',
            props: ['param'],
            methods: {
                childAtion: function(){
                    // 触发事件
                    this.$emit('childfn', 'child component');
                }
            }
        });
        
        var app = new Vue({
            el: '#app',
            data: {
                message: 'Hello vuejs!'
            },
            methods: {
                parentAtion: function(){
                    this.message = 'parent component';
                },
                fromChild: function(msg){
                    this.message = msg;
                }
            }
        })
    </script>
</body>
</html>

每个 Vue 实例都实现了事件接口(Events interface),即:

  • 使用 $on(eventName) 监听事件
  • 使用 $emit(eventName) 触发事件

Vue的事件系统分离自浏览器的EventTarget API。尽管它们的运行类似,但是$on和 $emit 不是addEventListener和 dispatchEvent的别名。

父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。对于非父子组件通信的通信,在简单的场景下,使用一个空的 Vue 实例作为中央事件总线:

var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
  // ...
})

在更多复杂的情况下,你应该考虑使用专门的状态管理

对于angular 而言,ng-controller指令指定了作用范围,通过$scope对象控制作用域,$scope层层嵌套形成父子关系或者兄弟并列关系,而父子作用域是通过原型继承实现,子作用域可以访问父作用域的数据模型,反过来不行,同理如果父级作用域中的状态改变会影响子作用域,反过来子作用域中的状态改变不会影响父级作用域。

<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8">
</head>
<body>
    <div ng-controller="parentCtrl">
        {{message}}
        <button ng-click="parentAtion()">parentAtion</button>
        <div ng-controller="childCtrl">
            {{message}}
            <button ng-click="childAtion()">childAtion</button>
        </div>
    </div>
    <script src="js/angular.min.js"></script>
    <script type="application/javascript">
        var app = angular.module('app', []);
        app.controller('parentCtrl', function ($scope) {
            $scope.message = 'hello angular';
            $scope.parentAtion = function () {
                $scope.message = 'parent scope';
            }
        })
        
        app.controller('childCtrl', function ($scope) {
            $scope.childAtion = function () {
                $scope.message = 'child scope';
            }
        })
    </script>
</body>
</html>

由于原型继承的关系,修改父级对象中的message会同时修改子对象中的值,但反之则不行。如果我们想在子级控制器中改变父对象中的值,则需要通过引用进行共享。

<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8">
</head>
<body>
    <div ng-controller="parentCtrl">
        {{dataModal.message}}
        <button ng-click="parentAtion()">parentAtion</button>
        <div ng-controller="childCtrl">
            {{dataModal.message}}
            <button ng-click="childAtion()">childAtion</button>
        </div>
    </div>
    <script src="js/angular.min.js"></script>
    <script type="application/javascript">
        var app = angular.module('app', []);
        app.controller('parentCtrl', function ($scope) {
            $scope.dataModal = {
                message: 'hello angular'
            }
            $scope.parentAtion = function () {
                $scope.dataModal.message = 'parent scope';
            }
        })
        
        app.controller('childCtrl', function ($scope) {
            $scope.childAtion = function () {
                $scope.dataModal.message = 'child scope';
            }
        })
    </script>
</body>
</html>

问题的本质在于字符串、数字和布尔型变量是值复制,数组、对象和函数则是引用复制,通过引用共享可以在子级作用域内部改变它会影响父级作用域的数据模型状态。

另外我们通过chrome的一个插件AngularJS Batarang可以很清晰的看出$scope对象的继承关系,会发现每个$scope对象都会有个$id,以及对于作用域$scope下的数据模型,如下:

AngularJS Batarang插件效果

需要说明的是ng-app对应的作用域是全局的,我们一般使用$rootScope对象表示,对应的$id等于1。

除了通过引用复制实现数据共享,我们也可以使用类似vue.js中的事件机制。

angular事件广播:

  • $broadcast:只能向child controller传递event与data
  • $emit:只能向parent controller传递event与data

$scope.$broadcast(name, data);
$scope.$emit(name, data);

angular事件监听:

  • $on:接收event与data

$scope.$on(eventName, function(event, data){
  // 这里写监听逻辑...
})

在$on的方法中的event事件参数,其对象的属性和方法如下:

事件属性 含义
event.targetScope 发出或者传播原始事件的作用域
event.currentScope 目前正在处理的事件的作用域
event.name 事件名称
event.stopPropagation() 一个防止事件进一步传播(冒泡/捕获)的函数(这只适用于使用$emit发出的事件)
event.preventDefault() 这个方法实际上不会做什么事,但是会设置defaultPrevented为true。直到事件监听器的实现者采取行动之前它才会检查defaultPrevented的值。
event.defaultPrevented 如果调用了preventDefault则为true

上述的例子可以改写为:

var app = angular.module('app', []);
app.controller('parentCtrl', function ($scope) {
    $scope.message = 'hello angular';
    $scope.parentAtion = function () {
        $scope.message = 'parent scope';
    }
    
    // 监听事件
    $scope.$on('sendMsg', function(event, data){    
        $scope.message = data;
    })
})

app.controller('childCtrl', function ($scope) {
    $scope.childAtion = function () {
        // 触发事件
        $scope.$emit('sendMsg', 'child scope');
    }
})

angular 依赖注入

前面我们对比了angular和vue对于数据模型共享的处理方式,发现两个框架在整体上大致相同,在细微上有所区别。对于一个大型模块化工程而言,除了需要处理数据模型数据流的问题,插件和依赖的处理同样也是实现模块化必不可少的一部分。我们如何将我们的应用分成一个个小模块,互不干扰高度自治,但是却又能够相互依赖。在vue中我们是通过构建工具例如webpack+模块化标准来实现,在angular中是通过依赖注入(Dependency Injection)实现的。

这里我们先来看一下知乎上的一个问题:AngularJS中的依赖注入实际应用场景?依赖注入是一个设计模式,遵循依赖倒转原则(Dependence Inversion Priciple, DIP)。关于依赖注入的实现原理后面再做深入探讨,我们先可以认为angular提供了一套机制可以很好的解决模块之间相互依赖的问题。

angular是用$provider对象来实现自动依赖注入机制,注入机制通过调用一个provide的$get()方法,把得到的对象作为参数进行相关调用。例如:

<div ng-controller="serveCtrl">
    {{message}}
</div>

<script type="application/javascript">
    var app = angular.module('app', []);    
    //  使用provider方法创建服务
    app.provider('dataServe',{  
        $get: function() {  
            return {  
                message:"dataServe Provide"  
            };  
        }  
    }); 
    
    app.controller('serveCtrl', function ($scope, dataServe) {
        // 调用服务
        $scope.message = dataServe.message;
    })
</script>

这里我们演示了angular中依赖注入的一个简单例子,在serveCtrl控制器中我们通过将dataServe作为函数参数名写入,就可以访问dataServe服务的内容,从而实现模块解耦。

上面是一种实现方法,但不是最佳实践,我们一步步说明其他写法。

首先我们需要知道在angular中声明和引用一个模块的方法:
声明模块的写法如下:

angular.module(name, [requires], [configFn]);

name:字符串类型,代表模块的名称;
requires:字符串的数组,代表该模块依赖的其他模块列表,如果不依赖其他模块,用空数组即可;
configFn:用来对该模块进行一些配置。

函数返回模块的引用

使用模块的方法:

angular.module(name)

上面创建服务,我们可以通过$provide服务实现:

angular.module('app', [], function($provide){
    $provide.provider('dataServe', function(){
        this.$get = function(){
            return {
                message:"dataServe Provide"  
            }
        }
    })
}).controller('serveCtrl', function ($scope, dataServe) {
    // 调用服务
    $scope.message = dataServe.message;
})var app = angular.module('app', []);
app.config(function($provide){
    $provide.provider('dataServe', function(){
        this.$get = function(){
            return {
                message:"dataServe Provide"  
            }
        }
    })
})
app.controller('serveCtrl', function ($scope, dataServe) {
    $scope.message = dataServe.message;
})

创建服务的几种常用方法

上述我们使用provider和$provide.provider两种不同的写法,但是都是需要写$get函数,为了简化创建服务的过程,angualr提供了其他的五种方法:

constant:定义常量,定义的值不能被改变,可以被注入到任何地方,但是不能被装饰器(decorator)装饰;

<div ng-controller="serveCtrl">
    {{message}}
</div>

<script type="application/javascript">
    var app = angular.module('app', []);
    app.config(function($provide) {
        $provide.constant('constantServe', 'constant serve');
    })
    app.controller('serveCtrl', function($scope, constantServe) {
        $scope.message = constantServe;
    }) 
</script>

语法糖:

app.constant('constantServe', 'constant serve');

value:可以是string,number甚至function,和constant的不同之处在于,它可以被修改,不能被注入到config中,可以被decorator装饰

<div ng-controller="serveCtrl">
    <button ng-click="showTips()">show tips</button>
</div>

<script type="application/javascript">
    var app = angular.module('app', []);    
    app.value('valueServe', function(){
        alert("哈哈");
    })
    app.controller('serveCtrl', function ($scope, valueServe) {
        $scope.showTips = valueServe;
    })
</script>

factory:factory为自定义工厂,是注册服务的最常规方式,它可返回任何对象,包括基本的数据类型。

<div ng-controller="serveCtrl">
    {{message}}
</div>

<script type="application/javascript">
    var app = angular.module('app', []);
    app.config(function($provide){
        $provide.factory('factoryServe', function(){
            return {
                message: 'factory serve'
            }
        })
    })
    app.controller('serveCtrl', function ($scope, factoryServe) {
        $scope.message = factoryServe.message
    })
</script>

语法糖:

app.factory('factoryServe', function(){
    return {
        message: 'factory serve'
    }
})

factory就是通过provider第二个参数为对象的方法实现,factory底层通过调用$provide.provider(name, {$get: $getFn}),而$getFn就是自定义factory的参数,即factory所传的方法需返回一个对象,这个对象会绑定到provider的$get属性上。

service:当使用service创建服务的时候,相当于使用new关键词进行了实例化。因此,你只需要在this上添加属性和方法,然后,服务就会自动的返回this。当把这个服务注入控制器的时候,控制器就可以访问在那个对象上的属性了。

<div ng-controller="serveCtrl">
    {{message}}
</div>

<script type="application/javascript">
    var app = angular.module('app', []);
    app.config(function($provide){
        $provide.service('serviceServe', function(){
            this.message = 'factory serve';
        })
    })
    app.controller('serveCtrl', function ($scope, serviceServe) {
        $scope.message = serviceServe.message
    })
</script>

语法糖:

app.service('serviceServe', function(){
    this.message = 'factory serve';
})

decorator:provider里面的装饰器——锦上添花神器

为什么使用decorator?
我们经常在使用某些Service的时候,更希望它能具备一些额外的功能,这时我们难道改这个Service吗?如果是系统自带的呢,改吗?这当然不现实吧。所以我们的装饰器decorator就发挥作用了,它能让已有的功能锦上添花。我们在config里面使用装饰器。

<div ng-controller="serveController">
    {{message}}
</div>

<script type="application/javascript">
    var app = angular.module("app", []);
    
    app.config(['$provide', function($provide) {
        $provide.decorator('infoService', function($delegate) {
            $delegate.email = "1028317108@qq.com";
            return $delegate;
        })
    }]);

    app.service('infoService', function() {
        this.name = 'zhaomenghuan';
    })

    app.controller('serveController', ['$scope', 'infoService', function($scope, infoService) {
        $scope.message = 'name:'+infoService.name + '<=>Emil:'+infoService.email;
    }]);
</script>

我们开始写的infoService没有Emil,我们通过装饰器decorator在不修改infoService代码的情况下添加了email新属性。

源码部分:

function provider(name, provider_) {
    if(isFunction(provider_)) {
        provider_ = providerInjector.instantiate(provider_);
    }
    if(!provider_.$get) {
        throw Error('Provider ' + name + ' must define $get factory method.');
    }
    return providerCache[name + providerSuffix] = provider_;
}

function factory(name, factoryFn) {
    return provider(name, { $get: factoryFn });
}

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
        return $injector.instantiate(constructor);
    }]);
}

function value(name, value) { return factory(name, valueFn(value)); }

function constant(name, value) {
    providerCache[name] = value;
    instanceCache[name] = value;
}

function decorator(serviceName, decorFn) {
    var origProvider = providerInjector.get(serviceName + providerSuffix),
        orig$get = origProvider.$get;
    origProvider.$get = function() {
        var origInstance = instanceInjector.invoke(orig$get, origProvider);
        return instanceInjector.invoke(decorFn, null, { $delegate: origInstance });
    };
}

注意:所有的供应商都只被实例化一次,也就说他们都是单例的,除了constant,所有的供应商都可以被装饰器(decorator)装饰。模块的config方法只能注入 provider 和 constant;模块的run方法中可被注入的依赖包括服务(service)、值(value)和参量(constant),不能注入”provider”类型的依赖。

注入依赖的几种方法

通过数组标注在方法的参数中声明依赖(优先考虑)

app.controller('serveCtrl', ['$scope', 'factoryServe', function ($scope, factoryServe) {
    $scope.message = factoryServe.message;
}])

定义在控制器构造方法的$inject属性中

app.$inject = ['$scope', 'factoryServe']
app.controller('serveCtrl', function ($scope, factoryServe) {
    $scope.message = factoryServe.message;
})

隐式声明依赖

app.controller('serveCtrl', function ($scope, factoryServe) {
    $scope.message = factoryServe.message;
})

注入器可以从函数的参数名中推断所依赖的服务。上面的函数中声明了$scope和factoryServe服务作为依赖。 这种方式可是代码更加简洁,但这种方式不能和JavaScript的代码混淆器一起使用。可以通过ng-annotate在minifying之前隐式的添加依赖。

通过在ng-app所在的DOM元素中添加ng-strict-di切换到严格的依赖注入模式,strict模式下使用隐式的标注会报错,如果用angular.bootstrap手动启动angular应用,我们可以通过设置config中的strictDi属性,启动strict模式。

angular.bootstrap(document, ['myApp'], {
  strictDi: true
});

注入器($injector)

angular注入器($injector类似于spring容器)负责创建、查找注入依赖, 每个module都会有自己的注入器。注入器负责从我们通过$provide创建的服务中创建注入的实例。只要你编写了一个带有可注入性的参数,你都能看到注入器是如何运行的。每一个AngularJS应用都有唯一一个$injector,当应用启动的时候它被创造出来,你可以通过将$injector注入到任何可注入函数中来得到它($injector知道如何注入它自己!)。

一旦你拥有了$injector,你可以动过调用get函数来获得任何一个已经被定义过的服务的实例。例如:

<div ng-controller="serveController">
    {{message}}
</div>

<script type="application/javascript">
    var app = angular.module('app', []);
    
    app.factory('factoryServe', function(){
        return {
            message: 'factory serve'
        }
    })
    
    var injector = angular.injector(['app']);
    app.controller('serveController', ['$scope', function ($scope) {
        var factoryServe = injector.get('factoryServe');
        $scope.message = factoryServe.message;
    }])
</script>

这是因为Angular在编译模板阶段为ng-controller指令以及实例化serveController注入相关的依赖。

injector.instantiate(serveController);

总结

react,angular,vue这三大框架带给前端变化,远远不只是让我们放弃JQuery这种直接操作dom的方式,也不仅仅是解决数据绑定的问题,我想更多的是改变我们的思维方式,用数据驱动的方式去更新视图,通过划分组件或者作用域,来实现整体的颗粒化,实现模块间分治,同时也改变了前后端协作的工作模式。

参考

vue.js组件
AngularJS的学习–$on、$emit和$broadcast的使用
前端需要知道的 依赖注入(Dependency Injection, DI)
理解AngularJS中的依赖注入
AngularJs中的provide

作者:技术宅小青年
链接:https://www.jianshu.com/p/0fc26c75f089
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Git 克隆分支 - 简书

mikel阅读(854)

来源: Git 克隆分支 – 简书

  1. 首先,你需要使用$ git clone这个命令克隆一个本地库。

e:
cd ProxSpace/pm3
git clone https://github.com/iceman1001/proxmark3.git "iceman"

这将远端的proxmark3项目的master分支克隆到 E:\ProxSpace\pm3下的iceman文件夹,
注意要克隆的目录(比如这里的iceman)要先创建好,且里面不能有文件(必须是空目录)。

之后不会克隆任何一个分支下来的。

  1. 查看本地与远端分支情况: git branch -a
    假定你现在的命令行正处于已经在当前克隆好的文件夹中。

git branch -a
image.png

绿色代表本地已经有的分支。remotes是远端分支情况

  1. 假定你需要一个autoBuild(此处假定远程库中已经存在此分支,也就是你需要克隆的)分支用于开发的话,你需要在autoBuild分支上开发,就必须创建远程origin的autoBuild分支到本地,于是用checkout这个命令创建本地autoBuild分支(并不会在本地创建分支文件夹的,只是在本地建立了个和远端同步的分支):

$ git checkout -b autoBuild origin/autoBuild

再同步下:

$ git pull

不执行这步的话,新checkout的分支并不会同步内容下来,以后获取分支更新内容也是用这个命令.

image.png

这样就实现了克隆autoBuild分支。

再输入git branch -a 来看看分支情况:

image.png
  1. 切换分支:

git checkout autoBuild

这样就在本地切换到了autoBuild分支了。

作者:Thresh0ld
链接:https://www.jianshu.com/p/c261867cf49a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

git 克隆指定分支代码_wudinaniya的博客-CSDN博客

mikel阅读(2022)

来源: git 克隆指定分支代码_wudinaniya的博客-CSDN博客

git拉取代码最常用的方式为:

git clone http://gitslab.yiqing.com/declare/about.git

这种方式没有指定分支,当代码有多个分支时,拉取的分支不一定就是master。比如,下面这个其实拉下来的就不是master分支代码:

  1. [root@izbp1845cet96se1qmb5ekz home]# git clone http://gitslab.yiqing.com/declare/about.git
  2. Cloning into ‘about’
  3. Username for ‘http://gitslab.yiqing.com’: account
  4. Password for ‘http://account@gitslab.yiqing.com’:
  5. remote: Counting objects: 116, done.
  6. remote: Compressing objects: 100% (86/86), done.
  7. remote: Total 116 (delta 45), reused 80 (delta 28)
  8. Receiving objects: 100% (116/116), 313.49 KiB | 508.00 KiB/s, done.
  9. Resolving deltas: 100% (45/45), done.
  10. [root@izbp1845cet96se1qmb5ekz home]# ls
  11. about
  12. [root@izbp1845cet96se1qmb5ekz home]# cd about/
  13. [root@izbp1845cet96se1qmb5ekz about]# git branch
  14. * develop

 

使用git拉代码时可以使用 -b 指定分支

指定拉 master 分支代码

git clone -b master http://gitslab.yiqing.com/declare/about.git

指定拉 develop 分支代码

git clone -b develop http://gitslab.yiqing.com/declare/about.git

 

查看当前项目拉的是哪个分支的代码:

进入项目根目录, 然后执行 git branch 命令

git branch

如图所示:

 

查看当前项目拉的是哪个分支的代码详情:

进入项目根目录, 然后执行 git branch -vv 命令

git branch -vv

如图:

查看分支上的递交情况:

进入项目根目录,执行 git show-branch

git show-branch

如图:

meteor下载太慢!3步教你搞定meteor下载和安装。_zhujuyu的博客-CSDN博客

mikel阅读(1102)

来源: meteor下载太慢!3步教你搞定meteor下载和安装。_zhujuyu的博客-CSDN博客

操作系统:ubuntu 16.04 LTS

根据官网指导 https://www.meteor.com/install,下载meteor需要使用以下命令:

curl https://install.meteor.com/ | sh

但是这种下载速度实在太慢,难以忍受,于是撸起袖子自己干。

Step1:手动下载压缩包

https://d3sqy0vbqsdhku.cloudfront.net/packages-bootstrap/1.2.1/meteor-bootstrap-os.linux.x86_64.tar.gz

我直接用浏览器下载meteor的压缩包,速度明显快了,但依然用了20分钟左右。

Step2:更改脚本

我们将官网提供的脚本命令导出到本地保存为install.meteor.sh

curl https://install.meteor.com > install.meteor.sh

然后使用编辑器vim/nano/gedit打开 install.meteor.sh,找到TARBALL_URL所在位置:

TARBALL_URL=https://static-meteor.netdna-ssl.com/package……..

我们将其注释掉,改用我们的本地地址,这个本地地址一定要在浏览器中能够打开看到才行。网上有方法说上传到服务器再下载实在是画蛇添足。

TARBALL-URL=”file:///home/usr/Downloads/meteor-bootstrap-os.linux.x86_64.tar.gz”

保存文件并退出。

Step3:运行脚本

sh install.meteor.sh

秒成。

注意:一定要下载osx版本的安装包,我将安装包下载后上传到了百度云盘,

Linux要下再Linux版本

用c#操作Mongodb(附demo) - 保安保安 - 博客园

mikel阅读(751)

来源: 用c#操作Mongodb(附demo) – 保安保安 – 博客园

因为需要,写了一个基于泛型的helper,这样要使用起来方便一点。

为了大家也不重复造轮子,所以发出来希望能帮到谁。

复杂的查询最好用linq,这也是mongodb官方建议的。

mongodb的C#配置

这部分很多文章都提到了,需要注意的是用的驱动与你的mongodb版本还有你.Net好像有点关系

我是mongodb-2.x,.NET4,driver我用的是1.x系列

2.x系列好像我这种配置用不起,大家可以试一试,貌似要.NET要4.5才行

驱动下载地址:

https://github.com/mongodb/mongo-csharp-driver

 

这里有个小坑,mongodb的数据库连接字符串和mySQL是不一样的,很多文章没有提到完整的连接字符串,花半天在官网上看到了

mongodb://username:password@myserver:port/databaseName

 

Model的编写

其他没什么,但请注意ID、时间的类型,用的是mongdoDB自己的数据类型

这里用了一个虚函数,是为了方便helper里面用泛型获取id

 

以下是Model的源码

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Driver;
using MongoDB.Bson;
namespace WindowsFormsApplication1.Model
{
    public abstract class MongoModel
    { 
        public ObjectId id { get; set; } 
        public BsonDateTime created_at { get; set; }
        public BsonDateTime updated_at { get; set; } 
    }

 public class AccountModel : MongoModel
    {
     //例子
        public AccountModel()
        { 
        }
         
        public string name { get; set; }
        
    }
}
复制代码

 

Helper的编写

因为mongodb的操作语句必须大量用到你的Model,因此考虑用泛型来做Helper

用Builder模式的原因无非是觉得好玩,你可以修改代码用构造函数直接初始化

我也没有用静态方法,你有需要可以自己修改

 

以下是helper的源码

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using MongoDB.Driver;
using MongoDB.Bson;
using MongoDB.Driver.Builders;

namespace FrameWork
{
    public class MongoHelper<T> where T : WindowsFormsApplication1.Model.MongoModel
    {
        public string conn;
        public string dbName;
        public string collectionName;

        private MongoCollection<T> collection;

        private MongoHelper()
        {

        }

        /// <summary>
        /// 设置你的collection
        /// </summary>
        public void SetCollection()
        {
            MongoClient client = new MongoClient(conn);
            var server = client.GetServer();
            var database = server.GetDatabase(dbName);
            collection = database.GetCollection<T>(collectionName);
        }

        /// <summary>
        /// 你用linq的时候会用到
        /// </summary>
        public void getCollection()
        {
            MongoClient client = new MongoClient(conn);
            var server = client.GetServer();
            var database = server.GetDatabase(dbName);
            collection = database.GetCollection<T>(collectionName);
        }

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public T Find(IMongoQuery query)
        {
            return this.collection.FindOne(query);
        }

        /**
         * 条件查询用linq
         * http://mongodb.github.io/mongo-csharp-driver/1.11/linq/
         * */
        public List<T> FindAll()
        {
            return this.collection.FindAll().ToList();
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public long Update(T model)
        {
            BsonDocument doc = BsonExtensionMethods.ToBsonDocument(model);
            WriteConcernResult res = this.collection.Update(Query.EQ("_id", model.id), new UpdateDocument(doc));
            return res.DocumentsAffected;
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public bool Insert(T model)
        {
            WriteConcernResult res = this.collection.Insert(model);
            return res.Ok;
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public bool Delete(T model)
        {
            WriteConcernResult res = this.collection.Remove(Query.EQ("_id", model.id));
            return res.Ok;
        }

        /// <summary>
        /// 构造器
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public class Builder<T> where T : WindowsFormsApplication1.Model.MongoModel
        {
            private MongoHelper<T> client;

            public Builder()
            {
                client = new MongoHelper<T>();
            }

            public void setConn(string conn)
            {
                client.conn = conn;
            }

            public void setDbName(string dbName)
            {
                client.dbName = dbName;
            }

            public void setCollectionName(string collectionName)
            {
                client.collectionName = collectionName;
            }

            public MongoHelper<T> build()
            {
                client.SetCollection();
                return client;
            }
        }
    }
}
复制代码

 

Helper的使用

很简单,我写在demo的form代码里了,注释也写的很清楚什么流程

1.设计好你的model

2.初始化数据库配置

3.build一个helper

4.调用方法

复制代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using DevComponents.DotNetBar;
using System.IO;
using FrameWork;

namespace WindowsFormsApplication1
{
    /**
     * 
     * MongoDB数据库增删改查DEMO
     * 任意拷贝、修改
     * 仅供学习
     * 曾维周 16/2/25
     * 
     * App独立开发群 533838427
     * 
     * */
    public partial class MainForm : DevComponents.DotNetBar.Metro.MetroForm
    {
        public Model.ConfModel conf = new Model.ConfModel();
        private bool isFirst = true;
        private string filePath;
        private List<Model.AccountModel> accounts = new List<Model.AccountModel>();
        private FrameWork.MongoHelper<Model.AccountModel> client;
        public MainForm()
        {
            InitializeComponent();
            this.Activated += new EventHandler(Form2_Activated);
        }

        void Form2_Activated(object sender, EventArgs e)
        {
            if (isFirst)
            {
                init();
                isFirst = false;
            }
        }

        void init()
        {
            /**
             * 
             * step-1
             * 配置你的mongodb链接
             * 请配置完
             * 
             * */
            conf.mongodb_dbAddr = "localhost";
        }

        private void buttonX2_Click(object sender, EventArgs e)
        {
            /**
             * 
             * step-2
             * 请操作前修改好你的model
             * 
             * step-3
             * 用builder初始化一个helper
             * 当然你也完全可以修改代码直接在构造函数里面初始化
             * 我是觉得好玩
             * 
             * */
            FrameWork.MongoHelper<Model.AccountModel>.Builder<Model.AccountModel> builder = new FrameWork.MongoHelper<Model.AccountModel>.Builder<Model.AccountModel>();
            builder.setCollectionName("你的collection名字");
            builder.setConn(conf.mongodb_conn);
            builder.setDbName(conf.mongodb_dbName);
            client = builder.build();
        }

        private void buttonX1_Click(object sender, EventArgs e)
        {
            //增
            Model.AccountModel account = new Model.AccountModel();
            account.name = "love";
            client.Insert(account);

            //删
            client.Delete(account);

            //改
            account.name = "not love";
            client.Update(account);

            //查
            Model.AccountModel res = client.Find(MongoDB.Driver.Builders.Query<Model.AccountModel>.EQ(xx => xx.id, account.id));

            //强烈建议用linq进行查询操作
            //http://mongodb.github.io/mongo-csharp-driver/1.11/linq/ 
            //var query = collection.AsQueryable<Model.AccountModel>().Where(e => e.FirstName == "John");

        }

    }
}
复制代码

 

 

参考资料

http://mongodb.github.io/mongo-csharp-driver/1.11/linq/

http://blog.csdn.net/haukwong/article/details/7840158

http://www.cnblogs.com/viprx/archive/2012/09/07/2674637.html

demo下载

链接: http://pan.baidu.com/s/1qX3vfdE 密码: buh2

轻量级ORM框架 Bankinate - 7tiny - 博客园

mikel阅读(715)

来源: 轻量级ORM框架 Bankinate – 7tiny – 博客园

【前言】

前面讲过ORM的前世今生,对ORM框架不了解的朋友可以参考博文:https://www.cnblogs.com/7tiny/p/9551754.html

今天,我们主要通过设计一款轻量级的ORM框架来介绍:如何实现一个ORM框架”

  文末给出了GitHub源码地址~

【基本要素】

既然是ORM框架,那么必不可或缺的三点:

  1.SQL语句的自动生成

  2.数据结果集自动映射到类型实体

  3.多数据库的支持

甚至可以在此三点的基础上扩展出更多的:

  1.缓存处理

  2.Api的封装

  3.日志系统

基于以上几点,那么我们逐步开始我们的设计:

为了功能抽象和细化的职责划分,我们将各个功能点拆分成为各个组件,灵活进行装配。

数据存储核心:调用底层数据库驱动执行SQL语句,将数据持久化

表映射描述器:描述表和实体的映射关系

SQL语句转化器:将封装的数据操作Api转化成对应数据库的Sql语句

数据操作上下文:用户数据操作信息传递,包装,数据库连接管理等,缓存核心配置信息的承载

缓存核心:用户ORM框架的缓存支持(一级缓存/二级缓存)

【实现细节】

我们抽象出核心功能组件后,对各个功能组件进行详细设计:

  数据存储核心:

数据存储核心主要包括对多种数据库驱动的封装调用,读写分离的简单策略,查询数据集合与强类型实体的映射(性能优化点,目前采用Expression 表达式树缓存委托方式)。

这里以封装的支持多种关系型数据库的DbHelper形式呈现

复制代码
  1 /*********************************************************
  2  * CopyRight: 7TINY CODE BUILDER. 
  3  * Version: 5.0.0
  4  * Author: 7tiny
  5  * Address: Earth
  6  * Create: 2018-04-19 21:34:01
  7  * Modify: 2018-04-19 21:34:01
  8  * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 
  9  * GitHub: https://github.com/sevenTiny 
 10  * Personal web site: http://www.7tiny.com 
 11  * Technical WebSit: http://www.cnblogs.com/7tiny/ 
 12  * Description: 
 13  * Thx , Best Regards ~
 14  *********************************************************/
 15 using MySql.Data.MySqlClient;
 16 using System;
 17 using System.Collections.Generic;
 18 using System.ComponentModel;
 19 using System.Data;
 20 using System.Data.Common;
 21 using System.Data.SqlClient;
 22 using System.Linq;
 23 using System.Linq.Expressions;
 24 using System.Reflection;
 25 using System.Threading.Tasks;
 26 
 27 namespace SevenTiny.Bantina.Bankinate
 28 {
 29     public enum DataBaseType
 30     {
 31         SqlServer,
 32         MySql,
 33         Oracle,
 34         MongoDB
 35     }
 36     public abstract class DbHelper
 37     {
 38         #region ConnString 链接字符串声明
 39 
 40         /// <summary>
 41         /// 连接字符串 ConnString_Default 默认,且赋值时会直接覆盖掉读写
 42         /// </summary>
 43         private static string _connString;
 44         public static string ConnString_Default
 45         {
 46             get { return _connString; }
 47             set
 48             {
 49                 _connString = value;
 50                 ConnString_RW = _connString;
 51                 ConnString_R = _connString;
 52             }
 53         }
 54         /// <summary>
 55         /// 连接字符串 ConnString_RW 读写数据库使用
 56         /// </summary>
 57         public static string ConnString_RW { get; set; } = _connString;
 58         /// <summary>
 59         /// 连接字符串 ConnString_R 读数据库使用
 60         /// </summary>
 61         public static string ConnString_R { get; set; } = _connString;
 62         /// <summary>
 63         /// DataBaseType Select default:mysql
 64         /// </summary>
 65         public static DataBaseType DbType { get; set; } = DataBaseType.MySql;
 66 
 67         #endregion
 68 
 69         #region ExcuteNonQuery 执行sql语句或者存储过程,返回影响的行数---ExcuteNonQuery
 70         public static int ExecuteNonQuery(string commandTextOrSpName, CommandType commandType = CommandType.Text)
 71         {
 72             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_RW))
 73             {
 74                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
 75                 {
 76                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
 77                     return cmd.DbCommand.ExecuteNonQuery();
 78                 }
 79             }
 80         }
 81         public static int ExecuteNonQuery(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary)
 82         {
 83             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_RW))
 84             {
 85                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
 86                 {
 87                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType, dictionary);//参数增加了commandType 可以自己编辑执行方式
 88                     return cmd.DbCommand.ExecuteNonQuery();
 89                 }
 90             }
 91         }
 92         public static void BatchExecuteNonQuery(IEnumerable<BatchExecuteModel> batchExecuteModels)
 93         {
 94             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_RW))
 95             {
 96                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
 97                 {
 98                     foreach (var item in batchExecuteModels)
 99                     {
100                         PreparCommand(conn.DbConnection, cmd.DbCommand, item.CommandTextOrSpName, item.CommandType, item.ParamsDic);
101                         cmd.DbCommand.ExecuteNonQuery();
102                     }
103                 }
104             }
105         }
106         public static Task<int> ExecuteNonQueryAsync(string commandTextOrSpName, CommandType commandType = CommandType.Text)
107         {
108             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_RW))
109             {
110                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
111                 {
112                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
113                     return cmd.DbCommand.ExecuteNonQueryAsync();
114                 }
115             }
116         }
117         public static Task<int> ExecuteNonQueryAsync(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary)
118         {
119             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_RW))
120             {
121                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
122                 {
123                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType, dictionary);//参数增加了commandType 可以自己编辑执行方式
124                     return cmd.DbCommand.ExecuteNonQueryAsync();
125                 }
126             }
127         }
128         public static void BatchExecuteNonQueryAsync(IEnumerable<BatchExecuteModel> batchExecuteModels)
129         {
130             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_RW))
131             {
132                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
133                 {
134                     foreach (var item in batchExecuteModels)
135                     {
136                         PreparCommand(conn.DbConnection, cmd.DbCommand, item.CommandTextOrSpName, item.CommandType, item.ParamsDic);
137                         cmd.DbCommand.ExecuteNonQueryAsync();
138                     }
139                 }
140             }
141         }
142         #endregion
143 
144         #region ExecuteScalar 执行sql语句或者存储过程,执行单条语句,返回单个结果---ScalarExecuteScalar
145         public static object ExecuteScalar(string commandTextOrSpName, CommandType commandType = CommandType.Text)
146         {
147             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
148             {
149                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
150                 {
151                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
152                     return cmd.DbCommand.ExecuteScalar();
153                 }
154             }
155         }
156         public static object ExecuteScalar(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary)
157         {
158             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
159             {
160                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
161                 {
162                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType, dictionary);
163                     return cmd.DbCommand.ExecuteScalar();
164                 }
165 
166             }
167         }
168         public static Task<object> ExecuteScalarAsync(string commandTextOrSpName, CommandType commandType = CommandType.Text)
169         {
170             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
171             {
172                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
173                 {
174                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
175                     return cmd.DbCommand.ExecuteScalarAsync();
176                 }
177             }
178         }
179         public static Task<object> ExecuteScalarAsync(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary)
180         {
181             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
182             {
183                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
184                 {
185                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType, dictionary);
186                     return cmd.DbCommand.ExecuteScalarAsync();
187                 }
188 
189             }
190         }
191         #endregion
192 
193         #region ExecuteReader 执行sql语句或者存储过程,返回DataReader---DataReader
194         public static DbDataReader ExecuteReader(string commandTextOrSpName, CommandType commandType = CommandType.Text)
195         {
196             //sqlDataReader不能用using 会关闭conn 导致不能获取到返回值。注意:DataReader获取值时必须保持连接状态
197             SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW);
198             DbCommandCommon cmd = new DbCommandCommon(DbType);
199             PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
200             return cmd.DbCommand.ExecuteReader(CommandBehavior.CloseConnection);
201         }
202         public static DbDataReader ExecuteReader(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary)
203         {
204             //sqlDataReader不能用using 会关闭conn 导致不能获取到返回值。注意:DataReader获取值时必须保持连接状态
205             SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW);
206             DbCommandCommon cmd = new DbCommandCommon(DbType);
207             PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType, dictionary);
208             return cmd.DbCommand.ExecuteReader(CommandBehavior.CloseConnection);
209         }
210         #endregion
211 
212         #region ExecuteDataTable 执行sql语句或者存储过程,返回一个DataTable---DataTable
213 
214         /**
215          * Update At 2017-3-2 14:58:45
216          * Add the ExecuteDataTable Method into Sql_Helper_DG  
217          **/
218         public static DataTable ExecuteDataTable(string commandTextOrSpName, CommandType commandType = CommandType.Text)
219         {
220             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
221             {
222                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
223                 {
224                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
225                     using (DbDataAdapterCommon da = new DbDataAdapterCommon(DbType, cmd.DbCommand))
226                     {
227                         DataSet ds = new DataSet();
228                         da.Fill(ds);
229                         if (ds.Tables.Count > 0)
230                         {
231                             return ds.Tables[0];
232                         }
233                         return default(DataTable);
234                     }
235                 }
236             }
237         }
238         public static DataTable ExecuteDataTable(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary)
239         {
240             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
241             {
242                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
243                 {
244                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType, dictionary);
245                     using (DbDataAdapterCommon da = new DbDataAdapterCommon(DbType, cmd.DbCommand))
246                     {
247                         DataSet ds = new DataSet();
248                         da.Fill(ds);
249                         if (ds.Tables.Count > 0)
250                         {
251                             return ds.Tables[0];
252                         }
253                         return default(DataTable);
254                     }
255                 }
256             }
257         }
258         #endregion
259 
260         #region ExecuteDataSet 执行sql语句或者存储过程,返回一个DataSet---DataSet
261         public static DataSet ExecuteDataSet(string commandTextOrSpName, CommandType commandType = CommandType.Text)
262         {
263             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
264             {
265                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
266                 {
267                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType);
268                     using (DbDataAdapterCommon da = new DbDataAdapterCommon(DbType, cmd.DbCommand))
269                     {
270                         DataSet ds = new DataSet();
271                         da.Fill(ds);
272                         return ds;
273                     }
274                 }
275             }
276         }
277         public static DataSet ExecuteDataSet(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary)
278         {
279             using (SqlConnection_RW conn = new SqlConnection_RW(DbType, ConnString_R, ConnString_RW))
280             {
281                 using (DbCommandCommon cmd = new DbCommandCommon(DbType))
282                 {
283                     PreparCommand(conn.DbConnection, cmd.DbCommand, commandTextOrSpName, commandType, dictionary);
284                     using (DbDataAdapterCommon da = new DbDataAdapterCommon(DbType, cmd.DbCommand))
285                     {
286                         DataSet ds = new DataSet();
287                         da.Fill(ds);
288                         return ds;
289                     }
290                 }
291             }
292         }
293         #endregion
294 
295         #region ExecuteList Entity 执行sql语句或者存储过程,返回一个List<T>---List<T>
296         public static List<Entity> ExecuteList<Entity>(string commandTextOrSpName, CommandType commandType = CommandType.Text) where Entity : class
297         {
298             return GetListFromDataSetV2<Entity>(ExecuteDataSet(commandTextOrSpName, commandType));
299         }
300         public static List<Entity> ExecuteList<Entity>(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary) where Entity : class
301         {
302             return GetListFromDataSetV2<Entity>(ExecuteDataSet(commandTextOrSpName, commandType, dictionary));
303         }
304         #endregion
305 
306         #region ExecuteEntity 执行sql语句或者存储过程,返回一个Entity---Entity
307         public static Entity ExecuteEntity<Entity>(string commandTextOrSpName, CommandType commandType = CommandType.Text) where Entity : class
308         {
309             return GetEntityFromDataSetV2<Entity>(ExecuteDataSet(commandTextOrSpName, commandType));
310         }
311         public static Entity ExecuteEntity<Entity>(string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary) where Entity : class
312         {
313             return GetEntityFromDataSetV2<Entity>(ExecuteDataSet(commandTextOrSpName, commandType, dictionary));
314         }
315         #endregion
316 
317         #region ---PreparCommand 构建一个通用的command对象供内部方法进行调用---
318         private static void PreparCommand(DbConnection conn, DbCommand cmd, string commandTextOrSpName, CommandType commandType, IDictionary<string, object> dictionary = null)
319         {
320             //打开连接
321             if (conn.State != ConnectionState.Open)
322             {
323                 conn.Open();
324             }
325 
326             //设置SqlCommand对象的属性值
327             cmd.Connection = conn;
328             cmd.CommandType = commandType;
329             cmd.CommandText = commandTextOrSpName;
330             cmd.CommandTimeout = 60;
331 
332             if (dictionary != null)
333             {
334                 cmd.Parameters.Clear();
335                 DbParameter[] parameters;
336                 switch (conn)
337                 {
338                     case SqlConnection s:
339                         parameters = new SqlParameter[dictionary.Count];
340                         break;
341                     case MySqlConnection m:
342                         parameters = new MySqlParameter[dictionary.Count];
343                         break;
344                     //case OracleConnection o:
345                     //parameters = new OracleParameter[dictionary.Count];
346                     //break;
347                     default:
348                         parameters = new SqlParameter[dictionary.Count];
349                         break;
350                 }
351 
352                 string[] keyArray = dictionary.Keys.ToArray();
353                 object[] valueArray = dictionary.Values.ToArray();
354 
355                 for (int i = 0; i < parameters.Length; i++)
356                 {
357                     switch (conn)
358                     {
359                         case SqlConnection s:
360                             parameters[i] = new SqlParameter(keyArray[i], valueArray[i]);
361                             break;
362                         case MySqlConnection m:
363                             parameters[i] = new MySqlParameter(keyArray[i], valueArray[i]);
364                             break;
365                         //case OracleConnection o:
366                         // parameters[i] = new OracleParameter(keyArray[i], valueArray[i]);
367                         // break;
368                         default:
369                             parameters[i] = new SqlParameter(keyArray[i], valueArray[i]);
370                             break;
371                     }
372                 }
373                 cmd.Parameters.AddRange(parameters);
374             }
375         }
376         #endregion
377 
378         #region 通过Model反射返回结果集 Model为 Entity 泛型变量的真实类型---反射返回结果集
379         public static List<Entity> GetListFromDataSet<Entity>(DataSet ds) where Entity : class
380         {
381             List<Entity> list = new List<Entity>();//实例化一个list对象
382             PropertyInfo[] propertyInfos = typeof(Entity).GetProperties();     //获取T对象的所有公共属性
383 
384             DataTable dt = ds.Tables[0];//获取到ds的dt
385             if (dt.Rows.Count > 0)
386             {
387                 //判断读取的行是否>0 即数据库数据已被读取
388                 foreach (DataRow row in dt.Rows)
389                 {
390                     Entity model1 = System.Activator.CreateInstance<Entity>();//实例化一个对象,便于往list里填充数据
391                     foreach (PropertyInfo propertyInfo in propertyInfos)
392                     {
393                         try
394                         {
395                             //遍历模型里所有的字段
396                             if (row[propertyInfo.Name] != System.DBNull.Value)
397                             {
398                                 //判断值是否为空,如果空赋值为null见else
399                                 if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
400                                 {
401                                     //如果convertsionType为nullable类,声明一个NullableConverter类,该类提供从Nullable类到基础基元类型的转换
402                                     NullableConverter nullableConverter = new NullableConverter(propertyInfo.PropertyType);
403                                     //将convertsionType转换为nullable对的基础基元类型
404                                     propertyInfo.SetValue(model1, Convert.ChangeType(row[propertyInfo.Name], nullableConverter.UnderlyingType), null);
405                                 }
406                                 else
407                                 {
408                                     propertyInfo.SetValue(model1, Convert.ChangeType(row[propertyInfo.Name], propertyInfo.PropertyType), null);
409                                 }
410                             }
411                             else
412                             {
413                                 propertyInfo.SetValue(model1, null, null);//如果数据库的值为空,则赋值为null
414                             }
415                         }
416                         catch (Exception)
417                         {
418                             propertyInfo.SetValue(model1, null, null);//如果数据库的值为空,则赋值为null
419                         }
420                     }
421                     list.Add(model1);//将对象填充到list中
422                 }
423             }
424             return list;
425         }
426         public static List<Entity> GetListFromDataSetV2<Entity>(DataSet ds) where Entity : class
427         {
428             List<Entity> list = new List<Entity>();
429             DataTable dt = ds.Tables[0];
430             if (dt.Rows.Count > 0)
431             {
432                 foreach (DataRow row in dt.Rows)
433                 {
434                     Entity entity = FillAdapter<Entity>.AutoFill(row);
435                     list.Add(entity);
436                 }
437             }
438             return list;
439         }
440         public static Entity GetEntityFromDataReader<Entity>(DbDataReader reader) where Entity : class
441         {
442             Entity model = System.Activator.CreateInstance<Entity>();           //实例化一个T类型对象
443             PropertyInfo[] propertyInfos = model.GetType().GetProperties();     //获取T对象的所有公共属性
444             using (reader)
445             {
446                 if (reader.Read())
447                 {
448                     foreach (PropertyInfo propertyInfo in propertyInfos)
449                     {
450                         //遍历模型里所有的字段
451                         if (reader[propertyInfo.Name] != System.DBNull.Value)
452                         {
453                             //判断值是否为空,如果空赋值为null见else
454                             if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
455                             {
456                                 //如果convertsionType为nullable类,声明一个NullableConverter类,该类提供从Nullable类到基础基元类型的转换
457                                 NullableConverter nullableConverter = new NullableConverter(propertyInfo.PropertyType);
458                                 //将convertsionType转换为nullable对的基础基元类型
459                                 propertyInfo.SetValue(model, Convert.ChangeType(reader[propertyInfo.Name], nullableConverter.UnderlyingType), null);
460                             }
461                             else
462                             {
463                                 propertyInfo.SetValue(model, Convert.ChangeType(reader[propertyInfo.Name], propertyInfo.PropertyType), null);
464                             }
465                         }
466                         else
467                         {
468                             propertyInfo.SetValue(model, null, null);//如果数据库的值为空,则赋值为null
469                         }
470                     }
471                     return model;//返回T类型的赋值后的对象 model
472                 }
473             }
474             return default(Entity);//返回引用类型和值类型的默认值0或null
475         }
476         public static Entity GetEntityFromDataSet<Entity>(DataSet ds) where Entity : class
477         {
478             return GetListFromDataSet<Entity>(ds).FirstOrDefault();
479         }
480         public static Entity GetEntityFromDataSetV2<Entity>(DataSet ds) where Entity : class
481         {
482             DataTable dt = ds.Tables[0];// 获取到ds的dt
483             if (dt.Rows.Count > 0)
484             {
485                 return FillAdapter<Entity>.AutoFill(dt.Rows[0]);
486             }
487             return default(Entity);
488         }
489         #endregion
490     }
491 
492     /// <summary>
493     /// Auto Fill Adapter
494     /// </summary>
495     /// <typeparam name="Entity"></typeparam>
496     internal class FillAdapter<Entity>
497     {
498         private static readonly Func<DataRow, Entity> funcCache = GetFactory();
499         public static Entity AutoFill(DataRow row)
500         {
501             return funcCache(row);
502         }
503         private static Func<DataRow, Entity> GetFactory()
504         {
505             var type = typeof(Entity);
506             var rowType = typeof(DataRow);
507             var rowDeclare = Expression.Parameter(rowType, "row");
508             var instanceDeclare = Expression.Parameter(type, "t");
509             //new Student()
510             var newExpression = Expression.New(type);
511             //(t = new Student())
512             var instanceExpression = Expression.Assign(instanceDeclare, newExpression);
513             //row == null
514             var nullEqualExpression = Expression.NotEqual(rowDeclare, Expression.Constant(null));
515             var containsMethod = typeof(DataColumnCollection).GetMethod("Contains");
516             var indexerMethod = rowType.GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, new[] { new ParameterModifier(1) });
517             var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
518             var setExpressions = new List<Expression>();
519             //row.Table.Columns
520             var columns = Expression.Property(Expression.Property(rowDeclare, "Table"), "Columns");
521             foreach (var propertyInfo in properties)
522             {
523                 if (propertyInfo.CanWrite)
524                 {
525                     //Id,Id is a property of Entity
526                     var propertyName = Expression.Constant(propertyInfo.Name, typeof(string));
527                     //row.Table.Columns.Contains("Id")
528                     var checkIfContainsColumn = Expression.Call(columns, containsMethod, propertyName);
529                     //t.Id
530                     var propertyExpression = Expression.Property(instanceDeclare, propertyInfo);
531                     //row.get_Item("Id")
532                     var value = Expression.Call(rowDeclare, indexerMethod, propertyName);
533                     //t.Id = Convert(row.get_Item("Id"), Int32)
534                     var propertyAssign = Expression.Assign(propertyExpression, Expression.Convert(value, propertyInfo.PropertyType));
535                     //t.Id = default(Int32)
536                     var propertyAssignDefault = Expression.Assign(propertyExpression, Expression.Default(propertyInfo.PropertyType));
537                     //if(row.Table.Columns.Contains("Id")&&!value.Equals(DBNull.Value<>)) {t.Id = Convert(row.get_Item("Id"), Int32)}else{t.Id = default(Int32)}
538                     var checkRowNull = Expression.IfThenElse(Expression.AndAlso(checkIfContainsColumn, Expression.NotEqual(value, Expression.Constant(System.DBNull.Value))), propertyAssign, propertyAssignDefault);
539                     //var checkContains = Expression.IfThen(checkIfContainsColumn, propertyAssign);
540                     setExpressions.Add(checkRowNull);
541                 }
542             }
543             var checkIfRowIsNull = Expression.IfThen(nullEqualExpression, Expression.Block(setExpressions));
544             var body = Expression.Block(new[] { instanceDeclare }, instanceExpression, checkIfRowIsNull, instanceDeclare);
545             return Expression.Lambda<Func<DataRow, Entity>>(body, rowDeclare).Compile();
546         }
547     }
548 
549     /**
550     * author:qixiao
551     * time:2017-9-18 18:02:23
552     * description:safe create sqlconnection support
553     * */
554     internal class SqlConnection_RW : IDisposable
555     {
556         /// <summary>
557         /// SqlConnection
558         /// </summary>
559         public DbConnection DbConnection { get; set; }
560 
561         public SqlConnection_RW(DataBaseType dataBaseType, string ConnString_RW)
562         {
563             this.DbConnection = GetDbConnection(dataBaseType, ConnString_RW);
564         }
565         /**
566          * if read db disabled,switchover to read write db immediately
567          * */
568         public SqlConnection_RW(DataBaseType dataBaseType, string ConnString_R, string ConnString_RW)
569         {
570             try
571             {
572                 this.DbConnection = GetDbConnection(dataBaseType, ConnString_R);
573             }
574             catch (Exception)
575             {
576                 this.DbConnection = GetDbConnection(dataBaseType, ConnString_RW);
577             }
578         }
579 
580         /// <summary>
581         /// GetDataBase ConnectionString by database type and connection string -- private use
582         /// </summary>
583         /// <param name="dataBaseType"></param>
584         /// <param name="ConnString"></param>
585         /// <returns></returns>
586         private DbConnection GetDbConnection(DataBaseType dataBaseType, string ConnString)
587         {
588             switch (dataBaseType)
589             {
590                 case DataBaseType.SqlServer:
591                     return new SqlConnection(ConnString);
592                 case DataBaseType.MySql:
593                     return new MySqlConnection(ConnString);
594                 case DataBaseType.Oracle:
595                 //return new OracleConnection(ConnString);
596                 default:
597                     return new SqlConnection(ConnString);
598             }
599         }
600         /// <summary>
601         /// Must Close Connection after use
602         /// </summary>
603         public void Dispose()
604         {
605             if (this.DbConnection != null)
606             {
607                 this.DbConnection.Dispose();
608             }
609         }
610     }
611     /// <summary>
612     /// Common sqlcommand
613     /// </summary>
614     internal class DbCommandCommon : IDisposable
615     {
616         /// <summary>
617         /// common dbcommand
618         /// </summary>
619         public DbCommand DbCommand { get; set; }
620         public DbCommandCommon(DataBaseType dataBaseType)
621         {
622             this.DbCommand = GetDbCommand(dataBaseType);
623         }
624 
625         /// <summary>
626         /// Get DbCommand select database type
627         /// </summary>
628         /// <param name="dataBaseType"></param>
629         /// <returns></returns>
630         private DbCommand GetDbCommand(DataBaseType dataBaseType)
631         {
632             switch (dataBaseType)
633             {
634                 case DataBaseType.SqlServer:
635                     return new SqlCommand();
636                 case DataBaseType.MySql:
637                     return new MySqlCommand();
638                 case DataBaseType.Oracle:
639                 //return new OracleCommand();
640                 default:
641                     return new SqlCommand();
642             }
643         }
644         /// <summary>
645         /// must dispose after use
646         /// </summary>
647         public void Dispose()
648         {
649             if (this.DbCommand != null)
650             {
651                 this.DbCommand.Dispose();
652             }
653         }
654     }
655     /// <summary>
656     /// DbDataAdapterCommon
657     /// </summary>
658     internal class DbDataAdapterCommon : DbDataAdapter, IDisposable
659     {
660         public DbDataAdapter DbDataAdapter { get; set; }
661         public DbDataAdapterCommon(DataBaseType dataBaseType, DbCommand dbCommand)
662         {
663             //get dbAdapter
664             this.DbDataAdapter = GetDbAdapter(dataBaseType, dbCommand);
665             //provid select command
666             this.SelectCommand = dbCommand;
667         }
668         private DbDataAdapter GetDbAdapter(DataBaseType dataBaseType, DbCommand dbCommand)
669         {
670             switch (dataBaseType)
671             {
672                 case DataBaseType.SqlServer:
673                     return new SqlDataAdapter();
674                 case DataBaseType.MySql:
675                     return new MySqlDataAdapter();
676                 case DataBaseType.Oracle:
677                 //return new OracleDataAdapter();
678                 default:
679                     return new SqlDataAdapter();
680             }
681         }
682         /// <summary>
683         /// must dispose after use
684         /// </summary>
685         public new void Dispose()
686         {
687             if (this.DbDataAdapter != null)
688             {
689                 this.DbDataAdapter.Dispose();
690             }
691         }
692     }
693 
694     /// <summary>
695     /// 用于批量操作的批量操作实体
696     /// </summary>
697     public class BatchExecuteModel
698     {
699         /// <summary>
700         /// 执行的语句或者存储过程名称
701         /// </summary>
702         public string CommandTextOrSpName { get; set; }
703         /// <summary>
704         /// 执行类别,默认执行sql语句
705         /// </summary>
706         public CommandType CommandType { get; set; } = CommandType.Text;
707         /// <summary>
708         /// 执行语句的参数字典
709         /// </summary>
710         public IDictionary<string, object> ParamsDic { get; set; }
711     }
712 }
复制代码

  表映射描述器:

 

表映射描述器定义了一系列对实体的标签,以描述该实体和数据库以及数据库表之间的映射关系。除此之外还扩展了对数据库表缓存的描述。

  Sql语句转化器:

实体类+条件 Sql语句转化过程:

Sql语句转化器的功能为将友好查询Api传递的Lambda表达式语句转化成对应功能的Sql条件语句,以及对应不同数据库生成针对数据库的Sql语句。

  数据操作上下文:

数据库操作上下文作为全部数据操作的载体,在DbContext的基础上分离出SqlDbContext和NoSqlDbContext,分别支持关系型数据库和非关系型数据库。在关系型数据库上下文基础上又可以衍生出各种类型的关系型数据库上下文。该设计保证了组件的水平扩容的能力。

上下文除了维系各种数据库操作的支持以外,还扩展出了缓存组件的强力支持,可以在上下文中设置当前会话的缓存配置项。

  缓存核心:

缓存核心拓扑:

缓存核心处理流程:

详细缓存策略:

缓存的处理逻辑比较细化,组件的缓存统一由缓存管理核心处理,缓存核心分别调用一级缓存和二级缓存处理对象缓存。缓存处理的步骤如下:

1.判断是否开启了二级缓存,如果未开启,跳过。

2.如果开启了二级缓存,检查是否存在二级缓存,如果不存在,则判断是否对实体开启表缓存,如果开启,则开启后台线程扫描表,存储表数据为二级缓存。

3.判断是否存在一级缓存,如果存在,直接返回一级缓存的结果集。

4.如果一级缓存不存在,则执行查询命令,并写入一级缓存。

当二级缓存存在时:

1.拿出二级缓存并对二级缓存执行增删改查操作,并执行对应的增删改操作持久化过程。

2.后续所有查询优先从二级缓存中获取。

如果二级缓存开启状态,执行增删改命令的同时,会同步维护持久化数据和二级缓存数据。

备注:

二级缓存是针对某表进行的策略,不是针对所有数据库表的,如果数据库表数量太大,则不建议对该表开启二级缓存,以免耗费大量的内存资源。

【SevenTiny.Bantina.Bankinate ORM框架的使用】

Nuget包源搜索 SevenTiny.Bantina.Bankinate 安装

 新建一个数据库上下文类(对应数据库名称,类似EntityFramework的上下文类)

如果和库名不一致,则使用DataBase标签进行特殊映射。并在上下文类传递链接字符串和一级缓存和二级缓存的开启配置(默认都关闭)。

这里测试使用SQLServer数据库,因此继承了SQLServerDbContext,如果是其他数据库,则继承对应的数据库。

根据数据库表创建实体类(实体类可以使用代码生成器自动生成,生成器迭代升级中,有需求可以联系博主)。

这里提供了一个Student类(表),并使用 TableCaching 标签指定了该表二级缓存的开启(重载可以配置该表二级缓存时间)。

Id为主键,并且是自增列。

Api列表:

SevenTiny.Bantina.Bankinate ORM框架提供了一系列标准的非标准的数据查询api,api基于Lambda Expression写法,以便习惯了.Net平台Linq的人群很快上手,无学习成本。

复制代码
 1 /*********************************************************
 2  * CopyRight: 7TINY CODE BUILDER. 
 3  * Version: 5.0.0
 4  * Author: 7tiny
 5  * Address: Earth
 6  * Create: 2018-04-19 23:58:08
 7  * Modify: 2018-04-19 23:58:08
 8  * E-mail: dong@7tiny.com | sevenTiny@foxmail.com 
 9  * GitHub: https://github.com/sevenTiny 
10  * Personal web site: http://www.7tiny.com 
11  * Technical WebSit: http://www.cnblogs.com/7tiny/ 
12  * Description: 
13  * Thx , Best Regards ~
14  *********************************************************/
15 using System;
16 using System.Collections.Generic;
17 using System.Data;
18 using System.Linq.Expressions;
19 
20 namespace SevenTiny.Bantina.Bankinate
21 {
22     /// <summary>
23     /// 通用的Api接口,具备基础的操作,缓存
24     /// </summary>
25     public interface IDbContext : IDisposable, IBaseOerate, ICacheable
26     {
27     }
28 
29     /// <summary>
30     /// 基础操作Api
31     /// </summary>
32     public interface IBaseOerate
33     {
34         void Add<TEntity>(TEntity entity) where TEntity : class;
35         void AddAsync<TEntity>(TEntity entity) where TEntity : class;
36         void Add<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;
37         void AddAsync<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;
38 
39         void Update<TEntity>(Expression<Func<TEntity, bool>> filter, TEntity entity) where TEntity : class;
40         void UpdateAsync<TEntity>(Expression<Func<TEntity, bool>> filter, TEntity entity) where TEntity : class;
41 
42         void Delete<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : class;
43         void DeleteAsync<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : class;
44 
45         bool QueryExist<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : class;
46         int QueryCount<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : class;
47         TEntity QueryOne<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : class;
48         List<TEntity> QueryList<TEntity>(Expression<Func<TEntity, bool>> filter) where TEntity : class;
49     }
50 
51     /// <summary>
52     /// 执行sql语句扩展Api
53     /// </summary>
54     public interface IExecuteSqlOperate
55     {
56         void ExecuteSql(string sqlStatement, IDictionary<string, object> parms = null);
57         void ExecuteSqlAsync(string sqlStatement, IDictionary<string, object> parms = null);
58         DataSet ExecuteQueryDataSetSql(string sqlStatement, IDictionary<string, object> parms = null);
59         object ExecuteQueryOneDataSql(string sqlStatement, IDictionary<string, object> parms = null);
60         TEntity ExecuteQueryOneSql<TEntity>(string sqlStatement, IDictionary<string, object> parms = null) where TEntity : class;
61         List<TEntity> ExecuteQueryListSql<TEntity>(string sqlStatement, IDictionary<string, object> parms = null) where TEntity : class;
62     }
63 
64     /// <summary>
65     /// 分页查询扩展Api
66     /// </summary>
67     public interface IQueryPagingOperate
68     {
69         List<TEntity> QueryListPaging<TEntity>(int pageIndex, int pageSize, Expression<Func<TEntity, object>> orderBy, Expression<Func<TEntity, bool>> filter, bool isDESC = false) where TEntity : class;
70         List<TEntity> QueryListPaging<TEntity>(int pageIndex, int pageSize, Expression<Func<TEntity, object>> orderBy, Expression<Func<TEntity, bool>> filter, out int count, bool isDESC = false) where TEntity : class;
71     }
72 
73     /// <summary>
74     /// 缓存接口,实现该接口的类必须具备ORM缓存
75     /// </summary>
76     public interface ICacheable
77     {
78     }
79 }
复制代码

查询全部(可以根据使用场景组装lambda表达式):

新增一条数据:

修改数据:

删除数据:

【框架缓存性能测试】

缓存性能测试的单元测试代码:

 一级二级缓存测试代码

  不带缓存的查询:

 执行查询100次耗时:6576.8009 ms

  一级缓存开启,二级缓存未开启:

执行查询10000次耗时:1598.2349 ms

  一级缓存和二级缓存同时开启:

执行查询10000次耗时:5846.0249

实际上,二级缓存开启以后,最初的查询会走一级缓存。待二级缓存对表扫描结束以后,后续查询将维护二级缓存数据,不再访问数据库表。

【系统展望】

1.查询api对特定列查询列的支持(性能提升)

2.对一对多关系的主外键查询支持

3.更多种类数据库的支持

4.打点日志的支持

【总结】

通过本文的一系列分析,不知各位看官对ORM框架的设计思路有没有一个整体的认识。如果在分析结束还没有充分理解设计思路,那么简单粗暴直接上GitHub克隆源码看呗~

项目基于.NetStandard 2.0构建,因此支持.NetCore2.0以上以及.NetFramework4.6.1以上。对更低版本不兼容。

虽然原理分析比较冗长,但是代码结构还是非常清晰的,有任何建议,还望不吝赐教,对代码质量的指教非常期待 ^_^

附源码地址: https://github.com/sevenTiny/SevenTiny.Bantina.Bankinate