Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework C

Webservice WCF WebApi

 

注明:改编加组合javascript

 

在.net平台下,有大量的技术让你建立一个HTTP服务,像Web Service,WCF,如今又出了Web API。在.net平台下,你有不少的选择来构建一个HTTP Services。我分享一下我对Web Service、WCF以及Web API的见解。html

  Web Service前端

  一、它是基于SOAP协议的,数据格式是XMLhtml5

  二、只支持HTTP协议java

  三、它不是开源的,但能够被任意一个了解XML的人使用node

  四、它只能部署在IIS上python

 

  WCFjquery

  一、这个也是基于SOAP的,数据格式是XMLandroid

  二、这个是Web Service(ASMX)的进化版,能够支持各类各样的协议,像TCP,HTTP,HTTPS,Named Pipes, MSMQ.ios

  三、WCF的主要问题是,它配置起来特别的繁琐

  四、它不是开源的,但能够被任意一个了解XML的人使用

  五、它能够部署应用程序中或者IIS上或者Windows服务中

 

  WCF Rest

  一、想使用WCF Rest service,你必须在WCF中使用webHttpBindings

  二、它分别用[WebGet]和[WebInvoke]属性,实现了HTTP的GET和POST动词

  三、要想使用其余的HTTP动词,你须要在IIS中作一些配置,使.svc文件能够接受这些动词的请求

  四、使用WebGet经过参数传输数据,也须要配置。并且必须指定UriTemplate

  五、它支持XML、JSON以及ATOM这些数据格式

 

  Web API

  一、这是一个简单的构建HTTP服务的新框架

  二、在.net平台上Web API 是一个开源的、理想的、构建REST-ful 服务的技术

  三、不像WCF REST Service.它可使用HTTP的所有特色(好比URIs、request/response头,缓存,版本控制,多种内容格式)

  四、它也支持MVC的特征,像路由、控制器、action、filter、模型绑定、控制反转(IOC)或依赖注入(DI),单元测试。这些可使程序更简单、更健壮

  五、它能够部署在应用程序和IIS上

  六、这是一个轻量级的框架,而且对限制带宽的设备,好比智能手机等支持的很好

  七、Response能够被Web API的MediaTypeFormatter转换成Json、XML 或者任何你想转换的格式。

  

  WCF和WEB API我该选择哪一个?

  一、当你想建立一个支持消息、消息队列、双工通讯的服务时,你应该选择WCF

  二、当你想建立一个服务,能够用更快速的传输通道时,像TCP、Named Pipes或者甚至是UDP(在WCF4.5中),在其余传输通道不可用的时候也能够支持HTTP。

  三、当你想建立一个基于HTTP的面向资源的服务而且可使用HTTP的所有特征时(好比URIs、request/response头,缓存,版本控制,多种内容格式),你应该选择Web API

  四、当你想让你的服务用于浏览器、手机、iPhone和平板电脑时,你应该选择Web API

  

  原文:http://www.dotnet-tricks.com/Tutorial/webapi/JI2X050413-Difference-between-WCF-and-Web-API-and-WCF-REST-and-Web-Service.html

 

Web API = Web Service - 服务定义,换言之 Web API + 服务定义 = Web Service。

少了服务定义会怎样?

  1. 没法发现服务,从而也没法知晓服务的变动和删除。但,这样又如何?服务发现原本就是UDDI而非WSDL作的事情。
  2. 没法得到数据类型的定义。Web API在这方面使用XML或者json直接传输数据而无须预先定义,这两个都是弱类型的语言:
    • 好处,再复杂的类型(只要不是循环引用)都轻松的搞定
    • 很差不坏,基础类型都有,通用性十足(WSDL也有,并且只须要作一次)
    • 坏处,没有动态语言功底的环境,每次都须要解析比较吃力(WS有了WSDL,这种事情只须要作一次)
  3. 没法得到消息结构的定义。正如2中描述的那样,若是描述一个数据那么复杂,又何须为了它创建一个公开接口定义?相应的,若是描述很容易,也没有必要去事先定义。
  4. 没法定义多个操做。。。对于调用API的人来讲没有任何做用。
  5. 没法定义多个站口/通信协议。这也是个赘饰。

少了服务定义又会怎样?

  1. 仍然须要一个服务的源来暴露服务。做为一个服务提供商,这是理所固然要作的事情。
  2. 数据类型的定义。只要不出现循环引用,一切都简单到不能更简单。
  3. 服务接口的定义。也许咱们没法提供在线的代码框架生成,但咱们能够提供手册+现成的接口调用代码。

固然具体到WS的表明SOAP与WebAPI的表明json。PK不会发生在Soap->Http Header VS Http Header(结果同样),就只有Soap Body->Http Body->XML vs Http Body->json了。这二者没什么可说的,json基本上是完胜。

因此,一个一般的服务提供商,json形式的Web API加上统一的源管理,赛过soap+http的Web Service。不要扯什么安全性,你们都是http基础;若是认为json可以跨域就叫“不安全”,那他应该弄明白怎么样才能跨域。

 

前端数据可视化

 

原文

在大数据时代,不少时候咱们须要在网页中显示数据统计报表,从而能很直观地了解数据的走向,开发人员不少时候须要使用图表来表现一些数据。随着Web技术的发展,从传统只能依靠于flash、IE的vml,各个浏览器尚不统一的svg,到现在规范统一的canvas、svg为表明的html5技术,表现点、线、面要素的技术已经愈来愈规范成熟。我把前端数据可视化分为了五种:
1.图表
2.图谱
3.地图
4.关系图
5.立体图
我将按照顺序介绍62款前端可视化插件,下面就分享下34款图表插件:
1.amcharts
url: http://www.amcharts.com/
browser:IE6+、chrome、safari、firefox、opear
resume:amCharts是一种先进的图表库,将适合任何数据可视化的须要。图表解决方案包括柱、栏、线、区域,一步,一步没有冒口,平滑线,烛台,OHLC,馅饼/甜甜圈,雷达/极地,XY /分散/泡沫,子弹,漏斗/金字塔图以及指标。

2.awesomechartjs
url:http://cyberpython.github.io/AwesomeChartJS/
github:https://github.com/cyberpython/AwesomeChartJS
browser:现代浏览器
resume:AwesomeChartJS是一个简单的Javascript库,可用于建立图表基于HTML 5画布元素。

3.axiis
url:http://www.axiis.org/
browser:官方未说明
resume:Axiis框架是一个开源的数据可视化为初学者和专家开发人员设计的。

4.bonsaijs
url:http://bonsaijs.org/
github:https://github.com/uxebu/bonsai
browser:IE9+、chrome20+、safari5+、firefox18+、opear12+
resume:用于建立图形和动画的js库

5.canvasjs
url:http://canvasjs.com
browser:官方未说明
resume:一个使用HTML五、JavaScript建立图表在画布上,图表包括几个好看的主题和10倍的速度比传统的基于Flash / SVG库——致使轻量级的,美丽的和响应指示板。收费

6.canvasxpress
url:http://canvasxpress.org/
browser:Firefox 1.5+, Opera 9+, Safari 3.x+, Chrome 1.0+, IE 6+
resume:CanvasXpress是一个独立的Javascript编写的图形库,支持全部主流浏览器中计算机和移动设备。

7.chartist
url:http://gionkunz.github.io/chartist-js/
github:https://github.com/gionkunz/chartist-js
browser:Firefox, Chrome, Safari, Opera, IE9+
resume:绘制多种图形的库

8.chartjs
url:http://www.chartjs.org/
github:https://github.com/nnnick/Chart.js
browser:IE9+、chrome、safari、firefox、opear、部分支持IE7/8
resume:chartjs是一个能够绘制多种图表的库,使用了html5的canvas技术

9.chartkick
url:http://ankane.github.io/chartkick/
github:https://github.com/ankane/chartkick
browser:IE6+、chrome、safari、firefox、opear
resume:chartkick是一个依赖于ruby的绘制图表的js库,在Python中也可使用

10.DataWrapper
url:https://datawrapper.de/
github:https://github.com/datawrapper/datawrapper
browser:支持大部分浏览器
resume:Datawrapper彻底免费,开源。您可使用他们的免费主机服务,或者安装在您本身的服务器上。Datawrapper用PHP编写,很是易于安装、修改和拓展。能够绘制。可是DataWrapper是生成图表后嵌入到站点的。

11.dataset
url:http://misoproject.com/dataset/
github:https://github.com/misoproject/dataset
browser:官方未说明
resume:dataset是一个JavaScript客户端数据转换和管理库。数据集管理客户端数据简单处理加载、解析、排序、查询和操纵来自各类数据源的数据。

12.dc
url:http://dc-js.github.io/dc.js/
github:https://github.com/dc-js/dc.js
browser:官方未说明
resume:专门为探索大型、多维数据集而进行优化的库

13.dygraphs
url:http://dygraphs.com/
browser:IE8+、chrome、safari、firefox、opear
resume:dygraphs是一种快速、灵活的开源JavaScript库图表。

14.echart
url:http://echarts.baidu.com/index.html
github:https://github.com/ecomfe/echarts
browser:IE9+、chrome、safari、firefox、opear
resume:基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。创新的拖拽重计算、数据视图、值域漫游等特性大大加强了用户体验,赋予了用户对数据进行挖掘、整合的能力。

15.flotr2
url:http://www.humblesoftware.com/flotr2/
github:https://github.com/HumbleSoftware/Flotr2
browser:FF, Chrome, IE6+, Android, iOS
resume:Flotr2是HTML5画图表和图形库。能够绘制线图、条图、蜡状图、饼图、气泡图

16.Flot
url:http://www.flotcharts.org/
browser:Internet Explorer 6+, Chrome, Firefox 2+, Safari 3+ and Opera 9.5+
resume:一个基于jQuery的绘图库,能够绘制折线、散点、条形、饼状图

17.fusioncharts
url:http://www.fusioncharts.com/
browser:IE6+、chrome、safari、firefox、opear
resume:一个专门用来绘制图表的库,能够绘制90多种图表,可是收费

18.graphael
url:http://g.raphaeljs.com/
browser:Firefox 3.0+, Safari 3.0+, Opera 9.5+ and Internet Explorer 6.0+

resume:能够绘制各类图表的插件,并且很是简单灵活

19.highchart
url:http://www.highcharts.com/
github:https://github.com/highslide-software/highcharts.com/
browser:支持各类设备,ie6+
resume:在高版本浏览器中使用SVG,而在旧版本IE(包括IE6及更新版本)中使用后备的VML。有本身的团队负责,可是只对非商业用途免费,能够绘制 line, spline, area, areaspline, column, bar, pie, scatter, angular gauges, arearange, areasplinerange, columnrange, bubble, box plot, error bars, funnel, waterfall and polar chart types

20.humble Finance
url:http://www.humblesoftware.com/finance/index
browser:FireFox, Safari, Chromium, or IE6+
resume:HumbleFinance是一个HTML5数据可视化工具相似于Flash工具,彻底是用JavaScript编写的工具使用原型和Flotr库。

21.ichartjs
url:http://www.ichartjs.com/
github:https://github.com/wanghetommy/ichartjs
browser:IE9+、chrome、safari、firefox、opear
resume:ichartjs 是一款基于HTML5的图形库。使用纯javascript语言, 利用HTML5的canvas标签绘制各式图形。 ichartjs致力于为您的应用提供简单、直观、可交互的体验级图表组件。ichartjs目前支持饼图、环形图、折线图、面积图、柱形图、条形图。

22.icharts
url:http://www.icharts.net/
browser:官方未说明
resume:iCharts免费版本提供了一些基本的交互式图表样式,若是更使用高级的样式,则须要购买高级版本。

23.JavaScript InfoVis Toolkit
url:http://philogb.github.io/jit/
github:https://github.com/philogb/jit
browser:官方未给出具体版本
resume:JavaScript InfoVis Toolkit能够动态绘制各类图形,提供了一些预设的样式可用于展现不一样的数据

24.jqplot
url:http://www.jqplot.com/
browser:IE 7+, Firefox, Safari, and Opera
resume:基于jQuery的绘图插件,能够绘制折线、条形、散点、饼状图

25.jscharts
url:http://www.jscharts.com/
browser:Firefox 1.5 +,Chrome 10 +,Internet Explorer 8 +,Safari 3.1 +,Opera 9 +
resume:jscharts是一个基于JavaScript的图表生成器,须要不多或根本没有编码。jscharts绘制图表是一个简单和容易的任务,由于您只须要使用客户端脚本(即由web浏览器)。不须要额外的插件或服务器模块。就包括咱们的脚本,准备你的图表数据XML、JSON或JavaScript数组和你的表已经准备好了!容许您建立图柱状图,饼图或简单的线条图。收费可是有免费版本。

26.kendo-ui
url:http://www.telerik.com/kendo-ui
github:https://github.com/telerik/kendo-ui-core
browser:现代浏览器
resume:http://www.cnblogs.com/xiyangbaixue/p/3951297.html

27.nvd3
url:http://nvd3.org/
github:https://github.com/novus/nvd3
browser:Chrome,Firefox, Opera, Safari and Internet Explorer 10
resume:d3图表库

28.pizza-pie-charts
url:http://zurb.com/playground/pizza-pie-charts
github:https://github.com/zurb/pizza
browser:官方未说明
resume:主要用来生成饼状图的库

29.protovis
url:http://mbostock.github.io/protovis/
github:https://github.com/mbostock/protovis
browser:现代浏览器
resume:Protovis组成自定义视图的数据用简单的标志如酒吧和点。与低级图形库,迅速成为可视化乏味,Protovis定义是经过编码数据的动态属性,容许继承,尺度和layoutsto简化施工。

30.Peity
url:http://benpickles.github.io/peity/
browser:Chrome, Firefox, IE9+, Opera, Safari
resume:能够绘制多种图形,可是都是很小的图形,与jQuery Sparklines类似

31.rgraph
url:http://www.rgraph.net/
browser:现代浏览器
resume:RGraph是一个基于HTML5的开放web图表和图表库。RGraph建立这些图表在web浏览器使用JavaScript,这意味着更快的页面和web服务器负载,致使较小的页面大小和更快的网站。

32.webfx
url:http://webfx.eae.net/
browser:Firefox 1.5, Opera 9 and Internet Explorer 6
resume:支持多种图表的库

33.xcharts
url:http://tenxer.github.io/xcharts/
github:https://github.com/tenXer/xcharts/
browser:现代浏览器
resume:xCharts美丽是一个JavaScript库,用于构建和自定义数据驱动的web使用D3.js图表可视化。使用HTML、CSS和SVG,xCharts被设计成动态、流体、集成和定制。

34.zingchart
url:http://www.zingchart.com/
browser:官方未声明
resume:ZingChart创造惊人的可视化提供了灵活性和资源。提供超过100个图表类型,独特的特性,如缩放和交互式。


小结:

每款插件各有千秋,根据项目需求挑选不一样插件。其中比较普遍使用的如echart(百度产品)、highchart等,下面我将分享图谱插件。
9款图谱插件:
1.crossfilter
url:http://square.github.io/crossfilter/
github:https://github.com/square/crossfilter
browser:官方未说明
resume:一个能够操做大型、多元数据集的库,帮助数据分析。

2.d3js
url:http://d3js.org/
github:https://github.com/mbostock/d3
browser:Firefox, Chrome, Safari, Opera, IE9+
resume:D3.js是一个JavaScript库,基于数据操做文档。D3能够帮助你把数据使用HTML、SVG和CSS。D3强调web标准给你完整的现代浏览器的功能没有把本身和一个专有的框架,结合强大的可视化组件和DOM操做的数据驱动的方法。

3.envisionjs
url:http://www.humblesoftware.com/envision/index
github:https://github.com/HumbleSoftware/envisionjs
browser:IE6+、chrome、safari、firefox、opear
resume:envisionjs是一个库来建立快速、动态和交互式可视化的图表

4.jsxgraph
url:http://jsxgraph.uni-bayreuth.de/wp/
github:https://github.com/jsxgraph/jsxgraph
browser:现代浏览器
resume:JSXGraph交互式几何是一个跨浏览器的库,函数绘图,图表和数据可视化在web浏览器中。它彻底实如今JavaScript中,不依赖于任何其余库,并使用SVG VML或画布上。

5.paperjs
url:http://paperjs.org/
github:https://github.com/paperjs/paper.js
browser:IE9+,chrome,firefox
resume:paperjs是一款不可多得的js插件,能够绘制各类动态图形效果

6.processingjs
url:http://processingjs.org/
github:https://github.com/processing-js/processing-js/
browser:现代浏览器
resume:processingjs是用Java编写的,因此图形在网页上显示要靠Java程序,使用canvas技术

7.Raphaël
url:http://raphaeljs.com/
github:
browser:Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ and Internet Explorer 6.0+.
resume:Raphaël是一款绘制矢量图的插件,支持低版本的浏览器

8.sparklines
url:http://omnipotent.net/jquery.sparkline/#s-about
github:
browser:Firefox 2+, Safari 3+, Opera 9, Chrome and Internet Explorer 6+,ios和andriod设备
resume:使用内嵌在HTML中的数据或经过javascript直接生成微线图(小内联图表),最主要的特色是能够生成波形图。

9.tangle
url:http://worrydream.com/Tangle/
github:
browser:
resume:Tangle是一个JavaScript库,用于建立活性文档。读者能够交互式地探索可能性,玩参数,并当即看到文档更新。Tangle是超级简单,容易学习。


小结:后面将分享6款地图插件。

6款地图插件:
1.Kartograph
url:http://kartograph.org/
github:https://github.com/kartograph/kartograph.py
browser:Internet Explorer 7+,chrome,Firefox
resume:Gregor Aisch开发的一个基于JavaScript和Python的很是炫的、彻底使用矢量的库。

2.leafletjs
url:http://leafletjs.com/
github:https://github.com/Leaflet/Leaflet
browser:Chrome,Firefox,Safari 5+,Opera 12+,IE 7–11
resume:leafletjs是一个开源的支持移动端的地图插件,js文件仅仅有33kb,

3.Modest Maps
url:http://modestmaps.com/
github:https://github.com/modestmaps/modestmaps-js
browser:Firefox, Chrome, Opera, iOS, Android, and Internet Explorer 7-9.
resume:Modest Maps支持各类设备,也有不少版本。虽然是一款老的地图插件,可是很是小、可扩展并且免费

4.polymaps
url:http://polymaps.org/
github:https://github.com/simplegeo/polymaps
browser:现代浏览器
resume:Polymaps依赖于SVG,所以在较新的浏览器中表现很好。

5.imagemapster
url:http://www.outsharked.com/imagemapster/
browser:Firefox, Chrome, Safari, Opera, IE6+
resume:ImageMapster是一个jQuery插件,它使你的HTML图片像Flash同样炫

6.datavlab
url:http://datavlab.org/
github:https://github.com/TBEDP/datavjs
browser:IE6+、chrome、safari、firefox、opear
resume:datav.js是为了下降平常对于可视化方法使用的成本,用数据可视化的方法帮助到更多的人。


如今来分享9款关系图插件:
1.arborjs
url:http://arborjs.org/halfviz/#/a-new-hope
github:https://github.com/samizdatco/arbor
browser:IE6+,chrome,firefox
resume:基于jQuery的图谱可视化库,对于高版本的浏览器这个库使用了HTML的canvas元素


2.cubism
url:http://square.github.io/cubism/
github:https://github.com/square/cubism
browser:官方未说明
resume:时间序列数据可视化的D3插件


3.gantti
url:http://bastianallgeier.com/gantti/
github:https://github.com/bastianallgeier/gantti
browser:IE7+、chrome、safari、firefox、opear
resume:是一款PHP的前端数据展现插件


4.getspringy
url:http://getspringy.com/
github:https://github.com/dhotson/springy/
browser:官方未说明
resume:Springy是一个使用JavaScirpt实现的有向图布局算法,使用了真实世界中的一些物理原理,你能够随意拖动图表中的元素。


5.graphdracula
url:http://www.graphdracula.net/
github:https://github.com/strathausen/dracula
browser:官方未说明
resume:graphdracula是一组工具来显示和布局互动图表,以及各类相关算法。


6.sigamajs
url:http://sigmajs.org/
github:https://github.com/jacomyal/sigma.js
browser:IE9+,chrome,firefox
resume:一个很是轻量级的图谱可视化库。Sigma.js很漂亮,速度也快。


7.smoothiecharts
url:http://smoothiecharts.org/
github:https://github.com/joewalnes/smoothie/
browser:IE7+、chrome、safari、firefox、opear
resume:smoothiecharts是一个很是小的图表库为实时流媒体数据而设计的


8.timeplot
url:http://www.simile-widgets.org/timeplot/
github:
browser:官方未说明
resume:Timeplot是基于dhtml AJAXy部件绘图时间序列和覆盖基于时间的事件


9.visjs
url:http://visjs.org/
github:https://github.com/almende/vis/
browser:Chrome, Firefox, Opera, Safari, IE9+
resume:Vis.js是一个动态的、基于浏览器可视化库。库被设计成易于使用,处理大量的动态数据,使操做和交互的数据。时间表,包括组件库数据集网络、Graph2d,Graph3d。

 

C# asp.net PhoneGap html5

 

好久没写博客,今天本身写一篇吧。来谈一谈c# PhoneGap,html5 与asp.net。能搜到这篇博客就说明你是一位.net开发者,即将或者正在从事移动开发。

你们可能都有疑,我是一名.net开发者,能用.net作苹果,安卓,wp平台的应用吗?

如你们所知,phoneGap是用来开发跨平台移动应用的,并且phoneGap能调用移动设置的硬件接口,实现一些普通html在移动设备上没法实现的功能,好比拍照,录音,录制视频等。

要作html5页面为单元的应用,必定少不了与服务器进行数据交互。想到的与服务器交互有两种方案:

一种是使用默认的本地HTML方式(固然也能够把HTML放在服务器端,可是这样还不如使用第二种方案),一种是全程使用服务器脚本,好比JSP页面,固然这些页面确定是放在服务器端,客户端只须要在初始化的时候引用这个页面便可,Android客户端,代码以下:

public class MyActivity extends DroidGap { 
  @Override 
  public void onCreate(Bundle savedInstanceState) { 
  super.onCreate(savedInstanceState); 
  //super.loadUrl("file:///android_asset/login.html"); 
  super.loadUrl("http://220.166.32.204:8080/test/login.jsp"); 
  super.setIntegerProperty("loadUrlTimeoutValue", 15000); 
  } 
}

(注释掉的代码为PhoneGap默认的本地HTML方案)

另外一种是http请求即为服务器脚本解决方案

各有各的好处,本地HTML方案能够把代码和服务器端的代码隔离开,维护只须要维护客户端,无需修改服务器端,而且由于是本地读取,能够在无网络状况下使用应用程序(能够把资源保存在本地sqlLite数据库中);服务器脚本解决方案好处是代码简单,和开发普通web页面几乎无差异,甚至能够直接使用以前针对PC写好的web页面,JS识别设备动态更换一下CSS样式便可。

我使用的是本地HTML5解决方案,在数据交互上,使用的是ajax,使用jQuery的ajax方法,JS代码以下:

function pullNoticeList(){ 
 $.ajax({ 
  url:addr+"notice_list.action", 
  type:"POST", 
  dataType:"jsonp", 
  data:{"start":"0","limit":"8","sort":"effectiveDate","dir":"DESC"}, 
  beforeSend: function(){ 
  $("#content").html("<p>数据拉取中,请稍后...</p>").trigger("create"); 
  }, 
  success:function(response){ 
  var startHtml = '<ul id="listview" data-role="listview" data-theme="c">'; 
  var endHtml = "<li id='more'><p >; 
  $.each(response.noticeList,function(index,item){ 
  startHtml+="<li><a href='javascript:showDetail("+item.noticeId+");'><p>"+item.noticeTitle+"<span class='ui-li-aside'>"+item.effectiveDate+"</span></p></a></li>"; 
  }); 
  $("#content").html(startHtml+endHtml).trigger("create"); 
  }, 
  error:function(){ 
  showReConnConfirm(); 
  } 
 }); 
}

必须使用jsonp格式,否则无返回值,其次,注意返回的json格式,必定要符合jQuery的json格式,能够经过firedug查看调试json,服务器端使用的struts,可使用struts的json插件,能够很智能把对象封装成json格式。

若是你想用phoneGap作一款跨平台的应用,首先要肯定会不会调用移动设备的硬件接口,若是不须要调用,那么第一张方案是最佳的选择,维护开发与日常web几乎没区别,最佳的适合咱们.net开发者,由于咱们能够直接选择用aspx页面来开发。

可是若是项目有要求要调用移动设备的硬件接口,那么必需要选择用第二种方案了。

这时,与服务器交互,html5端用ajax,后台能够用aspx,或者ashx,或者asmx,即分别对应普通aspx页面或者通常处理程序ashx页面,或者直接写webservice服务都行。但愿能帮到你们。

移动开发html5,必将是主流,一块儿迎接吧!

 

C# Where

 

判断一个字符串中是否包含字符串数组里的字符,恶意字符限制提交,通常人,包括最初的我,会这样写

 

复制代码
public bool ValidateStr(string[] parms)
    {

        bool result = false;

        //要验证的字符列表

        string[] validateParms = { "'", "\"", "%" };
        for (int i = 0; i < parms.Length; i++)
        {
            for (int j = 0; j < validateParms.Length; j++)
            {
                if (parms[i].IndexOf(validateParms[j]) != -1)
                {
                    result = true;
                }
            }
        }
        return result;
    }
复制代码

 

 

 

 

可是你毫不以为,看着代码很乱呢

其实咱们能够这样写

 

复制代码
  public bool ValidateStr(string[] parms)
    {
        bool result = false;
        //要验证的字符列表
        string[] validateParms = { "'", "\"", "%" };
        for (int i = 0; i < parms.Length; i++)
        {
            if (validateParms.Where(p => p.IndexOf(parms[i]) > 0).Count() > 0)
            {
                result = true;

            }
        }
        return result;
    }
复制代码

 

代码瞬间清晰

 

网站分布式开发简介

 

1          概述 分布式应用程序就是指应用程序分布在不一样计算机上,经过网络来共同完成一项任务,一般为服务器/客户端模式。更广义上理解“分布”,不仅是应用程序,还包括数据库等,分布在不一样计算机,完成同一个任务。之因此要把一个应用程序分布在不一样的计算机上,主要有两个目的:

1)        分散服务器的压力

大型系统中,模块众多,并发量大,仅用一个服务器承载每每会发生压力过大而致使系统瘫痪的状况。能够在横向和纵向两方面来进行拆分,把这些模块部署到不一样的服务器上。这样整个系统的压力就分布到了不一样的服务器上。

l        横向:按功能划分。

l        纵向:N层架构,其中的一些层分布到不一样的服务器上(分层的概念会在后文进行介绍)。

2)        提供服务,功能重用

使用服务进行功能重用比使用组件进行代码重用更进一层。举例来讲,若是在一个系统中的三个模块都须要用到报表功能,一种方法是把报表功能作成一个单独的组件,而后让三个模块都引用这个组件,计算操做由三个模块各自进行;另外一种方法是把报表功能作成单独的服务,让这三个模块直接使用这个服务来获取数据,全部的计算操做都在一处进行,很明显后者的方案会比前者好得多。

服务不只能对内提供还能对外提供,若是其余合做伙伴须要使用咱们的报表服务,咱们又不想直接把全部的信息都公开给它们。在这种状况下组件方式就不是很合理了,经过公开服务并对服务的使用方作受权和验证,那么咱们既能保证合做伙伴能获得他们须要的数据,又能保证核心的数据不公开。

2          架构 分布式的系统架构,主要从如下几个方面来考虑:分层、面向服务以及分布式数据库。

2.1        分层模型 通常地,咱们将应用程序功能分为三个方面,对应3层架构模式。它们是数据层、中间层(业务逻辑层)和表示层,以下图所示。

1)        数据层:存储数据以及从数据库中得到较为原始的数据。

2)        业务逻辑层:介于数据层和表示层之间,负责处理来自数据存储或发送给数据存储的数据,把数据转换成符合商务规则的有意义的信息。

3)        表示层:从业务逻辑层得到信息并显示给用户,负责与用户的交互。

 

 三层架构模式,将业务逻辑和数据存储分离,并分别用独立的服务器来承载,有利于分散系统的压力。其优势具体以下所示:

1)        由于客户机不包含业务逻辑,因此它们变得更加简洁。这就使部署和维护工做更加容易,由于更新业务逻辑只须要对应用服务器进行操做。

2)        客户机与数据库细节相分离。应用服务器可以与几个不一样的数据源(分布在不一样的数据库服务器上,下文中会对分布式数据库系统进行介绍)协同工做,而且只对客户机提供单一的访问点。

3)        若是设计正确,业务逻辑就可以被分布到几个负载均衡的应用服务器上。若是用户需求增长,则能够添加更多的服务器以知足要求。同时,能够将业务逻辑发布为服务,供客户端应用程序或者其它服务调用,构建成面向服务的系统架构。

2.2        面向服务 一家汽车租赁公司决定建立一个新的应用程序,用于汽车预约。该租车预约应用程序的建立者知道,应用程序所实现的业务逻辑必须可以让公司内外运行的其它软件访问。所以,他们决定以面向服务的方式来建立此应用程序,并经过定义完善的一组服务,将此应用程序的逻辑公开给其余软件。

为了实现这些服务并使之与其余软件进行通讯,这一新应用程序将使用 .Net Framework的分布式计算技术,主要有:

1)        ASP.NET Web 服务

它使用Soap交互信息,是跨平台,跨语言的,目前大多数平台都支持基本的 Web 服务,因此在 WCF 发布以前,这是实现跨供应商互操做性的最直接的方法。通常用在B/S结构的系统中,须要IIS进行启动。

下图演示了客户机消费Web服务的情形。客户机能够是一个Web应用程序、另外一个Web服务等。       Web服务的消费者调用名为Method()的Web服务上的方法。实际调用向下层传播,做为Soap消息经过网络,并向上层传播到Web服务。Web服务执行并响应(若是有的话)。实现Web服务与客户机之间的双向通知或者发布/订阅功能是可能的,可是必须手工完成。客户机能够实现本身的Web服务并在对服务器的调用中传递该Web服务的引用。服务器能够保存引用,而后回调给客户机。

2)        .NET Remoting

专门为紧密耦合的 .NET 到 .NET 通讯而设计,它为本地网络中的应用程序提供了无缝而直接的开发体验。通常用在C/S结构的系统中,须要经过一个WinForm或是Windows服务进行启动。

3)        Microsoft 消息队列 (MSMQ)

提供持久稳定的消息传送,这一般是间歇式链接的应用程序的最佳解决方案。这些应用程序对数据传送、工做量分离以及应用程序生存期均要求有保证。

4)        WCF服务

WCF是使用托管代码创建和运行面向服务的应用程序的统一架构,是开发者能够创建一个跨平台(可与在J2EE 服务器构建、非 Windows 系统上运行的应用程序通讯)、安全、可信赖、事务性的解决方案,能与目前已有的分布式系统兼容。它是微软分布式应用程序开发的集大成者,整合了.Net 平台下全部和分布式系统有关的技术。

 

以通讯范围而言,WCF能够跨进程(同一机器上不一样的应用程序之间的通讯)、跨子网、企业网(局域网内不一样的机器之间的通讯)甚至于Internet(互联网中不一样的机器之间的通讯)。从宿主程序而言,能够是ASP.NET,EXE , WPF(Windows Presentation Foundation), Windows Forms, NT Service, COM+.

2.3        分布式数据库系统 分布式数据库系统由分布于多个计算机结点上的若干个数据库系统组成,它提供有效的存取手段来操纵这些结点上的子数据库。分布式数据库在使用上可视为一个完整的数据库,而实际上它是分布在地理分散的各个结点上。分布式数据库系统适合于单位分散的部门,容许各个部门将其经常使用的数据存储在本地,实施就地存放本地使用,从而提升响应速度,下降通讯费用。它有如下优势:

1)        解决组织机构分散而数据须要相互联系的问题。好比银行系统,总行与各分行处于不一样的城市或城市中的各个地区,在业务上它们须要处理各自的数据,也须要彼此之间的交换和处理,这就须要分布式的系统。

2)        均衡负载。负载在各处理机间分担,可避免临界瓶颈。

3)        可靠性高。数据分布在不一样场地,且存有多个副本,即便个别场地发生故障,不致引发整个系统的瘫痪。

4)        可扩充性好。当须要增长新的相对自主的组织单位时,可在对当前机构影响最小的状况下进行扩充。

5)        提升系统性能系统。能够根据距离选择离用户最近的数据副本进行操做,减小通讯代价,改善整个系统的性能。

分布式数据库系统虽然有诸多优势,但它同时也带来了许多新问题。如:数据一致性问题、更新同步以及查询分解等的复杂性、数据远程传递的实现、通讯开销的下降等,这使得分布式数据库系统的开发变得较为复杂。

3          总结 分布式应用程序的开发,要对应用程序进行分层,各层之间相互独立,经过服务或接口来进行调用,不只便于开发的管理,也有利于集成其余平台上的应用程序,实现了功能模块的复用、重用,提升了应用程序的可扩展性。在业务数据量多的状况下,还要考虑构建分布式的数据库系统,这能够经过DBMS自动管理的数据订阅、分发技术实现数据库的数据同步,以达到数据共享的目的;也能够与一些分布式的计算技术结合起来,好比说.NET Remoting技术来解决各数据库之间的通讯问题,等等。

针对大型的网站应用,分布式部署策略能够从如下几个方面考虑:

1)        代理服务器实现请求的分离 。

2)        缓存的分布式部署,提升系统性能。

3)        拆分网站的对外功能,例如不一样域名前、后缀,URL 重写。

4)        面向服务,每一个服务分布到一台服务器上 。

5)        数据库的分布式集群部署。

6)        设立专门的应用服务器。好比发送邮件通知的服务。

 

EntityFramework Core依赖注入上下文方式不一样形成内存泄漏了解一下?

 

前言

这个问题从未碰见过,是一位前辈问我EF Core内存泄漏问题时我才去深刻探讨这个问题,刚开始我比较惊讶,竟然还有这种问题,而后就有了本文,直接拿前辈的示例代码并稍加修改为就了此文,但愿对在自学EF Core过程当中的童鞋能有些许帮助。

EntityFramework Core内存泄漏回顾

接下来我将用简单示例代码来还原整个形成EntityFramework Core内存泄漏的过程,同时在这个过程当中您也可思考一下其中的缘由和最终的结果是否一致。

    public class TestA
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }
复制代码
    public class EFCoreDbContext : DbContext
    {
        public EFCoreDbContext(DbContextOptions options)
            : base(options)
        {
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;");
            base.OnConfiguring(optionsBuilder);
        }

        public DbSet<TestA> TestA { get; set; }
    }
复制代码
复制代码
    public class TestUserCase
    {
        public void InvokeMethod(IServiceProvider serviceProvider)
        {
            EFCoreDbContext _context = null;

            if (_context == null)
            {
                _context = serviceProvider.GetRequiredService<EFCoreDbContext>();
            }

            for (var i = 0; i < 10; i++)
            {
                var testA = _context.TestA.FirstOrDefault();
                Console.WriteLine(i);
            }
        }
    }
复制代码

如上是整个示例代码,重头戏来了,接下来咱们在控制台中来经过依赖注入上下文,并获取注入容器中的上下文并调用上述TestUserCase类中的方法,以下:

复制代码
            var services = new ServiceCollection();

            services.AddDbContext<EFCoreDbContext>(options => options.UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;"));

            var serviceProvider = services.BuildServiceProvider();

            for (int i = 0; i < 1000; i++)
            {
                var test = new TestUserCase();
                test.InvokeMethod(serviceProvider);
            }
复制代码

 

经过上述测试内存基本在15兆左右,固然根据机器配置不一样最终获得的结果有所差别,可是内存基本没有什么大的波动,接下来咱们来改造上述代码。上述咱们将serviceProvider经过方法传递到TestUserCase中的InvokeMethod方法中,为了简便咱们将获取到的serviceProvider改形成静态的,以下:

复制代码
    public static class ServiceLocator
    {
        private static IServiceCollection _services;

        public static IServiceProvider Instance
        {
            get
            {
                if (_services == null)
                    return null;
                else
                    return _services.BuildServiceProvider();
            }
        }

        public static void Init(IServiceCollection services)
        {
            _services = services;
        }
    }
复制代码
复制代码
    public class TestUserCase
    {
        public void InvokeMethod()
        {
            IServiceScope _serviceScope = null;
            EFCoreDbContext _context = null;
            if (_context == null)
            {
                _serviceScope = ServiceLocator.Instance.GetRequiredService<IServiceScopeFactory>().CreateScope();
                _context = _serviceScope.ServiceProvider.GetRequiredService<EFCoreDbContext>();
            }

            for (var i = 0; i < 10; i++)
            {
                var testA = _context.TestA.FirstOrDefault();
                Console.WriteLine(i);
            }
        }
    }
复制代码
复制代码
            var services = new ServiceCollection();

            services.AddDbContext<EFCoreDbContext>(options => options.UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;"));

            ServiceLocator.Init(services);

            for (int i = 0; i < 1000; i++)
            {
                var test = new TestUserCase();
                test.InvokeMethod();
            }
复制代码

如上咱们经过ServiceLocator类来构建serviceProvider,并将返回serviceProvider赋值给静态变量,而后在咱们调用的方法中直接获取容器中的上下文,这样就免去了传递的麻烦。

通过咱们上述改造后最终运行内存达到了比较可怕的三百多兆,看来上下文压根就没进行GC,那我是否是改形成以下就能够了呢?

复制代码
            var scopeFactory = ServiceLocator.Instance.GetRequiredService<IServiceScopeFactory>();

            using (var scope = scopeFactory.CreateScope())
            using (var context = scope.ServiceProvider.GetRequiredService<EFCoreDbContext>())
            {
                for (var i = 0; i < 10; i++)
                {
                    var testA = context.TestA.FirstOrDefault();
                    Console.WriteLine(i);
                }
            }
复制代码

 

原觉得是上下文没有及时获得释放而致使内存激增,可是看到上述结果依然同样没有任何改变,问题是否是到此就结束了呢?下面咱们改变注入上下文的方式看看,以下:

复制代码
            var services = new ServiceCollection();

            var options = new DbContextOptionsBuilder<EFCoreDbContext>()
              .UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;")
              .Options;
            services.AddScoped(s => new EFCoreDbContext(options));
复制代码

当咱们改变了注入上下文方式后发现此时不会形成内存泄漏,也就是说上下文获得了GC,不管是我是不是手动释放上下文即经过Using包括或者不包括都不会出现内存泄漏问题。经过注入方式不一样获得的结果大相径庭,可是在咱们的理解中经过AddDbContext注入上下文中的第二个参数是默认为Scope,那和咱们经过AddScoped注入上下文应该是同样对不对,那为什么结果又不一样呢?岂不是冲突了吗?在Web不会出现这样的问题,未深刻研究,我猜想其缘由可能以下:

经过AddDbContext注入上下文只适用于Web应用程序即只对Web应用程序有效而对控制台程序可能无效,同时在Web应用程序中AddDbContext注入上下文和AddScoped注入上下文一致,而对于控制台程序存在不一致问题。一言以蔽之,在Web和Console中经过AddDbContext注入上下文可能存在处理机制不一样。

总结

不知如上浅薄分析是否有漏洞或者说代码有错误的地方,期待看到本文的您能有更深刻的看法可留下您的评论,若是结果是这样不建议在控制台中使用AddDbContext方法注入上下文。

 

SQL Server之深刻理解STUFF

 

前言

最近项目不管查询报表仍是其余数据都在和SQL Server数据库打交道,对于STUFF也有了解,可是发现当下一次再写SQL语句时我还得查看相关具体用法,说到底仍是没有彻底理解其原理,因此本节咱们来谈谈STUFF,Jeff是在项目中哪里不熟悉,哪里不会或者哪里耗时比较多就会去深刻理解和巩固即便是很基础的知识,直到彻底不用浪费时间去查阅相关资料,这是个人出发点。

深刻理解STUFF

STUFF字符串函数是将字符串插入到另外一个字符串中。它会删除开始位置第一个字符串中的指定长度的字符,而后将第二个字符串插入到开始位置的第一个字符串中,语法以下。

STUFF(<character_expression>,<开始>,<长度>,<character_expression>)
<character_expression>参数是给定的字符串数据,能够是字符或二进制数据的常量,变量或列。<start>参数是一个整数值,指定开始删除和插入的位置,能够是BIGINT类型。若是<开始>或<长度>参数为负数,则返回NULL字符串。若是<start>参数比第一个<character_expression>长,则返回一个NULL字符串。 <length>参数能够是BIGINT类型,它是一个整数,指定要删除的字符数。若是<length>比第一个<character_expression>长,则删除发生到最后一个<character_expression>中的最后一个字符。

复制代码
DECLARE @FullName       VARCHAR(100)
DECLARE @Alias          VARCHAR(20)

SET @FullName = 'Jeffcky Wang'
SET @Alias = ' "Superman" '

SELECT STUFF(@FullName, CHARINDEX(' ', @FullName), 1, @Alias) AS [FullName]
复制代码

 

如上STUFF函数中的第一个参数咱们给定的是@FullName,第二个是开始的位置,咱们经过CHARINDEX函数找出@FullName以空格隔开的的位置返回,最后由@Alias来代替,结果如图所示。

DECLARE @Time VARCHAR(10)
SET @Time = '1030'

SELECT STUFF(@Time, 3, 0, ':') AS [HH:MM]

咱们给定的字符串为@Time即1030,咱们从第3个位置开始,删除长度为0,此时则在3前面插入冒号,结果如上图输出10:30。

DECLARE @CreditCardNumber  VARCHAR(20)
SET @CreditCardNumber = '370200199408103544'

SELECT STUFF(@CreditCardNumber, LEN(@CreditCardNumber) -3, 4,
       'XXXX') AS [Output]

如上咱们将身份证经过STUFF将最后四位用XXXX代替。以上是STUFF最基础的用法。STUFF最多见的用途莫过于结合FOR XML PATH对返回JSON字符串的拼接。首先利用FOR XML PATH则返回XML格式的字符串,咱们将FOR XML PATH添加到查询的末尾,此时容许咱们将查询的结果做为XML元素输出,元素名称包含在PATH参数中。。

SELECT TOP 5 ',' + Name 
              FROM  Production.Product
              FOR XML PATH ('')

,Adjustable Race,All-Purpose Bike Stand,AWC Logo Cap,BB Ball Bearing,Bearing Ball

此时咱们利用STUFF将上述利用FOR XML PATH生成的字符串中的前置逗号去掉,以下:

SELECT Name = STUFF((
            SELECT TOP 5 ',' + NAME
            FROM Production.Product
            FOR XML PATH('')
            ), 1, 1, '')

好比咱们要查询各类产品中的产品列表名称,最后咱们改形成以下:

复制代码
SELECT TOP 5 p2.ProductID, Name = STUFF((
            SELECT ',' + NAME
            FROM Production.Product AS p1
            WHERE p1.ProductID = p2.ProductID
            FOR XML PATH('')
            ), 1, 1, '') FROM Production.Product AS p2
GROUP BY p2.ProductID
复制代码

接下来咱们利用STUFF结合FOR XML PATH来拼接JSON字符串,以下:

复制代码
DECLARE @content VARCHAR(MAX)

SET @content = (SELECT '['+ STUFF((SELECT TOP 5 ',{"ProductName": "' + ProductName + '","Price": "' + CONVERT(VARCHAR, Price) + '","Quantity": "' + CONVERT(VARCHAR, quantity) + '","Inserton": "' + CONVERT(VARCHAR, Inserton, 105) + '"}' FROM ProductList 
 FOR XML PATH('')), 1, 1,''
 ) 
 + ']'[ProductDetail])

PRINT @content
复制代码

结果如上正确输出JSON字符串,接下来咱们将如上拼接换行再试试。

复制代码
DECLARE @content VARCHAR(MAX)

SET @content = ( SELECT '['
                        + STUFF(( SELECT TOP 5
                                            ',{"ProductName": "' + ProductName
                                            + '","Price": "'
                                            + CONVERT(VARCHAR, Price)
                                            + '","Quantity": "'
                                            + CONVERT(VARCHAR, quantity)
                                            + '","Inserton": "'
                                            + CONVERT(VARCHAR, Inserton, 105)
                                            + '"}'
                                  FROM      ProductList
                                FOR
                                  XML PATH('')
                                ), 1, 1, '') + ']' [ProductDetail]
               )

PRINT @content
复制代码

如上是利用SQL Prompt直接格式化换行,结果依然正确输出JSON字符串,咱们再来手动换行试试。

复制代码
DECLARE @content VARCHAR(MAX)

SET @content = (SELECT 
'['+ STUFF((SELECT TOP 5 ',
{"ProductName": "' + ProductName 
+ '","Price": "' + CONVERT(VARCHAR, Price) 
+ '","Quantity": "' + CONVERT(VARCHAR, quantity) 
+ '","Inserton": "' + CONVERT(VARCHAR, Inserton, 105) 
+ '"}' FROM ProductList 
 FOR XML PATH('')), 1, 1,''
 ) 
 + ']'[ProductDetail])

PRINT @content
复制代码

结果输出如上咱们不指望的字符串,主要是由FOR XML PATH形成的,好比咱们利用FOR XML PATH进行以下查询:

SELECT  '    '
FOR     XML PATH('') 

当咱们利用FOR XML  PATH查询数据时,若是字符串中包含空格时会形成出现以如上错误的字符串来填充,因此此时咱们为了消除这种错误格式,咱们将上述继续添加参数。

SELECT  '    '
FOR     XML PATH(''),TYPE 

此时咱们将上述输出JSON字符串不错误的格式修改为以下便可:

复制代码
DECLARE @content VARCHAR(MAX)

SET @content = (SELECT 
'['+ STUFF((SELECT TOP 5 ',
{"ProductName": "' + ProductName 
+ '","Price": "' + CONVERT(VARCHAR, Price) 
+ '","Quantity": "' + CONVERT(VARCHAR, quantity) 
+ '","Inserton": "' + CONVERT(VARCHAR, Inserton, 105) + '"}' FROM ProductList 
 FOR XML PATH('') ,TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,''
 ) 
 + ']'[ProductDetail])

PRINT @content
复制代码

或者咱们对上述输出的错误字符串进行替换,以下:

复制代码
select t.PK, 
    ltrim(rtrim(replace(
    (select ' ' + isnull(ti.Column1, '') + ' ' + isnull(ti.Column2, '')
     from yourTable ti 
     where ti.PK = t.PK
     for xml path (''))
     , '&#x20;', ''))) fruits
from yourTable t
group by t.PK;
复制代码

这里咱们解决了利用STUFF有可能输出JSON字符串带有错误的字符串的问题,在利用STUFF输出JSON字符串时只要有一列数据包含NULL,那么返回的数据则为空,那么咱们在对列数据经过ISNULL来进行判断,好比以下将输出NULL。

复制代码
DECLARE @content VARCHAR(MAX)

SET @content = (SELECT 
'['+ STUFF((SELECT TOP 5 ',
{"ProductName": "' + NULL 
+ '","Price": "' + CONVERT(VARCHAR, Price) 
+ '","Quantity": "' + CONVERT(VARCHAR, quantity) 
+ '","Inserton": "' + CONVERT(VARCHAR, Inserton, 105) + '"}' FROM ProductList 
 FOR XML PATH('') ,TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,''
 ) 
 + ']'[ProductDetail])

PRINT @content
复制代码

因此此时咱们必须经过ISNULL来判断列数据是否为NULL,修改为以下形式:

复制代码
DECLARE @content VARCHAR(MAX)

SET @content = (SELECT 
'['+ STUFF((SELECT TOP 5 ',
{"ProductName": "' + ISNULL(ProductName,'') 
+ '","Price": "' + CONVERT(VARCHAR, Price) 
+ '","Quantity": "' + CONVERT(VARCHAR, quantity) 
+ '","Inserton": "' + CONVERT(VARCHAR, Inserton, 105) + '"}' FROM ProductList 
 FOR XML PATH('') ,TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,''
 ) 
 + ']'[ProductDetail])

PRINT @content



你必须知道的EntityFramework 6.x和EntityFramework Core变动追踪状态

 

前言

只要有时间就会时不时去看最新EF Core的进展状况,同时也会去看下基础,把握好基础相当重要,本节咱们对比看看如标题EF 6.x和EF Core的不一样,但愿对正在学习EF Core的同行能有所帮助,同时也但愿经过本文能对您心中可能产生的疑惑进行解答,本文略长,请耐心阅读。

深刻探讨EF 6.x和EF Core变动追踪状态话题

请注意虽然EF 6.x和EF Core在使用方式上没有什么不一样,可是内置实现却有所不一样,了解它们的不一样很重要,同时也但愿更多同行选择EF Core,EF 6.x使人诟病毋庸置疑,在使用上不了解基本知识就会出很大的问题,这一点我已经明确阐述过,EF Core为咱们作了许多,只是咱们并未知道而已,看完本文相信您会认同我说的这句话,为了便于你们理解咱们用实例来讲明。

EntityState状态设置和使用对应方法是否匹配?

不管是在EntityFramework 6.x仍是EntityFramework Core中DbSet上始终包含有Add、Attath、Remove等方法,当咱们调用Add方法来添加对象时,此时内部则是将对象状态置为添加状态(Add),若是咱们调用Remove方法删除对象,内部则是将对象置为删除状态(Deleted),在EF 6.x中没有Update方法,如果更新全部列则是只需将对象状态修改成Modified,不管怎样都是经过EntityState来根据咱们的操做来设置对象相应的状态,下面咱们一块儿来看下例子。

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = new Customer() { Id = 1, Name = "Jeffcky" };

                ctx.Entry(customer).State = EntityState.Modified;

                ctx.Customers.Add(customer);

                var state = ctx.Entry(customer).State;

                var result = ctx.SaveChanges();
            };
复制代码

如上示例是在EF 6.x中,咱们首先实例化一个customer,而后将其状态修改成Modified,最后咱们调用Add方法添加到上下文中,此时咱们获得customer的状态会是怎样的呢?

没毛病,对不对,最终调用Add方法其状态将覆盖咱们手动经过Entry设置的Modified状态,接下来咱们将如上经过Entry方法修改成以下:

  ctx.Entry(customer).State = EntityState.Unchanged;

若是咱们这样作了,结果固然也是好使的,那要是咱们继续修改成以下形式呢?

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = new Customer() { Id = 1, Name = "Jeffcky" };

                ctx.Entry(customer).State = EntityState.Deleted;

                ctx.Customers.Add(customer);

                var state = ctx.Entry(customer).State;

                var result = ctx.SaveChanges();
            };
复制代码

结果仍是Added状态,依然好使,咱们继续进行以下修改。

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = new Customer() { Id = 1, Name = "Jeffcky" };

                ctx.Entry(customer).State = EntityState.Added;

                ctx.Customers.Attach(customer);

                var state = ctx.Entry(customer).State;

                var result = ctx.SaveChanges();
            };
复制代码

恩,仍是没问题,这里咱们能够得出咱们经过Entry方法手动设置未被跟踪对象的状态后,最后状态会被最终调用的方法所覆盖,一切都是明朗,还没彻底结束。那接下来咱们添加导航属性看看呢?

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = new Customer() { Id = 1, Name = "Jeffcky" };
                var order = new Order() { Id = 1, CustomerId = 1 };
                customer.Orders.Add(order);

                ctx.Entry(order).State = EntityState.Modified;

                ctx.Customers.Attach(customer);

                var state = ctx.Entry(order).State;

                var result = ctx.SaveChanges();
            };
复制代码

反观上述代码,咱们实例化customer和order对象,并将order添加到customer导航属性中,接下来咱们将order状态修改成Modified,最后调用Attath方法附加customer,根据咱们上述对单个对象的结论,此时order状态理论上应该是Unchanged,可是真的是这样?

和咱们所指望的截然相反,此时经过调用attach方法并未将咱们手动经过Entry方法设置状态为Modified覆盖,换言之此时形成了对象状态不一致问题,这是EF 6.x的问题,接下来咱们再来看一种状况,你会发现此时会抛出异常,抛出的异常我也看不懂,也不知道它想表达啥意思(在EF Core中不会出现这样的状况,我就不占用一一篇幅说明,您可自行实践)。

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = new Customer() { Id = 1, Name = "Jeffcky" };
                var order = new Order() { Id = 1 };
                customer.Orders.Add(order);

                ctx.Entry(order).State = EntityState.Deleted;

                ctx.Customers.Attach(customer);

                var state = ctx.Entry(order).State;

                var result = ctx.SaveChanges();
            };
复制代码

由上咱们得出什么结论呢?在EF 6.x中使用Entry设置对象状态和调用方法对相关的对象影响将出现不一致的状况,接下来咱们来对比EF 6.x和EF Core在使用上的区别,对此咱们会有深入的理解,若是咱们还沿袭EF 6.x那一套,你会发现竟然很差使,首先咱们来看EF 6.x例子。

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = new Customer()
                {
                    Name = "Jeffcky",
                    Email = "2752154844@qq.com",
                    Orders = new List<Order>()
                    {
                        new Order()
                        {
                            Code = "order",
                            CreatedTime = DateTime.Now,
                            ModifiedTime = DateTime.Now,
                            Price = 100,
                            Quantity = 10
                        }
                    }
                };
                ctx.Customers.Add(customer);
                var result = ctx.SaveChanges();
            };
复制代码

这里须要说明的是我将customer和order是配置了一对多的关系,从如上例子也可看出,咱们调用SaveChanges方法毫无疑问会将customer和order插入到数据库表中,以下:

接下来咱们手动经过Entry方法设置customer状态为Added,再来看看,以下:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = new Customer()
                {
                    Name = "Jeffcky",
                    Email = "2752154844@qq.com",
                    Orders = new List<Order>()
                    {
                        new Order()
                        {
                            Code = "order",
                            CreatedTime = DateTime.Now,
                            ModifiedTime = DateTime.Now,
                            Price = 100,
                            Quantity = 10
                        }
                    }
                };
                ctx.Entry(customer).State = EntityState.Added;
                var result = ctx.SaveChanges();
            };
复制代码

对照如上咱们再来看看在EF Core中是如何处理的呢?直接调用Add方法就不浪费时间演示了,用过EF Core的都知道必然好使,咱们看看手动设置状态。

复制代码
        public static void Main(string[] args)
        {
            using (var context = new EFCoreDbContext())
            {
                var blog = GetBlog();
                var post = new Post()
                {
                    CommentCount = 10,
                    CreatedTime = DateTime.Now,
                    ModifiedTime = DateTime.Now,
                    Name = "Jeffcky"
                };
                context.Entry(blog).State = EntityState.Added;
                var result = context.SaveChanges();
            }
            Console.ReadKey();
        }
        static Blog GetBlog()
        {
            return new Blog()
            {
                IsDeleted = false,
                CreatedTime = DateTime.Now,
                ModifiedTime = DateTime.Now,
                Name = "Jeffcky",
                Status = 0,
                Url = "http://www.blogs/com/createmyself"
            };
        }
复制代码

经过实践证实此时不会将Post添加到表中,为何会如此呢?由于咱们只是手动设置了blog的状态为Added,而未对Post进行设置,看到这里想必您知道了EF 6.x和EF Core的不一样。EF团队之因此这么作的目的在于如EF 6.x同样手动设置根对象的状态其导航属性即相应关联的对象也会设置,这样作会形成混乱,当咱们添加对象时其导航属性也会对应添加,虽然看起来很天然,也适应一些状况,可是对象模型并不清楚主体和依赖关系,因此在EF Core中则发生了改变,经过Entry方法只会对传入对象的状态有所影响而对关联的对象不会发生任何改变,这点尤为重要,咱们在使用EF Core时要格外注意,额外多说一句在EF Core经过Entry().State这个APi设置状态只会对单个对象产生影响不会对关联对象产生任何影响即忽略关联对象。 

EntityFramework Core为何在上下文中添加对应方法?

不知道使用过EF Core的您有没有发现,在EF 6.x中咱们发如今上下文中并无如暴露的DbSet上的方法好比Add、AddRange、Remove、RemoveRange等等,可是在EF Core则存在对应的方法,不知道您发现过没有,我虽然发现,可是一直不明白为什么如此这样作,这样作的目的在哪里呢?我还特地看了EF Core实现源码,结果发现其内部好像仍是调用了暴露在DbSet上的方法,若是我没记错的话,这样不是画蛇添足,吃饱了撑着了吗,有这个时间实现这样一个玩意,那怎么不早早实现经过Incude进行过滤数据呢?EF Core团队在github上讨论当前这不是优先级比较高的特性,其实否则,不少时候咱们须要经过导航属性来筛选数据,少了这一步,咱们只能加载到内存中再进行过滤。好了回到话题,我也是偶然看到一篇文章,才发现这样设计的目的何在,接下来咱们首先来看看在EF 6.x中的上下文中没有对应的方法结果形成的影响是怎样的呢?经过实例咱们一看便知。

复制代码
            using (var ctx = new EfDbContext())
            {
                var order = ctx.Orders.FirstOrDefault();
                var newOrder = new Order()
                {
                    CustomerId = order.CustomerId,
                    CreatedTime = DateTime.Now,
                    ModifiedTime = DateTime.Now,
                    Code = "addOrder",
                    Price = 200,
                    Quantity = 1000
                };
                ctx.Orders.Add(newOrder);
                var result = ctx.SaveChanges();
            };
复制代码

特地给出如上表中数据来进行对比,如上代码咱们查询出第一个Order即上图标注,而后咱们从新实例化一个Order进行添加,此时您能想象到会发生什么吗?瞧瞧吧。

结果是添加到表中了,可是可是可是,重要的事情说三遍,仔细看看数据和咱们要添加的Order数据对照看看,万万没想到,此时获得的数据是主键等于1的数据也就是旧数据。让咱们再次回到EF Core中演示上述例子。

复制代码
            using (var context = new EFCoreDbContext())
            {
                var post = context.Posts.FirstOrDefault();
                var newPost = new Post()
                {
                    CreatedTime = Convert.ToDateTime("2018-06-01"),
                    ModifiedTime = Convert.ToDateTime("2018-06-01"),
                    Name = "《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版",
                    CommentCount = 0,
                    BlogId = post.BlogId 
                };
                context.Add(newPost);
                var result = context.SaveChanges();
            }
复制代码

如上代码从新实例化一个Blog并添加到表中数据和如上图中数据彻底不同,咱们经过上下文中暴露的Add方法来添加Blog,咱们来看看最终在表中的数据是怎样的呢?

在EF Core上下文中有了Add,Attach、Remove方法以及Update和四个相关的Range方法(AddRange等等)和暴露在DbSet上的方法同样。 同时在上下文中的方法更加聪明了。 它们如今能够肯定类型并自动将实体对象关联到咱们想要的的DbSet。不能说很方便,而是很是方便,由于它容许咱们编写通用代码而彻底不须要再实例化DbSet,固然咱们也能够这样作,只不过如今又多了一条康庄大道罢了,代码简易且易于发现。 

即便是以下动态对象,EF Core也能正确关联到对应的对象,您亲自实践便知。

复制代码
            using (var context = new EFCoreDbContext())
            {
                dynamic newBlog = new Blog()
                {
                    IsDeleted = true,
                    CreatedTime = Convert.ToDateTime("2018-06-01"),
                    ModifiedTime = Convert.ToDateTime("2018-06-01"),
                    Name = "《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版",
                    Status = 0,
                    Url = "http://www.cnblogs.com/CreateMyself/p/8655069.html"
                };
                context.Add(newBlog);
                var result = context.SaveChanges();
            }
复制代码

让咱们再来看看一种状况来对比EF 6.x和EF Core在使用方式上的不一样,首先咱们来看看EF 6.x例子:

复制代码
            using (var ctx = new EfDbContext())
            {
                var customer = ctx.Customers.Include(d => d.Orders).FirstOrDefault();
                var newOrder = new Order()
                {
                    CreatedTime = DateTime.Now,
                    ModifiedTime = DateTime.Now,
                    Code = "addOrder",
                    Price = 200,
                    Quantity = 1000,
                    CustomerId = customer.Id
                };
                ctx.Orders.Attach(newOrder);  
                var result = ctx.SaveChanges();
            };
复制代码

此时咱们可以看到咱们只是经过Attatch方法附加了newOrder,而后进行经过SaveChanges进行提交,此时并未提交到数据库表中,那EF Core处理机制是否是也同样呢?咱们来看看:

复制代码
            using (var context = new EFCoreDbContext())
            {
                var blog = context.Blogs.FirstOrDefault();
                var newPost = new Post()
                {
                    CreatedTime = Convert.ToDateTime("2018-06-01"),
                    ModifiedTime = Convert.ToDateTime("2018-06-01"),
                    Name = "《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版",
                    CommentCount = 0,
                    BlogId = blog.Id
                };
                context.Attach(newPost);
                var result = context.SaveChanges();
            }
复制代码

很惊讶是否是,在EF Core最终添加到数据库表中了,依照咱们对EF 6.x的理解,经过Attach方法只是将实体对象状态修改成Unchanged,若是咱们想添加对象那么必须调用Add方法,此时对象状态将变为Added状态,也就是说在EF 6.x中若是咱们调用Attatch方法,可是须要将对象添加到数据库表中,此时必需要调用Add方法,反之若是咱们调用Add方法,那么调用Attath方法附加对象则画蛇添足,可是在EF Core中这种状况经过上述演示很显然发生了改变。那么EF Core内部是根据什么来判断的呢?咱们来看以下源代码:

经过上述源代码不难看出在EF Core对于未设置主键都将视为添加换句话说则是若是调用Attach方法附加一个未被跟踪的对象时且主键值未被填充时,EF Core将其视为添加,因此若是咱们须要添加对象时此时可直接调用Attach而无需调用Add方法。若是您依然不信,您可自行进行以下测试,也一样会被添加到表中。

复制代码
        public static void Main(string[] args)
        {
            using (var context = new EFCoreDbContext())
            {
                var blog = GetBlog();
                context.Attach(blog);
                var result = context.SaveChanges();
            }
            Console.ReadKey();
        }
        static Blog GetBlog()
        {
            return new Blog()
            {
                IsDeleted = false,
                CreatedTime = DateTime.Now,
                ModifiedTime = DateTime.Now,
                Name = "Jeffcky",
                Status = 0,
                Url = "http://www.blogs/com/createmyself"
            };
        }
复制代码

EF Core团队这么作的目的是什么呢?大部分状况下经过调用Attach方法将可抵达图中全部未跟踪实体的状态设置为Unchanged,除非实体对象的主键咱们没有设置且正在使用值生成,对于更新/修改同理。很显然 这对许多断开链接的实体的常见状况很是有用。可是,在许多状况下它不起做用,由于在特殊状况下是根实体,即便咱们未设置主键也强制它的状态保持不变,这样作显然不合理。若是咱们经过未设置主键调用Attach并最终添加它,这被认为是意外行为,咱们须要放开对根实体的特殊封装,经过调用Attach方法来改变这种行为,这会使得Attach变得更加适用,它并非突破性的改变。

Jeff自问自答模式来了,那么咱们是否容许咱们屡次调用Attach来附加实体对象呢?您以为是否可行呢?咱们来验证下:

复制代码
            using (var context = new EFCoreDbContext())
            {
                var blog = GetBlog();
                context.Attach(blog);
                context.Attach(blog);
                var result = context.SaveChanges();
            }
复制代码

EntityFramework Core为何添加无链接跟踪图(Disconnected TrackGraph)?

追踪图是EF中全新的概念,它提供了咱们对对象状态的彻底控制,TrackGraph遍历图(即遍历图中的每一个对象)并将指定的函数应用于每一个对象。 该函数是TrackGraph方法的第二个参数。此特性的出现也是为了调用对应方法和手动设置状态而形成的混乱而给。好比咱们想实现如EF 6.x同样,当调用Attach方法时不添加实体,那么咱们能够以下这样作。

复制代码
            using (var context = new EFCoreDbContext())
            {
                var blog = GetBlog();
                context.ChangeTracker.TrackGraph(blog, node =>
                {
                    if (!node.Entry.IsKeySet)
                    {
                        node.Entry.State = EntityState.Unchanged;
                    }
                });
                context.Attach(blog);
                var result = context.SaveChanges();
            }
复制代码

总结

本文咱们详细讲解了EF 6.x和EF Core在实体状态上使用方式的不一样且讲解了咱们须要注意到两者的不一样,接下来咱们会继续回顾基础,感谢您的阅读,咱们下节再会。

修正

关于本文用EF 6.x添加数据出现旧数据问题是我写的代码有问题,特此致歉,不知道是在什么场景会产生旧数据的问题,以前确实看过一篇文章,可是示例忘记了。

相关文章
相关标签/搜索