0%

C# 开源框架(整理) - 游子 - 博客园

Excerpt

Json.NET Json.Net 是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单。通过Linq To JSON可以快速的读写Json,通过JsonSerializer可以序列化你的.Net对象。让你轻松实现.Net中所有类型(对象,基本数据类


Json.NET

http://json.codeplex.com/

Json.Net 是一个读写Json效率比较高的.Net框架.Json.Net 使得在.Net环境下使用Json更加简单。通过Linq To JSON可以快速的读写Json,通过JsonSerializer可以序列化你的.Net对象。让你轻松实现.Net中所有类型(对象,基本数据类型 等)和Json的转换。

Math.NET

http://www.mathdotnet.com/

Math.NET的目标是为提供一款自身包含清晰框架的符号运算和数学运算/科学运算,它是C#开发的开源类库。Math.NET含了一个支持线性代数的解析器,分析复杂微分,解方程等等功能。

Faker.Net

https://github.com/jonwingfield/Faker.Net

开发的时候是不是为测试数据烦恼?Faker.Net可以非常方便帮你生成大批量测试数据。例如人员表里面的姓名、性别什么的。

Html Agility Pack

http://htmlagilitypack.codeplex.com/

Html Agility Pack 是CodePlex 上的一个开源项目。它提供了标准的DOM API 和XPath 导航–即使 HTML 不是适当的格式!HTML Agility Pack 搭配 ScrapySharp,彻底解除Html解析的痛苦。

NCrawler

http://ncrawler.codeplex.com/

NCrawler是一款国外的开源网络爬虫软件,遵循LGPL许可协议。其HTML处理使用的是htmlagilitypack开源库,采用xpath的方式处理定位网页元素,十分方便。

SuperWebSocket

http://superwebsocket.codeplex.com/

SuperWebSocket是基于.NET开源Socket框架SuperSocket开发的, SuperSocket所支持的大部分功能在SuperWebSocket中得到了继承。用户可通过SuperWebSocket来快速的构建可靠的,高性能的websocket服务器端应用程序。

SuperSocket

http://supersocket.codeplex.com/

SuperSocket 是 一个轻量级的可扩展的 Socket 开发框架,可用来构建一个服务器端 Socket 程序,而无需了解如何使用 Socket,如何维护Socket连接,Socket是如何工作的。该项目使用纯 C# 开发,易于扩展和集成到已有的项目。只要你的已有系统是使用.NET开发的,你都能够使用 SuperSocket来轻易的开发出你需要的Socket应用程序来集成到你的现有系统之中。

Quartz.NET

http://www.quartz-scheduler.net/

Quartz.NET 是一个开源的作业调度框架,是 OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而 创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,支持cron-like表达式等等。

Lucene.Net

http://lucenenet.apache.org/

Lucene.net是Lucene的.net移植版本,是一个开源的全文检索引擎开发包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎。开发人员可以基于Lucene.net实现全文检索的功能。

HttpLib

 http://httplib.codeplex.com/

一个基于C#语言的http协议的类库,Httplib让异步交互处理数据更容易了。类库的方法包括:上传文件到服务器,获取页面数据等等。

Smart Thread Pool

http://www.codeproject.com/Articles/7933/Smart-Thread-Pool

智能线程池,用SmartThreadPool可以简单就实现支持多线程的程序,由线程池来管理线程,可以减少死锁的出现。SmartThreadPool还支持简单的生产者-消费者模式,当不需要对任务进行持久化时,还是很好用的。

DocX

https://docx.codeplex.com/

DocX是一个用来操作word的轻量级的类库。借助DocX,开发人员可以在不需要安装Microsoft Word的情况下操纵word2007/2010文件。

NPOI

http://npoi.codeplex.com/

NPOI 是 POI 项目的 .NET 版本。POI是一个开源的Java读写Excel、WORD等微软OLE2组件文档的项目。使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 WORD/EXCEL 文档进行读写。NPOI是构建在POI 3.x版本之上的,它可以在没有安装Office的情况下对Word/Excel文档进行读写操作。

PDFsharp

https://pdfsharp.codeplex.com/

PDFsharp 是可以轻松地在 .NET 语言中创建PDF文档的开放源码库。它使用相同的绘制程序来创建 PDF 文档,在屏幕上显示,以及输出到打印机。可以修改、合并、拆分已经存在的 PDF 文件,支持透明图像。

Dapper

https://github.com/SamSaffron/dapper-dot-net

Dapper 是一个轻型的ORM类。代码就一个 SqlMapper.cs文件,编译后就40K的一个很小的Dll.Dapper很快。Dapper的速度接近与IDataReader,取列表的数据超 过了DataTable。Dapper支持Mysql,SqlLite,Mssql2000,Mssql2005,Oracle等一系列的数据库,当然如 果你知道原理也可以让它支持Mongo db。话说,这个ORM,博主自己也一直在使用,确实十分强悍,至少在性能方面,恐怕.NET里面的大多数ORM只能是望其项背了。

NHibernate

https://github.com/nhibernate

NHibernate是现在开发人员用的较多的一个ORM。NHibernate是一个面向.NET环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping,ORM)这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。

log4net

http://logging.apache.org/log4net/

log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台、文件、数据库等)的工具。

SharpSerializer

http://sharpserializer.codeplex.com/

SharpSerializer是一个开源XML和二进制序列化器。SharpSerializer可以序列化Xml和自己的二进制格式,还可以序列化Json等其他文本格式或其他数据加密,压缩,优化等二进制流。

XProxy

http://xproxy.codeplex.com/

XProxy是一个支持插件的基础代理程序集。通过编写简单的插件程序,你将能开发各种各样的代理程序。
XProxy是一个支持插件开发的数据交换机,可以编写插件对中转数据进行处理。内置有NAT插件、加解密插件、反向代理、直接代理、间接代理等插件。

nopCommerce

http://nopcommerce.codeplex.com/releases/view/106146

nopcommerce 是国外的一个高质量的开源b2c 网站系统,基于EntityFramework4.0和MVC3.0,使用Razor模板引擎,有很强的插件机制,包括支付配送功能都是通过插件来实现 的,基于xml的多语言版本,非常灵活的语言切换功能,包括在后台都能同时编辑产品的中英文属性,非常适合做外贸,优秀超前的程序架构,性能也非常强大, 自定义的产品名称和分类又有很好的seo优化。综合能力远远高于国内的一些程序架构糟糕的.net商城程序,是二次开发和大型b2c架构的首选。3.0开 始支持多店。

Enterprise Library

https://entlib.codeplex.com/

Enterprise Library for .Net Framework 3.5 – EntLib v4.1 是patterns & practices 小组为.NET Framework 3.5 开发一套企业库, 目前最新版本为v5.0,支持.NET Framework 4.0,共包括9个Application Block,包括数据访问(Data Access Application Block)、异常管理(Exception Handling Application Block)、数据验证(Validation Application Block)等等,对企业应用开发非常有帮助,也非常实用。

Autofac

http://autofac.org/

Autofac是一款非常优秀的IOC框架,比较于其他的IOC框架,如Spring.NET,等等之类的,它非常的轻量级且性能上也很卓越。

AutoMapper

https://github.com/AutoMapper/AutoMapper

AutoMapper是一个.NET的对象映射工具。主要用于领域对象与DTO之间的转换、数据库查询结果映射至实体对象。

7-Zip

http://www.7-zip.org/

http://sourceforge.net/projects/sevenzip/postdownload?source=dlp

7-Zip 是 一款号称有着现今最高压缩比的压缩软件,它不仅支持独有的 7z 文件格式,而且还支持各种其它压缩文件格式,其中包括 ZIP, RAR, CAB, GZIP, BZIP2和 TAR 等等。此软件压缩的压缩比要比普通 ZIP 文件高 30-50% ,因此,它可以把 Zip 格式的文件再压缩 2-10% 。

.Net PDF 类库

PDFsharp   

    PDFsharp是一款可以让.NET框架支持的任何语言很容易的创建PDF文件的类库。

ASP.NET FO PDF   

    FO PDF 是一款C#编写类似于ASP.NET服务器控件的控件。它接受DataTable 和一些其它参数来创建XSL FO,并使用NFOP (Apache FOP Port in J#) PDF Formatter来绘制一个类似PDF Report 的DataGrid 。今后将会增加更多的标签来可以生成XSL FO 。

Report.NET    

    Report.NET 开源类库包含了生成精确PDF文档的类。它是.NET平台下的C#编写的,可以帮助你创建简单的灵活的PDF文件。你可以从任何ADO.NET的 DataSet取得数据来创建PDF文档。ASP.NET可以用Report.NET来创建动态的PDF响应页面。

SharpPDF   

     SharpPDF是可以用来简单的创建PDF文件的C#类库。它创建的文件百分白兼容PDF格式。

iTextSharp   

   iTextSharp是一款开源的PDF操作类库,使用它可以快速的创建PDF文件。http://hardrock.cnblogs.com/  是一个关于 iTextSharp的中文Blog。

工作流 

Workflow.Net    

     Workflow.Net是使用微软.Net技术基于wmfc标准的创建工作流引擎。

netBPM    

     NetBPM是JBpm移植到.net平台下的一款开源工作流软件。NetBpm可以很容易和.Net应用程序集成在一起,可以创建,执行和管理工作流程序。
     Bpm Tool支持将业务模型转换成软件模型。业务开发人员可以使用模型驱动的方法设计,实现,执行和跟踪业务流程。因此开发人员能够更容易的关注业务逻辑的变化。

持久层框架

NHibernate    

     NHibernate是一个面向.NET环境的针对关系型数据库的对象持久化类库。 
     NHibernate来源于非常优秀的基于Java的Hibernate关系型持久化工具。 NHibernate从数据库底层来持久化你的.Net对象到关系型数据库。NHibernate为你处理这些,你不用自己写SQL去数据库存取对象。你 的代码仅仅和对象关联,NHibernat自动产生SQL语句,并确保对象提交到正确的表和字段中去.大量减少开发时人工使用SQL和ADO.NET处理 数据的时间. NHibernate可以帮助你消除或者包装那些针对特定数据库的SQL代码,并且帮你把结果集从表格式的表示形式转换到一系列的对象去。因此对于那些在 基于.NET的中间层的应用中,它们实现面向对象的业务模型和商业逻辑的应用,NHibernate是最有用的。

FileHelpers Library    

    FileHelpers Library是一款C#编写的开源 .NET 类库。它使用简单,很容易就可以从固定长度文件或界定记录(CSV)读/写数据。它也支持从不同的数据存储格式(Excel, Access, SqlServer)导入/导出数据。

Websharp   

    Websharp是国人开源的一款开源持久层框架,它的目标是设计一个基于.Net的通用的应用软件系统的框架,以简化基于.Net平台的企业应用软件的开发。目前,Websharp关注于企业应用软件的以下几个方面:
1、  数据库访问
2、  O/R 映射
3、  AOP
4、  分布式访问

ObjectBroker    

    ObjectBroker是.NET平台下的一款开源O/R映射框架。它支持对象缓存,1:1, 1:n 和 m:n的关联映射等特性。

Gentle.NET    

    Gentle.NET是一款开源的与关系数据库(RDBMS)无关的对象持久层框架,可以自动生成SQL和对象结构。它拥有一个SQL工厂用来创建自定义查询、DataView构建助手和卓越的性能和完善的文档。

Ubik   

     Ubik是C# 2.0下的ORM持久层框架,当前是WinForms应用程序开发提供的.它支持OPath的子集而可以进行面向对象查询,且包含一个网络事件系统.

NDal    

     NDal是一个数据提取层(DAL)框架,它可以运行在.NET和Mono环境下。

Persist.NET    

     Persist.NET是C#编写的一款完整的持久层框架。

ObjectBroker    

     ObjectBroker是.NET平台下的数据库对象/关系映射(O/R Mapping)框架。

iBATIS.NET    

    iBATIS.NET帮助你的应用系统创建更好的持久层框架。

Advanced Data Provider    

     Advanced Data Provider是为ADO.NET提供的动态数据提供者 。可以让应用程序透明的访问不同的ADO.NET 数据提供者。

OJB.NET    

     OJB.NET是一款.NET平台下的对象/关系映射(O/R Mapping)工具。

图表制作

ZedGraph    

    ZedGraph是C#编写的.NET类库,提供了用户控件和web控件。它可以创建2D的线性图、条形图和饼图。它功能完整且有详细的功能自定义,不过使用默认的选项就足够好用了。

.NET Charts    

     一款类似 PieChart, StackBar, LineChart的C#开源图表组件。

NPlot   

    NPlot是一款.NET下的开源图表类库.它值得称道的地方是优雅且灵活的API设计.NPlot包含了Windows Form控件, ASP.NET控件和一个创建Bitmap图片的类。还有一个可用的GTK#控件。

XSCharting    

    XSCharting是C#开发的图表组件,提供了多种多样的图表选项。

DaveChart   

    DaveChart是一个免费的DotNet类库。

NChart    

    NChart 提供了很多值得应用在商业,教育等多个领域的2 D图表。

WebGis

SharpMap    

    SharpMap是一款易于使用的地图渲染器,它可以为Web和Windows应用程序渲染GIS数据。SharpMap是使用C#编写,基于.NET 2.0框架上开发的开源项目。

monoGIS   

    monoGIS将成为Mono平台下的开源完整GIS。已经发布了internet mapserver,OGC WMS实现和一些工具像空间格式转换。

NASA World Wind    

    NASA World Wind 是C#开发的个人电脑上的开源的3D图形虚拟地球系统。它结合了美国国家航空航天局(NASA)从卫星拍摄的图像,这些图像应用于Blue Marble, Landsat 7, SRTM, MODIS 以及其它更多的地方。

AvalonDock的基本用法 - 龙腾飞 - 博客园

Excerpt

AvalonDock的基本用法 AvalonDock是优秀的开源项目,用于创建可停靠式布局,能够在WPF中方便开发出类似VS2010的软件界面。对于复杂的软件系统,大量控件的使用会使的界面变得难以管理。AvalonDock帮我们解决了这一问题。想要在WPF项目中使用AvalonDock的功能,首先要


AvalonDock的基本用法

        AvalonDock是优秀的开源项目,用于创建可停靠式布局,能够在WPF中方便开发出类似VS2010的软件界面。对于复杂的软件系统,大量控件的使用会使的界面变得难以管理。AvalonDock帮我们解决了这一问题。想要在WPF项目中使用AvalonDock的功能,首先要加载AvalonDock所提供的动态库,下载地址:http://avalondock.codeplex.com/releases/view/107371,目前最新的库版本为2.02。下载AvalonDock的动态库与主题库,解压后如图所示:搜狗截图20150711105140

在WPF项目的引用中添加这些库,然后使用在xaml中引入命名空间:xmlns:avalon=”http://schemas.xceed.com/wpf/xaml/avalondock",便可以在WPF中开发AvalonDock应用程序了。

下图是AvalonDock主页展示的示例截图。

AvalonDock截图

AvalonDock库中提供了一些基本的类,熟悉这些类的功能是使用AvalonDock的第一步。

DockingManager : 停靠管理器类,是AvalonDock中的核心控件之一,负责管理浮动窗体、布局存储、恢复,样式主题等。在XAML中,是AvaDock元素的根节点。

LayoutRoot : 布局根节点类,DockingManager中的内容控件完全占满DockingManager中的空间。LayoutRoot包含四个属性,LeftSide,RightSide,TopSide,BottomSide,分别用于展示DockingManager中左右上下四个位置的内容,但初始状态为隐藏状态。另外两个属性FloatingWindows,Hidden分别为浮动窗体集合和隐藏窗体集合。当一个窗格浮动时,AvalonDock会将其从其所在组中删除,然后放置到FloatingWindows集合中。当一个窗格关闭时,会将其放置在Hidden集合中。

LayoutPanel:布局面板类,LayoutRoot中的内容控件,完全占满LayoutRoot中的空间,在LayoutPanel中,可以有多个LayoutGroup,可以设定Orientation 属性,控件布局组的浮动方向。实际的窗格都位于LayoutPanel节点下。

LayoutAnchorablePane:可停靠窗格类,浮动窗格是可停靠控件LayoutAnchorable的容器。一个窗格中,可以有多个可停靠控件。浮动窗格中的可停靠控件只能是LayoutAnchorable.窗格大小设定后,不能自动改变。

LayoutDocumentPane:文档窗格类,与LayoutAnchorablePane类似,也是可停靠控件的容器,文档窗格类中可以放置可停靠控件LayoutAnchorable,也可以放置文档控件LayoutDocument,LayoutDocunemtPane会自动占满窗体的窗体布局中的剩余空间。

LayoutAnchorablePaneGroup:可停靠窗格组类,是可停靠窗格LayoutAnchorablePane的容器。通过设置Orientation 属性,用于管理多个可停靠窗格的浮动方向。

LayoutDocumentPaneGroup:文档窗格组类,是文档窗格LayoutDocumentPane的容器。通过设置Orientation 属性,用于管理多个文档窗格的浮动方向。

LayoutAnchorable:可停靠内容类,一般放置在LayoutAnchorablePane中,其内容可以是用户自定义控件类型,比如,在UserControl中设置好WPF基础控件布局,然后将整个UserControl放置在LayoutAnchorable中,这样,整个UserControl内容就可以随着可停靠控件一起浮动或者停靠。

LayoutDocument:文档类,与LayoutAnchorable功能类似,区别在于LayoutDoucument会随着LayoutDocumentPane一起占满窗体剩余空间。

        介绍了这么多内容,目的只是为了让大家对AvalonDock中的类有个简单的了解。其实AvalonDock中的类有着明显的层次结构,其实就是容器的嵌套。DockingManager作为顶层容器,然后包含一个LayoutRoot对象,LayoutRoot中又包含一个LayoutPanel对象。LayoutPanel中便是LayoutAnchroablePane对象和LayouDocumentPane对象的集合。同时,可以对LayoutAnchroablePane对象和LayouDocumentPane对象进行分组,每个组可以单独设定组内的浮动方向。LayoutAnchorablePane又是LayoutAnchorable的容器,LayioutDocumanePane又是LayoutDocument的容器。一层一层进行嵌套,在最后的LayoutAnchorable中或者LayoutDocument中,我们放入我们真正的控件对象,这样,就可以对他们进行分类摆放布局。

下面介绍具体的用法。

1.窗体布局存储与恢复

DockingManager中提供了将窗体布局序列化为xml文件内容的方法,同时提供了从xml布局文件中恢复布局的方法。

(1)保存布局

1
2
3
4
5
XmlLayoutSerializer serializer = new XmlLayoutSerializer(DockManager);
using (var stream = new StreamWriter("Layout.xml"))
{
serializer.Serialize(stream);
}

(2)恢复布局

1
2
3
4
5
XmlLayoutSerializer serializer = new XmlLayoutSerializer(DockManager);
using (var stream = new StreamReader("Layout.xml"))
{
serializer.Deserialize(stream);
}

恢复布局时,有一点需要注意,需要为LayoutAnchrobale对象和LayoutDocument对象设置ContentId属性,否则,DockingManager会忽略内容的恢复。

2.主题更换

AvalonDock中提供了六种主题样式,要使用这些主题,需要在程序中导入主题库。DockManger为DockingManager对象,通过改变DockingManager中的Theme属性,便可以改变整个界面的样式。

复制代码

1
2
3
4
5
6
DockManager.Theme = new GenericTheme();
//DockManager.Theme = new AeroTheme();
//DockManager.Theme = new ExpressionDarkTheme();
//DockManager.Theme = new ExpressionLightTheme();
//DockManager.Theme = new MetroTheme();
//DockManager.Theme = new VS2010Theme();

复制代码

3.RootSide操作

动态改变LayoutRoot.LeftSide对象内容。

(1)xaml中的代码

复制代码

1
2
3
4
5
6
7
<avalon:LayoutRoot.LeftSide>
<avalon:LayoutAnchorSide>
<avalon:LayoutAnchorGroup x:Name="LeftGroup">

</avalon:LayoutAnchorGroup>
</avalon:LayoutAnchorSide>
</avalon:LayoutRoot.LeftSide>

复制代码

(2)后台代码

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
private void miLeft_Click_1(object sender, RoutedEventArgs e)
{
try
{
LayoutAnchorable anchorable = new LayoutAnchorable();
anchorable.Title = "Left";
LeftGroup.Children.Add(anchorable);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "[MainWindow][miLeft_Click_1]");
}
}

复制代码

4.Pane操作

动态改变软件中的窗格布局。

(1)xaml中的代码

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<avalon:DockingManager x:Name="DockManager">
<avalon:DockingManager.Theme>
<avalon:ExpressionDarkTheme/>
</avalon:DockingManager.Theme>

<avalon:LayoutRoot x:Name="Root">
<avalon:LayoutPanel x:Name="Panel" >
<avalon:LayoutAnchorablePaneGroup x:Name="LeftAnchorableGroup" DockWidth="300">
<avalon:LayoutAnchorablePane x:Name="LeftPane">
<avalon:LayoutAnchorable x:Name="Solution" Title="解决方案" ContentId="Solution"/>
</avalon:LayoutAnchorablePane>
</avalon:LayoutAnchorablePaneGroup>

<avalon:LayoutAnchorablePane>
<avalon:LayoutAnchorable ></avalon:LayoutAnchorable>
</avalon:LayoutAnchorablePane>
<avalon:LayoutDocumentPane>
<avalon:LayoutDocument></avalon:LayoutDocument>
</avalon:LayoutDocumentPane>

<avalon:LayoutDocumentPaneGroup x:Name="DocumentGroup">
<avalon:LayoutDocumentPane x:Name="DocumentPane">
<avalon:LayoutDocument Title="document" ContentId="document">

</avalon:LayoutDocument>
</avalon:LayoutDocumentPane>
</avalon:LayoutDocumentPaneGroup>

<avalon:LayoutAnchorablePaneGroup x:Name="RightAnchorableGroup" Orientation="Vertical" DockWidth="300">
<avalon:LayoutAnchorablePane x:Name="RightPane" >
<avalon:LayoutAnchorable Title="属性" ContentId="Property"/>
</avalon:LayoutAnchorablePane>
</avalon:LayoutAnchorablePaneGroup>


</avalon:LayoutPanel>
</avalon:LayoutRoot>
</avalon:DockingManager>

复制代码

(2)添加水平AnchorablePane

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void miAnchorPane_Click_1(object sender, RoutedEventArgs e)
{
try
{
LayoutAnchorablePane pane = new LayoutAnchorablePane();
LayoutAnchorable anchorable = new LayoutAnchorable();
anchorable.Title="水平方向";
pane.Children.Add(anchorable);
LeftAnchorableGroup.Children.Add(pane);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message,"[MainWindow][miAnchorPane_Click_1]");
}

}

复制代码

(3)添加竖直AnchorablePane

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void miAnchorVerticalPane_Click_1(object sender, RoutedEventArgs e)
{
try
{
LayoutAnchorablePane pane = new LayoutAnchorablePane();
LayoutAnchorable anchorable = new LayoutAnchorable();
anchorable.Title = "竖直方向";
pane.Children.Add(anchorable);
RightAnchorableGroup.Children.Add(pane);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "[MainWindow][miAnchorVerticalPane_Click_1]");
}
}

复制代码

(4)添加DocumentPane

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void miDocumentPane_Click_1(object sender, RoutedEventArgs e)
{
try
{
LayoutDocumentPane documentPane = new LayoutDocumentPane();
LayoutDocument document = new LayoutDocument();
document.Title = "document";
document.Content = new RichTextBox();
documentPane.Children.Add(document);
DocumentGroup.Children.Add(documentPane);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "[MainWindow][miDocumentPane_Click_1]");
}
}

复制代码

5.浮动窗体显示

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void miSearchWnd_Click_1(object sender, RoutedEventArgs e)
{
LayoutAnchorable anchorable = new LayoutAnchorable();
anchorable.Title = "查询";
anchorable.FloatingWidth = 300;
anchorable.FloatingHeight = 300;
anchorable.FloatingTop = 200;
anchorable.FloatingLeft = 300;

Button button = new Button();
button.Content = "查询";
button.Width = 80;
button.Height = 40;

anchorable.Content = button;
LeftPane.Children.Add(anchorable);
anchorable.Float(); //调用Float方法,使窗体浮动显示
}

复制代码

6.隐藏窗体显示

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void miRestoreHideWnd_Click_1(object sender, RoutedEventArgs e)
{
try
{
if (Root.Hidden != null)
{
while (Root.Hidden.Count > 0)
{
Root.Hidden[0].Show();//调用show方法,恢复窗体显示。
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "[MainWindow][miRestoreHideWnd_Click_1]");
}
}

复制代码

7.窗体操作

(1)添加Anchorable

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
private void miAddAnchroable_Click_1(object sender, RoutedEventArgs e)
{
LayoutAnchorable anchorable = new LayoutAnchorable();
anchorable.Title = "工具";
Button btn = new Button();
btn.Content = "this is a test button";
anchorable.Content = btn;
btn.Height = 30;
btn.Width = 150;
anchorable.IsActive = true;
RightPane.Children.Add(anchorable);
}

复制代码

(2)添加Document

复制代码

1
2
3
4
5
6
7
8
private void miAddDocument_Click_1(object sender, RoutedEventArgs e)
{
LayoutDocument document = new LayoutDocument();
document.Title = "doc";
document.Content = new RichTextBox();
document.IsActive = true;
DocumentPane.Children.Add(document);
}

复制代码

(3)添加并显示窗体

复制代码

1
2
3
4
5
6
7
private void miOutPutWnd_Click_1(object sender, RoutedEventArgs e)
{
LayoutAnchorable anchorable = new LayoutAnchorable();
anchorable.Title = "输出";
anchorable.Content = new RichTextBox();
anchorable.AddToLayout(DockManager, AnchorableShowStrategy.Bottom);
}

复制代码

(4)窗体切换自动隐藏

复制代码

1
2
3
4
5
6
7
private void miAutoHide_Click_1(object sender, RoutedEventArgs e)
{
if (Solution != null)
{
Solution.ToggleAutoHide();
}
}

复制代码

至此,AvalonDock的基础用法,至于更改AvalonDock的外观样式,使用MVVM模式等高级的用法,需要自己慢慢去学习了。

引言

在看PDC-09大会的视频时,其中一篇讲利用Blend来扩展Silverlight元素的行 为,当时感觉很酷:在Blend中,将MouseDragElementBehavior拖到任意一个元素上,这个元 素就可以被随意拖动。

因为之前在Silverlight SDK中好像没有看到相关的介绍,事实如此, Microsoft.Expression.Interactions和System.Windows.Interactivity程序集在Blend工具中才有。但是从来没有去看过Blend的帮助文档,因为我以为那不过是告诉你怎样使用工具,事实上 我错了,Microsoft Expression Blend 软件开发工具包(SDK) 没有包含怎样使用Blend的信息, 而只有介绍以上提到的两个程序集。

无知的人第一件会做的事就是上Google,我也是这样的人。所以赶紧Google一下,原来 Behavior是一种Silverlight元素行为的重用方式,并且每一篇介绍Behavior的作者,无不流露出对Behavior的赞赏,甚至对有些人来讲,那是Silverlight最令人激动的特性,因为那使他们可能很方便并且简单的为自己的界面添加只有少数具有美术创意的人才能做出的元素效果。

于是我决定整理一下Behavior相关的知识。

然而Blend SDK一看却感到很吃惊:这不是微软史上最简单的SDK么!如果排除类库介绍 这个SDK只有3个页面,而里面每个例子的代码加起来还不到50行。更惊讶的是微软认为这么简单的东西我却怎么看也不会做,因为我有一种先天缺陷:在不明白为什么的情况下再简单的事情我都不会做

     Behavior真这么简单么?事实上不能这么说,从使用的角度,实现一个简单的Behavior不需要多少代码,需要做的事情也不多,然而在这背后的思路呢。比如ASP.NET,一般人看几个小时说就能Hello,World,但是真正做起来却会遇到很多问题。尤其现在开发工具越来越强大,许多东西不需要我过多理解就能实现,但是我相信,只有真正理解一些东西,我们的技术才会真正提高。

     本文从自己的学习角度更深入点的分析这种特性。网上介绍的一般只是教怎么做,很多差不多就是SDK里面的简单的例子。Behavior特性的背后其实是有比较好的思想的。这些思想有助于我们更好的理解这种特性。      本文不打算写太多例子,因为例子实在很简单,可以参考Blend SDK。

Behavior 的设计模式是一种行为模式

我们应该怎样认识设计模式?

设计模式到底是一种什么样的东西,每个开发人员有自己的理解。有的人认为那是具有很高技术经验的人才应该掌握的东西;有的人也认为我们一般的开发中不会用到什么设计模式……. 这些观点好像想说明这样一件事情:设计模式是一种可有可无的东西,你有那个兴致就可以去看 下,看了记不住也没有关系。

当然每个人有自己的个人经验,也许不懂设计模式不会影响你做某些事情,但是明白一种设计模式却能帮助你更好的做某件事情。其实在我们的开发中,很多技术都从设计模式中抽象了出来,使我们不用去实现这些高度抽象的技术思维。从语言级别,比如C#中的迭代器模式,有一次我面试没有答上来的委托对应的Observer模式;到框架级别,比如MVC,MVP以及MVVM模 式。Ruby On Rails从2004年出现到现在,一直受到不少开发者的追捧,其实ROR的开发者只不过是用Ruby来实现了MVC的模式,这个框架使其他开发者可以直接利用MVC的模式进行开发,而不必关心怎样自己去实现这个模式。但是与对照教程做练习相比,理解MVC的思路绝对有助于真正的掌握Ruby On Rails技术。到我们的WPF也是一样,XAML不仅仅是一个可以编写界面的方式,它包含的是一种MVP的设计模式,当我们明白这种设计模式的思路时,我相信我们会觉得 XAML就是那么一回事,不然,跟着技术细节跑,你总会有许多的为什么并且不知道为什么。比如WPF有事件模式,为什么又要来个命令模式(Command),其实命令模式也是23种经典模式之一,在WPF中得到支持,看看命令模式再来看WPF中的Command,相信会有不同的感觉。      所以,在我们的学习中,可以将对技术的理解和设计模式一起思考,那样将有助于我们更深刻的理解技术。我们本可以用更底层的技术来实现很多东西,正是因为一些高级语言和开发工具将一些复杂的设计模式融入到各种设计框架和语言中,才使得我们的开发更加简单有效。既然 是基于这些抽象的模式之上,那么理解这些不同的模式将有助于我们真正掌握技术的内涵。

重用行为

语言发展到今天,很重要的一个进步就是重用。无论是从语言级别的类,还是COM里的dll,这些技术为技术的发展做出了很大的贡献。

Behavior也是可重用的一种特性。Expression的网站上有许许多多的Behavior可供下载。Blend本身也提供了几个,如下图:

我们只要将一个Behavior拖到一个元素上,就能使这个元素具有某种行为,而实现这种行为的程序集和你的项目是独立的,你创建的Behavior也可以被其他人使用。我们后面将会讨论这是怎样 实现的。      在行为设计模式中。往往有一下几个参与者:

•Element :要被附加行为的对象元素(这里也可以是一种逻辑功能的抽象,比如排序)

•IBehavior:行为接口

•Behavior:不同的行为实现      通常,Element所在的程序集和Behavior所在的程序集是不同的,Behavior有多个实现版本,可以将不同的版本附加到Element,就会使Element具有不同的行为。比如这里Element是 一种排序的功能,那么你可以定义一个Behavior是归并排序,而另一个Behavior是快速排序;所 不同的是行为模式中通常有一种选择器来决定选取那个Behavior,而WPF采用一种附加的技术将 Behavior附加到一个Element,并且很独特的是可以附加多个Behavior,后面我们将看到, Behavior中通常用于给元素附加事件,而事件的Add方法类似于多播委托,即使都添加相同的事 件仍然不会影响其他Behavior,事件和委托的一个重要区别就是事件没有赋值方法(另一个是事 件确保只有事件所在的类才能触发一个事件通知),这避免不小心取消其他订阅。所以在 Silverlight中,一个元素可以添加多个Behavior。

不管怎样,我们看到的是这样一种思路:你可以为某个元素添加某种独特的行为,而这种行 为不是直接通过为元素添加事件来实现,因为那样在其他地方将达不到重用而不得不拷代码或者 重写。在汉语中很多相同字开头的词往往是近义词,但我认为重写和重用是反义词。而是通过一种附加的技术,将其他程序集的Behavior附加到一个元素,这个意义非常重大,使得我们可无限 扩展并且重用行为。这样你可以享受别人的成果。那么Silverlight怎样实现这种附加呢?下一节将会讨论。

怎样将一种行为附加到一个元素呢?

附加,非常可怕的一种技术       

      当你在自定义一个Behavior的时候,你将决定千千万万以后会引用你的Behavior的元素的行为,你可以让他放大,你可以让缩小,旋转,任何你想得出来的创意。他人的添加你的程序集 并将你自定义的附加到一个元素时,这个元素就具有了你定义的行为,想想要是世界上有一种附加技术将一种杀人的行为附加到你身上真是非常可怕。 背后的英雄 : IAttachedObject

     Interface这种技术隐含的意义真是无法估量。.Net Framework中就有大量的接口,以下是 IAttachedObject 定义:        

     An interface for an object that can be attached to another object.   

     public interfaceIAttachedObject   

    {

         DependencyObject AssociatedObject { get; }     

         void Attach(DependencyObject dependencyObject);   

         void Detach();

    }      实现这个接口的类的实例可以被附加到另一个对象,如:TriggerBase,TriggerAction, Behavior等。在程序中我们可以调用void Attach(DependencyObject dependencyObject)方 法将继承该接口的对象附加到另一对象,这里是dependencyObject对象。        

     通常我们在自定义行为的时候不是直接继承自IAttachedObject接口,而是继承自从 IAttachedObject继承的一些类,这些类都有可重载的OnAttached(),OnDetached()方法,比如 Behavior类,TriggerBase类,TriggerAction类等。通常在Attach()方法中会触发 OnAttached()方法,在Detach()方法中会触发OnDetached()方法。        

     所以Blend SDK中自定义Action,Behavior和Trigger的例子都非常简单,你只需要重写 Ontached()方法和OnDetached()方法即可。你可以为AssociatedObject 添加各种事件从 而改变其行为。       

     以上分析的就是Silverlight中Behavior的机制。所以,本质上,Behavior仍然是利用事件机制来实现的。但是我们通过引入一种不同的设计模式而使得相同的功能更具有扩展性。这进一步说明设计模式的重要性。 后面我们进一步来分析实现方法。

回顾附加属性:        

     这里有必要回顾以下附加属性的概念,这将有助于理解Behavior的附加方式。附加属性其 实是一种普通的依赖项属性。并且它由WPF属性系统管理,不同的是附加属性被应用到一个非定 义到该属性的类,附加属性通过DependencyProperty.RegisterAttached()方法来定义。并 \且该属性不需要.NET属性包装器,因为附加属性可以被应用与任何附加的对象。看一下Grid的定义:

     public class Grid: Panel   

    {

        public static readonly DependencyProperty ColumnProperty;

        public static readonly DependencyProperty ColumnSpanProperty;

        public static readonly DependencyProperty RowProperty;

        public static readonly DependencyProperty RowSpanProperty;

        public static readonly DependencyProperty ShowGridLinesProperty;

        public Grid();

        public ColumnDefinitionCollection ColumnDefinitions { get; }

        public RowDefinitionCollection RowDefinitions { get; }

        public bool ShowGridLines { get; set; }

        protected override sealed Size ArrangeOverride(Size arrangeSize);

        public static int GetColumn(FrameworkElement element);

        public static int GetColumnSpan(FrameworkElement element);

        public static intGetRow(FrameworkElement element); 

        public static int GetRowSpan(FrameworkElement element);

        protected override sealed Size MeasureOverride(Size constraint);

        public static void SetColumn(FrameworkElement element, int value);

        public static void SetColumnSpan(FrameworkElement element, int value);

        public static void SetRow(FrameworkElement element, int value);

        public static void SetRowSpan(FrameworkElement element, int value);

    }      可以看到上面标记处的Grid的几个附加属性,它们没有.NET属性包装器,而通过 GetPropertyName()和SetPropertyName()静态方法来取值和赋值。        

     在被附加属性的元素内部看不到属性的值,所以也根本不知道它的存在—但是在自己定义 的代码中可以根据相应的值进行操作。比如设置了Grid.Row属性后,Grid在计算和排列内部元素 时知道每个元素的这个属性的值,可以根据此值安排此元素在Grid中的位置,Canvac的 Top,Left,Bottom,Right附加属性也是如此,只不过Canvas的附加属性更精确,所以内部计算 更少,因此Canvas的执行效率通常更高。        

     你甚至可以在不包含Grid的地方使用Grid.Row等附加属性,但是这没有丝毫意义,除非你 想在程序中使用这个值。所以你也可以自定义这种附加属性,比如你想在每个元素上保存一个状 态值,你在程序中需要用到这个状态值。我们接下来将看到Silverlight正是通过这种附加属性的 特性实现了行为的附加

Silverlight 通过Interaction的附加属性来实现这种附加          

     实现IAttachedObject的TriggerBase,TriggerAction以及Behavior通过Interaction的附 加属性Triggers和Behaviors来实现这种附加。

     namespace System.Windows.Interactivity

    {

        public static class Interaction

       {

             public static readonly DependencyProperty BehaviorsProperty;

             public static readonly DependencyProperty TriggersProperty;              public static BehaviorCollection GetBehaviors(DependencyObject obj);

             public static TriggerCollection GetTriggers(DependencyObject obj);

       }

    }

     通过Reflectot工具我们可以看到BehaviorsProperty和TriggersProperty的声明:   

     public static readonly DependencyProperty BehaviorsProperty =

              DependencyProperty.RegisterAttached(“Behaviors”,

                                                                      typeof(BehaviorCollection),

                                                                      typeof(Interaction),

                                                                      new PropertyMetadata( new PropertyChangedCallback(Interaction.OnBehaviorsChanged) )) ;

     TriggersProperty的声明方式类似,不再贴代码。我们看到其数据类型为BehaviorCollection,并且注册为附加属性,在属性发生改变时会触发 Interaction.OnBehaviorsChanged方法:

     private static void OnBehaviorsChanged(DependencyObjectobj, DependencyPropertyChangedEventArgs args)

    {

         BehaviorCollection oldValue = (BehaviorCollection) args.OldValue;

         BehaviorCollection newValue = (BehaviorCollection) args.NewValue;

         if (oldValue != newValue)

        { 

             if ((oldValue != null) && (oldValue.AssociatedObject != null)) 

            {

                 oldValue.Detach(); 

             }

            if ((newValue != null) && (obj != null))  

            {

                 if (newValue.AssociatedObject != null)

                {

                      throw new InvalidOperationException(ExceptionStringTable.CannotHostBehaviorCollectionMultiple TimesExceptionMessage);

                } 

                newValue.Attach(obj);

             }

        }

     }

     分析此方法,此方法最主要的功劳就是调用BehaviorCollection.Attach()方法。而 BehaviorCollection集合会调用里面每一个子项的Attach()方法。前面我们讲过在调用Behavior类的Attach()方法的时候,该方法会调用OnAttached()方法,所以在OnAttached方法里面,我们就可以为元素添加各种事件代码。而因为事件的Add操作使得其中 一个Behavior添加的相同的事件处理程序不会覆盖另一个Behavior添加的事件处理程序,从而我 们可以为一个元素添加很多的Behavior。        

     这里值得注意的是,上面标出的红色背景的参数obj,在依赖项属性中 PropertyChangedCallback函数中的第一个参数是定义这个属性的类本身的实例对象。而对于以 来属性,这个参数是指被附加了附加属性的那个对象。这里不明白的话就看不懂上面的代码。

Behaviors VS Triggers       

     下面来看下所谓简单的实例代码:

    

     看得出这都是Blend提供的程序集才有的命名空间。在实例中我们为Rectangle和Canvas 元素添加了Interaction的两个附加属性,这两个附加属性是集合类型的,里面添加了一些子项, 比如MouseDragElementBehavior,EventTrigger。         

     Trigger和Behavior是类似的,而且用法也如此,Triggers的子项也都是继承自 IAttachedObject接口的类的实例,所不同的是Triggers子项包含Actions属性,这个属性是一个 Action集合,每个Action也都是继承自IAttachedObject接口。都有Attach()方法和 DeTach()方法,并且相应的会触发OnAttached()和OnDetached()方法。而Action继承 自Action的子类还包含InVoke()方法。        

     所以,对于Interaction.Triggers中的每个子项,会制定特定的事件,当该事件触发会会自动调用Actions属性中每个子项的InVoke()方法;而对于Interaction.Behaviors中的每个子 项,通常具体对什么事件做处理封装在每个Behavior中,这种方式更灵活。        

     对照下面的代码相信很容易理解两种的区别:     

     上面的类ListBoxItemSendToTop是Blend示例中的ColorSwatchSL中定义的一个Action。 该行为将ListBoxItem中的每个元素添加一种行为:当鼠标经过每个Item时将该元素放大,并且 浮到其他元素的上面。可以参考Blend的实例效果。我们可以参考下面的基础层次结构,事实上 TriggerAction还继承自IAttachedObject接口。

    

     这几行代码将元素添加附加属性Interaction.Triggers,并添加一个子项,指明当Loaded事 件发生时触发ListBoxItemSendToTop的Invoke()方法。不过奇怪的是Blend的实例并没有按 正确的思路使用,下面是截图:

    

     从上面的代码可以看出,对元素行为的附加发生在OnAttached()方法中,而此方法不是 被EventTrigger制定的Loaded事件触发,而是发生在程序初始化时TriggerCollection.Attach()触发 EventTrigger.Attach(),一次触发ListBoxItemSendToTop.Attach()而附加行为的。当这种触发Loaded事 件的时候什么都没有做。虽然这些写程序没有问题,但是和Triggers的思路却是不一致的。以上例 子我们可以看出Triggers的执行过程。       

     为了比较Triggers和Behaviors,我将上面的Triggers实现改为Behaviors实现,代码如下:

    

     我将SendToTopBehavior声明为一种Behavior,继承自Behavior,Behavior的基 础层次结构和Action类似,同样它也继承自IAttachedObject接口:

    

     通过改写为Behavior,这才是真正通过OnAttached()方法来添加行为,这和前面分析的过程一致。     

     通过以上比较应该能明白Triggers和Behaviors的区别。Behaviors提供了更灵活的方式,行为响应什么方式将完全有行为自己决定。而Action只提供行为,行为受什么事件触发则由元素定义中 自己决定。

结论:

     Behavior简单吗?现在看来它的思路不算复杂。然而从上面的分析过程来看也不算简单。最主 要的问题,里面涉及到的东西比较多,比如要理解附加属性。还得对XAML有一定理解,对于 XAML ,它里面的每一个标记都是对象或者对象的属性。那么既然是对象,我们就可以利用对象初 始化的时候去干一些事情,比如为元素添加事件。这样我们就能理解上面发生的一切,并且会明 白为什么我们感觉XAML又在做一些我们在后台代码才能做的事情,认为XAML就是一堆死标记, 这就不能真正理解 XAML 背后的设计模式和意义。      

     另外,我也从设计模式的角度思考这个问题。这样更有助于理解。       

     Blend SDK上两分钟就能看完的例子,我花两天才勉强理解。我在想微软是不是太高估开发者?还是我太笨了?

System.Windows.EventTrigger VS

System.Windows.Interactivity.EventTrigger      正当准备收笔的时候,才想起Silverlight本身也有触发器的啊。那里的用法好像可不一样。       

     这可吃惊不小,因为很多天没看Silverlight了,加上一直没做过项目,概念理解也不是很深。于是赶紧Go To Definition。才发现两种是有区别的,这里做一下比较: 1 ,命名空间区别:         

     Silverlight本身的Triggers位于System.Windows命名空间,而Blend提供的Triggers位于 System.Windows.Interactivity命名空间,并且随Blend一起附带,与Silverlight SDK是独立的。 2 ,定义区别:

     namespace System.Windows.Interactivity

    {

       public static class Interaction   

      {

          public static readonly DependencyProperty BehaviorsProperty;

          public static readonly DependencyProperty TriggersProperty;

          public static BehaviorCollection GetBehaviors(DependencyObject obj); 

          public static TriggerCollection GetTriggers(DependencyObject obj);

      }

    }      namespace System.Windows

    {

       public abstract class FrameworkElement : UIElement

       {

            ……       

            public TriggerCollectionTriggers{ get; }   

            …..        }

     }

     从定义上看,两种是有区别的,这决定了使用方式会有区别。       

      Silverlight本身的Triggers是一个普通的.NET属性,而Blend提供的Triggers是一个附加属性。而我 们转到TriggerCollection的定义,前者是一个普通的.NET集合类,继承自 DependencyObject,IList, ICollection, IEnumerable, IList, ICollection, IEnumerable ,而后者 继承自 DependencyObjectCollection , IAttachedObject ,具有 Attach(),DeTatch(),OnAttached(),OnDeTached()方法。 3 ,使用方式区别        

     前者的子项是StoryBoard,通过响应事件来激发动画,而后者正如前面分析,子项是继承自 IAttachedObject接口的Action对象,通过响应事件来激发Action的Invoke()方法。      以上简单的分析可以看出两者虽然名称相同,但要功能是不一样的,Silverlight的Trigger是用 来触发动画,动画是在一段时间内持续的改变元素属性。而Blend的Trigger更多用来响应鼠标和键 盘这种用户行为,当然你也可以在OnAttached里面来启动一个动画,这也是可以的。       

     总之,Blend提供的Trigger更灵活,功能更丰富,可以这么说,你能做出一切你想得到的用户交互行为。正如WPF的外观可以无限定制。虽然SDK是那么的简单!

如何将一个行为附加到某个元素上呢?我们可以通过自定义一个Behavior!

我们首先看一下IAttachedObject接口,Behavior默认继承之这个接口

复制代码

// 摘要: // 供可以附加到另一个对象的对象使用的接口。
public interface IAttachedObject
{ // 摘要: // 获得关联的对象。 //
// 备注: // 代表此实例附加到的对象。
DependencyObject AssociatedObject { get; } // 摘要: // 附加到指定的对象。 //
// 参数: // dependencyObject: // 要附加到的对象。
void Attach(DependencyObject dependencyObject); //
// 摘要: // 将此实例与其关联的对象分离。
void Detach();
}

复制代码

下面我们自定义一个Behavior附加到ListBox元素上:

复制代码

public class SelectedItemFillBehavior : Behavior//把行为附加到ListBox上。ListBox被附加了下面的行为
{ // Using a DependencyProperty as the backing store for DefaultHeight. This enables animation, styling, binding, etc…
public static readonly DependencyProperty DefaultHeightProperty = DependencyProperty.Register(“DefaultHeight”, typeof(double), typeof(SelectedItemFillBehavior), new UIPropertyMetadata(30.0)); // Using a DependencyProperty as the backing store for AnimationDuration. This enables animation, styling, binding, etc…
public static readonly DependencyProperty AnimationDurationProperty = DependencyProperty.Register(“AnimationDuration”, typeof(int), typeof(SelectedItemFillBehavior), new UIPropertyMetadata(300)); public double DefaultHeight
{ get { return (double)GetValue(DefaultHeightProperty); } set { SetValue(DefaultHeightProperty, value); }
} public int AnimationDuration
{ get { return (int)GetValue(AnimationDurationProperty); } set { SetValue(AnimationDurationProperty, value); }
} protected override void OnAttached()
{ base.OnAttached(); //附加行为后需要处理的事件
AssociatedObject.SelectionChanged += new SelectionChangedEventHandler(OnAssociatedObjectSelectionChanged);
} protected override void OnDetaching()
{ base.OnDetaching(); //解除的事件
AssociatedObject.SelectionChanged -= new SelectionChangedEventHandler(OnAssociatedObjectSelectionChanged);
} private void OnAssociatedObjectSelectionChanged(object sender, SelectionChangedEventArgs e)
{ double selectedItemFinalHeight = AssociatedObject.ActualHeight;

        Storyboard storyBoard \= new Storyboard(); for (int i = 0; i < AssociatedObject.Items.Count; i++)
        {
            ListBoxItem item \= AssociatedObject.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem; if (!item.IsSelected)
            {
                selectedItemFinalHeight \-= DefaultHeight;

                DoubleAnimation heightAnimation \= new DoubleAnimation()
                {
                    To \= DefaultHeight,
                    Duration \= new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration))
                };
                Storyboard.SetTarget(heightAnimation, item);
                Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(FrameworkElement.HeightProperty));
                storyBoard.Children.Add(heightAnimation);
            }
        }

        selectedItemFinalHeight \-= 4; if (AssociatedObject.SelectedIndex >= 0)
        {
            ListBoxItem selectedItem \= AssociatedObject.ItemContainerGenerator.ContainerFromIndex(AssociatedObject.SelectedIndex) as ListBoxItem;

            DoubleAnimation fillheightAnimation \= new DoubleAnimation()
            {
                To \= selectedItemFinalHeight,
                Duration \= new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration))
            };

            Storyboard.SetTarget(fillheightAnimation, selectedItem);
            Storyboard.SetTargetProperty(fillheightAnimation, new PropertyPath(FrameworkElement.HeightProperty));
            storyBoard.Children.Add(fillheightAnimation);
        }

        storyBoard.Begin(AssociatedObject);
    }
}

复制代码

这样ListBox被附加了以上行为。

  在Bing Maps Silverlight Control中以及为我们提供了地图图钉控件Pushpin,我曾经在《使用图钉层(Pushpin layer)及地图图层(MapLayer)》一文中介绍过他的使用方法,本篇主要介绍如何自定义图钉标注控件以及对他的一些扩展,比如实现图钉的动态ToolPanel。

  关于图钉的UI外观的设计这里就不详细介绍了,通过Blend可以快速的构建UI界面。首先介绍下我的实现思想,通过Path构建图钉标注控件的UI外观,控件整体布局使用Grid布局,分别在Grid容器里设计好图钉UI外观效果和需要动态提示的ToolPanel(使用Border布局设计),默认将ToolPanel隐藏,在后台代码中通过鼠标事件进行控制其是否出现;另外还布局了一个TextBlock控件,用于出现图钉标注上的内容。下面是定义好的图钉风格的样式:

自定义图钉样式

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       接下来要做的就是实现图标注控件的定义以及动态控制ToolPanel控件的显示与否了,主要思路就是通过引用上面定义的样式,以模板的方式从样式里获取到相应的控件元素进行动态控制,详细见如下代码: ![复制代码](https://common.cnblogs.com/images/copycode.gif) ///  /// 自定义图钉控件///  public class PushPinControl : ContentControl {    private string Text;public PushPinControl(String text)     {        //设置图钉标注控件的标注内容         this.Text \= text \== "" ? "M" : text;         DefaultStyleKey \= typeof(PushPinControl);        //为当前对象引用样式,从全局资源里读取         this.Style \= Application.Current.Resources\["PushpinStyle"\] as Style;     }public override void OnApplyTemplate()     {        base.OnApplyTemplate();//从样式模板中获取TextBlock控件         var textBlock \= GetTemplateChild("txtTitle") as TextBlock;         textBlock.Text \= this.Text;        //从样式模板中获取ToolPanel(既Border布局实现的,详细见样式的定义)控件         var ToolPanelBorder \= GetTemplateChild("ToolPanel") as Border;        //通过事件动态控制ToolPanel的显示和隐藏         this.MouseEnter += (mes, meo) \=> { ToolPanelBorder.Visibility \= Visibility.Visible; };        this.MouseLeave += (mls, mlo) \=> { ToolPanelBorder.Visibility \= Visibility.Collapsed; };     } } ![复制代码](https://common.cnblogs.com/images/copycode.gif)   到这里就成功的完成了图钉标注控件的自定义了,接下来看看如何使用这个自定义的图钉标注控件,详见如下代码块: ![复制代码](https://common.cnblogs.com/images/copycode.gif) private void AddPushPin() {    //添加Bing Maps Silverlight Control自带的图钉    //var pushpin = new Pushpin();    //pushpin.Location = new Location(29.5076372217973, 106.489384971208);    //添加自定义的图钉     var pushpin \= new PushPinControl("D");     MapLayer.AddChild(pushpin, new Location(29.5076372217973, 106.489384971208)); } ![复制代码](https://common.cnblogs.com/images/copycode.gif)        ![](https://images.cnblogs.com/cnblogs_com/beniao/BingMaps/PushpinDisplay.jpg)   或许会有人问道,如何动态的实现ToolPanel里显示的内容?要实现这个功能也是非常简单的,首先我们来分析下上面定义的样式里对ToolPanel部分的定义: ![复制代码](https://common.cnblogs.com/images/copycode.gif)                                                                                                                                                                      ![复制代码](https://common.cnblogs.com/images/copycode.gif)   使用了Border进行圆角布局,在其内部通过StackPanel垂直布局实现了ToolPanel的的标题和内容两大部分,内容部分并通过Border进行设计,取名为ContentBorder,这是为了实现动态呈现显示内容而设计,整体Border通过TranslateTransform进行一个偏移的处理,也就是让ToolPanel始终显示在图钉标注的右边相应的位置,详细见本文前面提供的截图。   从上面的实现知道ToolPanel是在鼠标的MouseEnter事件执行的时候才会显示的,当鼠标离开就会将其可见性设置为隐藏。了解了这个原理后就可以想到,是否可以从MouseEnter事件中通过某种方法去处理?事件是个非常强大的技术点,这里我们就可以通过事件,将显示内容的Border容器派发处理,通过事件的监听来动态更新其显示内容。首先需要扩展一个事件类: ![复制代码](https://common.cnblogs.com/images/copycode.gif) public delegate void PushpinContentChangedHandler(object sender,PushpinContentChangedEvantAgrs e);public class PushpinContentChangedEvantAgrs : EventArgs {    public Border ContentBorder { get; set; }public PushpinContentChangedEvantAgrs() { }public PushpinContentChangedEvantAgrs(Border ContentBorder)     {        this.ContentBorder \= ContentBorder;     } } ![复制代码](https://common.cnblogs.com/images/copycode.gif)      接下来为自定义的图钉标注类定义一个事件,事件类型就为上面所定义好的委托类型,传递的事件为PushpinContentChangedEventArgs. ![复制代码](https://common.cnblogs.com/images/copycode.gif) ///  /// 自定义图钉控件///  public class PushPinControl : ContentControl {    public event PushpinContentChangedHandler ContentChangedEvent; ![复制代码](https://common.cnblogs.com/images/copycode.gif)     现在需要做的就是在当前对象的MouseEnter事件中去触发这个事件,将样式里所定义好的用于显示内容的Border对象获取出来,通过事件参数传递派发出去,让事件监听段可以通过该事件获取事件参数进行ToolPanel内容的呈现更新。所有我们需要改写PushPinControl的OnApplyTemplate方法: ![复制代码](https://common.cnblogs.com/images/copycode.gif) public override void OnApplyTemplate() {    base.OnApplyTemplate();//从样式模板中获取TextBlock控件     var textBlock \= GetTemplateChild("txtTitle") as TextBlock;     textBlock.Text \= this.Text;    //从样式模板中获取ToolPanel(既Border布局实现的,详细见样式的定义)控件     var ToolPanelBorder \= GetTemplateChild("ToolPanel") as Border;    //获取内容Border对象,通过事件将该对象派发出去,在使用段可以通过监听这个事件而获取到内容Border     var ContentBorder \= GetTemplateChild("ContentBorder") as Border;    //通过事件动态控制ToolPanel的显示和隐藏     this.MouseEnter += (mes, meo) \=>     {          ToolPanelBorder.Visibility \= Visibility.Visible;          if (ContentChangedEvent != null)         {             ContentChangedEvent(this, new PushpinContentChangedEvantAgrs(ContentBorder));         }     };    this.MouseLeave += (mls, mlo) \=> { ToolPanelBorder.Visibility \= Visibility.Collapsed; }; } ![复制代码](https://common.cnblogs.com/images/copycode.gif)    通过ContentChangedEvent事件的监听动态的去改变ToolPanel里的显示内容就可以达到我们的目的了,比如我们动态的将ToolPanel里的显示内容更改为一张图片,上面使用自定义图钉标注的代码将变成下面这样的。 ![复制代码](https://common.cnblogs.com/images/copycode.gif) private void AddPushPin() {    //添加Bing Maps Silverlight Control自带的图钉    //var pushpin = new Pushpin();    //pushpin.Location = new Location(29.5076372217973, 106.489384971208);    //添加自定义的图钉     var pushpin \= new PushPinControl("D");     pushpin.ContentChangedEvent += (sender, o) \=>        {            //构造Image对象 用于显示在ToolPanel中             var image \= new Image { Width \= 100, Height \= 100, Margin \= new Thickness(8) };             image.Source \= new BitmapImage(new Uri("Resources/Smbecker.png", UriKind.RelativeOrAbsolute));             Border border \= o.ContentBorder;             border.Child  \= image;         };     MapLayer.AddChild(pushpin, new Location(29.5076372217973, 106.489384971208)); } ![复制代码](https://common.cnblogs.com/images/copycode.gif)         ![](https://images.cnblogs.com/cnblogs_com/beniao/BingMaps/ToolPanelImage.jpg)    之前有网友问到如何实现可拖拽的图钉标注控件,鉴于篇幅的原因这里就不做介绍,不清楚的朋友可以留言讨论。本篇就介绍到这里,欢迎大家前来交流、讨论、拍砖。   **更多学习资料:**   MSDN:[http://msdn.microsoft.com/en-us/library/ee681890.aspx](http://msdn.microsoft.com/en-us/library/ee681890.aspx)   官方:[http://www.microsoft.com/maps](http://www.microsoft.com/maps)   中国Bing Maps:[http://cn.bing.com/ditu/](http://cn.bing.com/ditu/)   官方SDK:[http://www.microsoft.com/maps/isdk/silverlight/](http://www.microsoft.com/maps/isdk/silverlight/)

RabbitMQ——Rabbit Message Queue的简写,但不能仅仅理解其为消息队列,消息代理更合适。RabbitMQ 是一个由 Erlang 语言开发的AMQP(高级消息队列协议)的开源实现,其内部结构如下:

RabbitMQ 内部结构

RabbitMQ作为一个消息代理,主要和消息打交道,负责接收并转发消息。RabbitMQ提供了可靠的消息机制、跟踪机制和灵活的消息路由,支持消息集群和分布式部署。适用于排队算法、秒杀活动、消息分发、异步处理、数据同步、处理耗时任务、CQRS等应用场景。

下面我们就来学习下RabbitMQ。

本文主要基于Windows下使用Vs Code 基于.net core进行demo演示。开始之前我们需要准备好以下环境。

  • 安装Erlang运行环境
    下载安装Erlang

  • 安装RabbitMQ
    下载安装Windows版本的RabbitMQ

  • 启动RabbitMQ Server
    点击Windows开始按钮,输入RabbitMQ找到RabbitMQ Comman Prompt,以管理员身份运行。

  • 依次执行以下命令启动RabbitMQ服务

    1
    2
    3
    rabbitmq-service install
    rabbitmq-service enable
    rabbitmq-service start
  • 执行rabbitmqlctl status检查RabbitMQ状态

  • 安装管理平台插件
    执行rabbitmq-plugins enable rabbitmq_management即可成功安装,使用默认账号密码(guest/guest)登录http://localhost:15672/即可。

在开始之前我们先来了解下消息模型:
消息流
消费者(consumer)订阅某个队列。生产者(producer)创建消息,然后发布到队列(queue)中,队列再将消息发送到监听的消费者。

下面我们我们通过demo来了解RabbitMQ的基本用法。

3.1.消息的发送和接收

创建RabbitMQ文件夹,打开命令提示符,分别创建两个控制台项目Send、Receive。

1
dotnet new console --name Send 
1
dotnet new console --name Receive 

我们先来添加消息发送端逻辑:

再来完善消息接收端逻辑:

先运行消息接收端,再运行消息发送端,结果如下图。

运行结果

从上面的代码中可以看出,发送端和消费端的代码前4步都是一样的。主要的区别在于发送端调用channel.BasicPublish方法发送消息;而接收端需要实例化一个EventingBasicConsumer实例来进行消息处理逻辑。另外一点需要注意的是:消息接收端和发送端的队列名称(queue)必须保持一致,这里指定的队列名称为hello。

3.2. 循环调度

使用工作队列的好处就是它能够并行的处理队列。如果堆积了很多任务,我们只需要添加更多的工作者(workers)就可以了。我们先启动两个接收端,等待消息接收,再启动一个发送端进行消息发送。

消息分发

我们增加运行一个消费端后的运行结果:

循环调度

从图中可知,我们循环发送4条信息,两个消息接收端按顺序被循环分配。
默认情况下,RabbitMQ将按顺序将每条消息发送给下一个消费者。平均每个消费者将获得相同数量的消息。这种分发消息的方式叫做循环(round-robin)。

3.3. 消息确认

按照我们上面的demo,一旦RabbitMQ将消息发送到消费端,消息就会立即从内存中移出,无论消费端是否处理完成。在这种情况下,消息就会丢失。

为了确保一个消息永远不会丢失,RabbitMQ支持消息确认(message acknowledgments)。当消费端接收消息并且处理完成后,会发送一个ack(消息确认)信号到RabbitMQ,RabbitMQ接收到这个信号后,就可以删除掉这条已经处理的消息任务。但如果消费端挂掉了(比如,通道关闭、连接丢失等)没有发送ack信号。RabbitMQ就会明白某个消息没有正常处理,RabbitMQ将会重新将消息入队,如果有另外一个消费端在线,就会快速的重新发送到另外一个消费端。

RabbitMQ中没有消息超时的概念,只有当消费端关闭或奔溃时,RabbitMQ才会重新分发消息。

微调下Receive中的代码逻辑:

主要改动的是将 autoAck:true修改为autoAck:fasle,以及在消息处理完毕后手动调用BasicAck方法进行手动消息确认。

从图中可知,消息发送端连续发送4条消息,其中消费端1先被分配处理第一条消息,消费端2被循环分配第二条消息,第三条消息由于没有空闲消费者仍然在队列中。
在消费端2未处理完第一条消息之前,手动中断(ctrl+c)。我们可以发现RabbitMQ在下一次分发时,会优先将被中断的消息分发给消费端1处理。

3.4. 消息持久化

消息确认确保了即使消费端异常,消息也不会丢失能够被重新分发处理。但是如果RabbitMQ服务端异常,消息依然会丢失。除非我们指定durable:true,否则当RabbitMQ退出或奔溃时,消息将依然会丢失。通过指定durable:true,并指定Persistent=true,来告知RabbitMQ将消息持久化。

将消息标记为持久性不能完全保证消息不会丢失。虽然它告诉RabbitMQ将消息保存到磁盘,但是当RabbitMQ接受消息并且还没有保存时,仍然有一个很短的时间窗口。RabbitMQ 可能只是将消息保存到了缓存中,并没有将其写入到磁盘上。持久化是不能够一定保证的,但是对于一个简单任务队列来说已经足够。如果需要确保消息队列的持久化,可以使用publisher confirms.

3.5. 公平分发

RabbitMQ的消息分发默认按照消费端的数量,按顺序循环分发。这样仅是确保了消费端被平均分发消息的数量,但却忽略了消费端的闲忙情况。这就可能出现某个消费端一直处理耗时任务处于阻塞状态,某个消费端一直处理一般任务处于空置状态,而只是它们分配的任务数量一样。

但我们可以通过channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
设置prefetchCount : 1来告知RabbitMQ,在未收到消费端的消息确认时,不再分发消息,也就确保了当消费端处于忙碌状态时,不再分配任务。

这时你需要注意的是如果所有的消费端都处于忙碌状态,你的队列可能会被塞满。你需要注意这一点,要么添加更多的消费端,要么采取其他策略。

细心的你也许发现上面的demo,生产者和消费者直接是通过相同队列名称进行匹配衔接的。消费者订阅某个队列,生产者创建消息发布到队列中,队列再将消息转发到订阅的消费者。这样就会有一个局限性,即消费者一次只能发送消息到某一个队列。

那消费者如何才能发送消息到多个消息队列呢?
RabbitMQ提供了Exchange,它类似于路由器的功能,它用于对消息进行路由,将消息发送到多个队列上。Exchange一方面从生产者接收消息,另一方面将消息推送到队列。但exchange必须知道如何处理接收到的消息,是将其附加到特定队列还是附加到多个队列,还是直接忽略。而这些规则由exchange type定义,exchange的原理如下图所示。
Exchange

常见的exchange type 有以下几种:

  • direct(明确的路由规则:消费端绑定的队列名称必须和消息发布时指定的路由名称一致)
  • topic (模式匹配的路由规则:支持通配符)
  • fanout (消息广播,将消息分发到exchange上绑定的所有队列上)

下面我们就来一一这介绍它们的用法。

4.1 fanout

本着先易后难的思想,我们先来了解下fanout的广播路由机制。fanout的路由机制如下图,即发送到 fanout 类型exchange的消息都会分发到所有绑定该exchange的队列上去。

fanout 路由机制

生产者示例代码:

消费者示例代码:

4.2. direct

direct相对于fanout就属于完全匹配、单播的模式,路由机制如下图,即队列名称和消息发送时指定的路由完全匹配时,消息才会发送到指定队列上。
direct路由机制

生产者示例代码:

消费者示例代码:

4.3. topic

topic是direct的升级版,是一种模式匹配的路由机制。它支持使用两种通配符来进行模式匹配:符号**#和符号*。其中***匹配一个单词, **#则表示匹配0个或多个单词,单词之间用.**分割。如下图所示。
topic路由机制

生产者示例代码:

消费者示例代码:

RPC——Remote Procedure Call,远程过程调用。
那RabbitMQ如何进行远程调用呢?示意图如下:
RPC机制
第一步,主要是进行远程调用的客户端需要指定接收远程回调的队列,并申明消费者监听此队列。
第二步,远程调用的服务端除了要申明消费端接收远程调用请求外,还要将结果发送到客户端用来监听的结果的队列中去。

远程调用客户端:

远程调用服务端:

基于上面的demo和对几种不同exchange路由机制的学习,我们发现RabbitMQ主要是涉及到以下几个核心概念:

  1. Publisher:生产者,消息的发送方。
  2. Connection:网络连接。
  3. Channel:信道,多路复用连接中的一条独立的双向数据流通道。
  4. Exchange:交换器(路由器),负责消息的路由到相应队列。
  5. Binding:队列与交换器间的关联绑定。消费者将关注的队列绑定到指定交换器上,以便Exchange能准确分发消息到指定队列。
  6. Queue:队列,消息的缓冲存储区。
  7. Virtual Host:虚拟主机,虚拟主机提供资源的逻辑分组和分离。包含连接,交换,队列,绑定,用户权限,策略等。
  8. Broker:消息队列的服务器实体。
  9. Consumer:消费者,消息的接收方。

AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(一) - 缘来爱宇宸 - 博客园

Excerpt

随着IOS7由之前UI的拟物化设计变为如今的扁平化设计,也许扁平化的时代要来了,当然我们是不是该吐槽一下,苹果什么时候也开始跟风了,自GOOGLE和微软界面扁平化过后,苹果也加入了这一队伍。 AvalonDock AvalonDock 是一个.NET库,用于在停靠模式布局(docking)中排列一系


随着IOS7由之前UI的拟物化设计变为如今的扁平化设计,也许扁平化的时代要来了,当然我们是不是该吐槽一下,苹果什么时候也开始跟风了,自GOOGLE和微软界面扁平化过后,苹果也加入了这一队伍。

 AvalonDock

   AvalonDock 是一个.NET库,用于在停靠模式布局(docking)中排列一系列WPF/WinForm控件。最新发布的版本原生支持MVVM框架、Aero Snap特效并具有更好的性能。

AvalonDock 2.0版本已经发布了,新版本是用MVVM框架重新编写,似乎也用了Command(命令)模式。2.0版的文档尚未发布,但你可以参考Avalon.TestApp 或者2.0版源码中的Avalon.MVVMTestApp文件夹来查看新的API。

这个库使用很简单——只需要用AvalonDock提供的控件包含你自己的控件,页面布局就立即变成可停靠的(dockable)。可以参考 入门 页面获取样例代码,了解不同控件的特性。当然你也可以在自己的C#代码中实例化或操作这些控件。2.0版本中,控件功能与以前一致,但控件名称已经改变,因此建议参考前述样例代码直至参考文档更新为止。

大名鼎鼎SharpEevelop也应用了AvalonDock,由于SharpEevelop的框架过于庞大,并且SharpEevelop里的AvalonDock 1.3的版本,并不支持MVVM的模式,所以就兴起了自己做一个插件式系统,当然也跟一下扁平化的风,目前框架已经做好并应用到个人项目中,本着开源的思想我会把框架搭建的过程,以及遇到的种种问题分享出来。

Caliburn.Micro    

Caliburn是Rob Eisenberg在2009年提出的一个开源框架,可以应用于WPF,Silverlight,WP7等,框架基于MVVM模式,像它的名字一样,是企业级应用的一把利器。

基于WPF的框架有很多,Prism,WAF等,每个框架都有自己侧重点,像Prism侧重于模块间的组合,WAF侧重于分层设计。通观CM的设计,它的一些想法如下: 1.ActionMessage,结合了Blend中的TriggerAction,可以把UI控件中的事件绑定到后台方法,类似于CallMethodAction。CM对ActionMessage进行了很多扩展,包括可以传入多个参数,参数支持绑定,可以通过CanExecute作执行前判断并设置控件的Enable等。

2.Conventions,协定,这个词听上去有点虚,其实就是智能匹配的意思。CM制定了一系列匹配的规则,比如说View和ViewModel之间的匹配,绑定时传入控件名可以找到控件,传入方法名可以绑定到方法等等。

3.Screen和Conductor,作为一个Presentation的框架,各个UI部件(Widget或者叫Pad)的管理是必不可少的。Screen就是用来表示UI部件的,它定义了一些列UI部件的生命期事件,比如Activated,DeActivated等。Conductor是用来管理Screen的,类似于传统的Controller,不同的Screen可以用一个Conductor来管理,Conductor也使用了策略模式允许更改对Screen的处理。

4.Coroutines,协同程序,定义了一组程序的执行,简化了异步编程。比如说在网络中下载图片并显示,通常来说需要显示BusyIndicator,后台线程去网络读取图片,读取成功后Invoke到UI线程,取消BusyIndicator,显示图片。CM提供了一个IResult接口,大大的简化了异步编程,结合ActionMessage,为AOP的扩展提供了可能。

5.配置性和扩展性,CM移除掉了原Caliburn的一些IOC实现,作为一个通用框架,最常用办法就是使用工厂模式结合配置文件提供可配置性,使用IOC来解耦组件间的依赖。CM默认是使用MEF来做IOC扩展的,你可以自定义Bootstrapper来使用你喜欢的IOC容器,如Unity等。

6.设计时支持(Design-time support),CM中的ActionMessage是继承自Blend中的TriggerAction的,也就是说可以在Blend编辑ActionMessage,大大方便了使用。 (这段摘抄了周永恒大大的部分对CM框架的解析,大家想详细了解的话可以去他的博客去学习,我就不仔细说明了,以后我会用到的地方做说明)

MahApps.Metro  

  这是一个Metro样式的开源项目,应用该项目可以使你的软件具有metro的风格,具体就不多说了。  

这是测试项目第一阶段的运行结果

言归正传,我们从零开始创建项目,下面是整个测试项目的结构:

 

 MefBootstrapper

这是启动加载类,一般我们WPF程序是从APP.XAML里StartupUri=“****WINDOWS.XAML”来启动主窗体,但现在由MefBootstrapper担当了启动窗体的职责:

复制代码

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MefBootstrapper : Bootstrapper&lt;IShell&gt;
{
private CompositionContainer container;

protected override void Configure()
{
/*CompositionContainer 对象在应用程序中有两种的主要用途。首先,它跟踪哪些部分可用于组合、它们的依赖项,并且充当任何指定组合的上下文。其次,它提供了应用程序可以启动组合的方法、获取组合部件的实例,或填充可组合部件的依存关系。
部件可直接用于容器,或通过 Catalog 属性来用于容器。在此 ComposablePartCatalog 中可发现的所有部件都可以供容器来满足导入,还包括直接添加的任何部件。*/
container = new CompositionContainer(
new AggregateCatalog(AssemblySource.Instance.Select(x =&gt; new AssemblyCatalog(x)))
);

var batch = new CompositionBatch();
var dockScreenManager = new DockScreenManager();
batch.AddExportedValue&lt;IWindowManager&gt;(new WindowManager());//將指定的导出加入至 CompositionBatch 物件
batch.AddExportedValue&lt;IDockScreenManager&gt;(dockScreenManager);
batch.AddExportedValue&lt;IEventAggregator&gt;(new EventAggregator());
batch.AddExportedValue(container);

container.Compose(batch);//在容器上执行组合,包括指定的 CompositionBatch 中的更改
}

protected override object GetInstance(Type serviceType, string key)
{
var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;//获取指定类型的规范协定名称
var exports = container.GetExportedValues&lt;object&gt;(contract);//返回具有从指定的类型参数派生的协定名称的已导出对象。如果不是正好有一个匹配的已导出对象,则将引发异常。

if (exports.Any())
return exports.First();

throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}

protected override IEnumerable&lt;object&gt; GetAllInstances(Type serviceType)
{
return container.GetExportedValues&lt;object&gt;(AttributedModelServices.GetContractName(serviceType));
}

protected override void BuildUp(object instance)
{
container.SatisfyImportsOnce(instance);//满足指定的 ComposablePart 对象的导入,而无需注册该对象以进行重新组合。
}
}

复制代码

复制代码

由上可知, MefBootstrapper继承与CM框架提供Bootstrapper,当Bootstrapper加载时,CM框架便会从MEF容器里寻找出TRootModel类型的实例,并且show出来,也就是我们的主窗体,之后我会把项目源码放出来,大家可以自己跟踪OnStartup事件。

我们来看看APP.XMAL

复制代码

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;Application x:Class="DemoApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DemoApplication"
&gt;
&lt;Application.Resources&gt;
&lt;ResourceDictionary&gt;
&lt;ResourceDictionary.MergedDictionaries&gt;
&lt;ResourceDictionary&gt;
&lt;local:MefBootstrapper x:Key="bootstrapper" /&gt;
&lt;/ResourceDictionary&gt;
&lt;/ResourceDictionary.MergedDictionaries&gt;
&lt;/ResourceDictionary&gt;
&lt;/Application.Resources&gt;
&lt;/Application&gt;

复制代码

复制代码

 IShell

  一个简单的接口,为了方便MEF导出部件

1
2
3
4
public interface IShell
{

}

ShellView.xaml

复制代码

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
&lt;MetrolControls:MetroWindow x:Class="DemoApplication.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MetrolControls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Title="ShellView" Height="500" Width="800"&gt;
&lt;Window.Resources&gt;
&lt;ResourceDictionary&gt;
&lt;ResourceDictionary.MergedDictionaries&gt;
&lt;ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colours.xaml"/&gt;
&lt;ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml"/&gt;
&lt;ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml"/&gt;
&lt;/ResourceDictionary.MergedDictionaries&gt;
&lt;/ResourceDictionary&gt;
&lt;/Window.Resources&gt;
&lt;Grid&gt;
&lt;ContentControl x:Name="DockContent" Margin="0,2,0,0" Grid.Row="0"/&gt;
&lt;/Grid&gt;
&lt;/MetrolControls:MetroWindow&gt;

复制代码

复制代码

  可以看到,这时我们引用了MahApps.Metro,MahApps.Metro自定义了一个WINDOWS,在我看来比传统的和谐那么一点,MahApps.Metro里还有10多种自定义控件,有兴趣的可以自己去研究

ShellViewModel

复制代码

1
2
3
4
5
6
[Export(typeof(IShell))]
class ShellViewModel:IShell
{
[Import]
public IScreen DockContent { get; set; }
}

复制代码

   一个ShellView.xaml对应一个ShellViewModel,当ShellViewModel标记为Export时,Bootstrapper会把当前程序集所有标记为Export的类导入CM框架的IOC容器里,ShellViewModel相当于ShellView的Datacontext,一个View的加载过程为,由Model找到(CM框架定义了各种查找规则)View,并把Model绑定到View的Datacontext,以后我们UI的逻辑代码就可以写在Model里面,并与UI完全分开,这就是我们所说的MVVM模式。上面也有一个典型的View绑定Model里的属性,细心的可以看到:

 [Import]
  public IScreen DockContent { get; set; }

  该属性的名称和ShellView.xaml里的 的命名完全一样,奇怪的是我们并没有写任何绑定,但DockContent是怎么绑定到View里面的呢,其实绑定的过程已经由CM框架帮我们做了,CM框架会帮助我们把Model里和控件名称一样的属性绑定在一起,这就然我们省了一些事,这只是CM框架的一些小特性。 

  好了,主窗体的说完了,下面我们来看看怎么把AvalonDock融合进去,上面我们说过了,一个Model对应一个View,所以我们要显示一个UserControl时得生成一对Model-View,

DockView.xaml

复制代码

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
&lt;UserControl x:Class="DemoApplication.Views.DockView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:avalonDock="http://avalondock.codeplex.com"
d:DesignHeight="300" d:DesignWidth="800"&gt;
&lt;UserControl.Resources&gt;
&lt;avalonDock:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/&gt;
&lt;/UserControl.Resources&gt;
&lt;Grid&gt;
&lt;Grid x:Name="layoutRoot"&gt;
&lt;avalonDock:DockingManager Grid.Row="1" x:Name="dockManager" AllowMixedOrientation="True" &gt;
&lt;avalonDock:DockingManager.Theme&gt;
&lt;avalonDock:MetroTheme/&gt;
&lt;/avalonDock:DockingManager.Theme&gt;
&lt;avalonDock:DockingManager.LayoutItemTemplate&gt;
&lt;DataTemplate&gt;
&lt;ContentControl IsTabStop="False" /&gt;
&lt;/DataTemplate&gt;
&lt;/avalonDock:DockingManager.LayoutItemTemplate&gt;
&lt;avalonDock:LayoutRoot&gt;
&lt;avalonDock:LayoutPanel Orientation="Horizontal" &gt;
&lt;avalonDock:LayoutAnchorablePaneGroup DockWidth="200" Orientation="Vertical" &gt;
&lt;avalonDock:LayoutAnchorablePane &gt;
&lt;avalonDock:LayoutAnchorable Title="left" &gt;&lt;/avalonDock:LayoutAnchorable&gt;
&lt;/avalonDock:LayoutAnchorablePane&gt;
&lt;/avalonDock:LayoutAnchorablePaneGroup&gt;
&lt;avalonDock:LayoutPanel Orientation="Vertical" &gt;
&lt;avalonDock:LayoutDocumentPaneGroup Orientation="Horizontal"&gt;
&lt;avalonDock:LayoutDocumentPane &gt;
&lt;avalonDock:LayoutDocument Title="main"&gt;&lt;/avalonDock:LayoutDocument&gt;
&lt;/avalonDock:LayoutDocumentPane&gt;

&lt;/avalonDock:LayoutDocumentPaneGroup&gt;
&lt;avalonDock:LayoutAnchorablePaneGroup DockHeight="100" Orientation="Horizontal" &gt;
&lt;avalonDock:LayoutAnchorablePane &gt;
&lt;avalonDock:LayoutAnchorable Title="bottom" &gt;&lt;/avalonDock:LayoutAnchorable&gt;
&lt;/avalonDock:LayoutAnchorablePane&gt;
&lt;/avalonDock:LayoutAnchorablePaneGroup&gt;
&lt;/avalonDock:LayoutPanel&gt;
&lt;avalonDock:LayoutAnchorablePaneGroup DockWidth="200" Orientation="Horizontal" &gt;
&lt;avalonDock:LayoutAnchorablePane &gt;
&lt;avalonDock:LayoutAnchorable Title="Right" &gt;&lt;/avalonDock:LayoutAnchorable&gt;
&lt;/avalonDock:LayoutAnchorablePane&gt;
&lt;/avalonDock:LayoutAnchorablePaneGroup&gt;
&lt;/avalonDock:LayoutPanel&gt;

&lt;/avalonDock:LayoutRoot&gt;
&lt;/avalonDock:DockingManager&gt;
&lt;/Grid&gt;
&lt;/Grid&gt;
&lt;/UserControl&gt;

复制代码

复制代码

这是VS2012设计器的显示


这些东西的学习周期还是有的,我就不一一去说。有些东西只可意会不可言传。

DockViewModel

1
2
3
4
5
[Export(typeof(IScreen))]
public class DockViewModel : Screen
{

}

  我们可以看到ShellViewModel里的DockContent就是IScreen类型的,由于标记为Import,所以程序会自动帮我们把MEF容器里IScreen类型注入,所以其实DockContent就是DockView,我这里为了方便直接用了CM框架的IScreen,如果有两个类标记为[Export(typeof(IScreen))],就会导致程序异常,因为有两个实例。程序不知道该导出哪个,所以我们之后会定义另一个接口,该接口只有唯一一个类即唯一的DockViewModel标记为导出,因为我们DockView就是唯一的,导入和导出部件这是MEF的知识,MEF是什么大家可以百度学习,CM框架默认是MEF作为容器。

MEF

  Managed Extensibility Framework(MEF)是.NET平台下的一个扩展性管理框架,它是一系列特性的集合,包括依赖注入(DI)以及Duck Typing等。MEF为开发人员提供了一个工具,让我们可以轻松的对应用程序进行扩展并且对已有的代码产生最小的影响,开发人员在开发过程中根据功能要求定义一些扩展点,之后扩展人员就可以使用这些扩展点与应用程序交互;同时MEF让应用程序与扩展程序之间不产生直接的依赖,这样也允许在多个具有同样的扩展需求之间共享扩展程序

第一阶段就先这样,以后我会慢慢更新,直道整个插件系统的完成

如果您看了本篇博客,觉得对您有所收获,请点击右下角的 [推荐]

如果您想转载本博客,请注明出处

如果您对本文有意见或者建议,欢迎留言

感谢您的阅读,请关注我的后续博客

作者:Zengg 出处:http://www.cnblogs.com/01codeworld/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

AvalonDock 2.0 的简单运用 - yangxw - 博客园

Excerpt

最近在研究AvalonDock的一些使用,碰到了一些问题。现在拿出来跟大家分享分享。 网上找了一大把AvalonDock 1.3版本的资料,弄出Demo后发现属性面板(DockableContent)设置成浮动后不能停靠其它的面板。最后只得试试AvalonDock 2.0版本的,还好2.0版本没让我


     最近在研究AvalonDock的一些使用,碰到了一些问题。现在拿出来跟大家分享分享。

     网上找了一大把AvalonDock 1.3版本的资料,弄出Demo后发现属性面板(DockableContent)设置成浮动后不能停靠其它的面板。最后只得试试AvalonDock 2.0版本的,还好2.0版本没让我们失望。

     首先需要库文件:Xceed.Wpf.AvalonDock,若需要Aero或VS2010主题效果需引用Xceed.Wpf.AvalonDock.Themes.Aero或Xceed.Wpf.AvalonDock.Themes.VS2010,当然还有其它漂亮的主题可以在官网(http://avalondock.codeplex.com/)自行下载.

     首先需要在xaml文件头部引入这些dll(当然在此之前您还得把前面说到的库文件引入项目):

1
xmlns:avalonDock="http://schemas.xceed.com/wpf/xaml/avalondock"

     xaml布局代码:

View Code

效果:

 

avalonDock:LayoutAnchorablePaneavalonDock:LayoutDocumentPane我分别把它们叫做属性布局面板和文档布局面板。属性布局面板的特性是停靠在面板的边缘时可以设置自动隐藏,而且属性布局面板也可以作为一个可停靠面板供其它的属性布局面板停靠。文档布局面板只可以漂浮和停靠在主布局面板中,不可以停靠在属性布局面板里面。

 avalonDock:LayoutAnchorablePaneavalonDock:LayoutDocumentPane布局面板需要位于avalonDock:LayoutPanel布局面板里面,若有需要设置多个属性布局面板和文档布局面板置于一个面板组中可以分别使用avalonDock:LayoutAnchorablePaneGroup和 avalonDock:LayoutDocumentPaneGroup

      正在承载我们内容控件的是avalonDock:LayoutAnchorableavalonDock:LayoutDocument,我们可以在它们里面添加我们自己的内容。它们只能放在avalonDock:LayoutAnchorablePaneavalonDock:LayoutDocumentPane里面,看自己的需求可以自己组合使用。

  布局就到这里了,接下来让我们看看如何保存和恢复布局,AvalonDock 2.0提供了相应的操作类XmlLayoutSerializer,需要添加引用using Xceed.Wpf.AvalonDock.Layout.Serialization;

     cs代码:

1
2
3
4
<span>//</span><span>保存布局</span>
<span>var</span> serializer = <span>new</span><span> XmlLayoutSerializer(DManager);
</span><span>using</span> (<span>var</span> stream = <span>new</span> StreamWriter(<span>"</span><span>lay.txt</span><span>"</span><span>))
serializer.Serialize(stream);</span>
1
2
3
4
<span>//</span><span>恢复布局</span>
<span>var</span> serializer = <span>new</span><span> XmlLayoutSerializer(DManager);
</span><span>using</span> (<span>var</span> stream = <span>new</span> StreamReader(<span>"</span><span>lay.txt</span><span>"</span><span>))
serializer.Deserialize(stream);</span>

保存布局会把当前的布局(DManager)保存在文件“lay.txt”中,恢复布局会把“lay.txt”中的布局内容恢复到界面(DManager)中。这里有点必须注意,使用这个功能时必须为每个内容面板添加ContentId属性,如 <avalonDock:LayoutAnchorable Title=”left1” AutoHideWidth=”50” ContentId=”left1”>,否则将不能恢复。

       分享就到这里结束了,其实AvalonDock还有许多其它方面的功能,比如可以使用avalonDock:DockingManager.AnchorableHeaderTemplate为属性内容界面的头部自定义内容等等,水平有限,内容可能存在纰漏之处还请见谅。

    源代码下载

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811

812

813

814

815

816

817

818

819

820

821

822

823

824

825

826

827

828

829

830

831

832

833

834

835

836

837

838

839

840

841

842

843

844

845

846

847

848

849

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

867

868

869

870

871

872

873

874

875

876

877

878

879

880

881

882

883

884

885

886

887

888

889

890

891

892

893

894

895

896

897

898

899

900

901

902

903

904

905

906

907

908

909

910

911

912

913

914

915

916

917

918

919

920

921

922

923

924

925

926

927

928

929

930

931

932

933

934

935

936

937

938

939

940

941

942

943

944

945

946

947

948

949

950

951

952

953

954

955

956

957

958

959

960

961

962

963

964

965

966

967

968

969

970

971

972

973

974

975

976

977

978

979

980

981

982

983

984

985

986

987

988

989

990

991

992

993

994

995

996

997

998

999

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

1012

1013

1014

1015

1016

1017

1018

1019

1020

1021

1022

1023

1024

1025

1026

1027

1028

1029

1030

1031

1032

1033

1034

1035

1036

1037

1038

1039

1040

1041

1042

1043

1044

1045

1046

1047

1048

1049

1050

1051

1052

1053

1054

1055

1056

1057

1058

1059

1060

1061

1062

1063

1064

1065

1066

1067

1068

1069

1070

1071

1072

1073

1074

1075

1076

1077

1078

1079

1080

1081

1082

1083

1084

1085

1086

1087

1088

1089

1090

1091

1092

1093

1094

1095

1096

1097

1098

1099

1100

1101

1102

1103

1104

1105

1106

1107

1108

1109

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1125

1126

1127

1128

1129

1130

1131

1132

1133

1134

1135

1136

1137

1138

1139

1140

1141

1142

1143

1144

1145

1146

1147

1148

1149

1150

1151

1152

1153

1154

1155

1156

1157

1158

1159

1160

1161

1162

1163

1164

1165

1166

1167

1168

1169

1170

1171

1172

1173

1174

1175

1176

1177

1178

1179

1180

1181

1182

1183

1184

1185

1186

1187

1188

1189

1190

1191

1192

1193

1194

1195

1196

1197

1198

1199

1200

1201

1202

1203

1204

1205

1206

1207

1208

1209

1210

1211

1212

1213

1214

1215

1216

1217

1218

1219

1220

1221

1222

1223

1224

1225

1226

1227

1228

1229

1230

1231

1232

1233

1234

1235

1236

1237

1238

1239

1240

1241

1242

1243

1244

1245

1246

1247

1248

1249

1250

1251

1252

1253

1254

1255

1256

1257

1258

1259

1260

1261

1262

1263

1264

1265

1266

1267

1268

1269

1270

1271

1272

1273

1274

1275

1276

1277

1278

1279

1280

1281

1282

1283

1284

1285

1286

1287

1288

1289

1290

1291

1292

1293

1294

1295

1296

1297

1298

1299

1300

1301

1302

1303

1304

1305

1306

1307

1308

1309

1310

1311

1312

1313

1314

1315

1316

1317

1318

1319

1320

1321

1322

1323

1324

1325

1326

1327

1328

1329

1330

1331

1332

1333

1334

1335

1336

1337

1338

1339

1340

1341

1342

1343

1344

1345

1346

1347

1348

1349

1350

1351

1352

1353

1354

1355

1356

1357

1358

1359

1360

1361

1362

1363

1364

1365

1366

1367

1368

1369

1370

1371

1372

1373

1374

1375

1376

1377

1378

1379

1380

1381

1382

1383

1384

1385

1386

1387

1388

1389

1390

1391

1392

1393

1394

1395

1396

1397

1398

1399

1400

1401

1402

1403

1404

1405

1406

1407

1408

1409

1410

1411

1412

1413

1414

1415

1416

1417

1418

1419

1420

1421

1422

1423

1424

1425

1426

1427

1428

1429

1430

1431

1432

1433

1434

1435

1436

1437

1438

1439

1440

1441

1442

1443

1444

1445

1446

1447

1448

1449

1450

1451

1452

1453

1454

1455

1456

1457

1458

1459

1460

1461

1462

1463

1464

1465

1466

1467

1468

1469

1470

1471

1472

1473

1474

1475

1476

1477

1478

1479

1480

1481

1482

1483

1484

1485

1486

1487

1488

1489

1490

1491

1492

1493

1494

1495

1496

1497

1498

1499

1500

1501

1502

1503

1504

1505

1506

1507

1508

1509

1510

1511

1512

1513

1514

1515

1516

1517

1518

1519

1520

1521

1522

1523

1524

1525

1526

1527

1528

1529

1530

1531

1532

1533

1534

1535

1536

1537

1538

1539

1540

1541

1542

1543

1544

1545

1546

1547

1548

1549

1550

1551

1552

1553

1554

1555

1556

1557

1558

1559

1560

1561

1562

1563

1564

1565

1566

1567

1568

1569

1570

1571

1572

1573

1574

1575

1576

1577

1578

1579

1580

1581

1582

1583

1584

1585

1586

1587

1588

1589

1590

1591

1592

1593

1594

1595

1596

1597

1598

1599

1600

1601

1602

1603

1604

1605

1606

1607

1608

1609

1610

1611

1612

1613

1614

1615

1616

1617

1618

1619

1620

1621

1622

1623

1624

1625

1626

1627

1628

1629

1630

1631

1632

1633

1634

1635

1636

1637

1638

1639

1640

1641

1642

1643

1644

1645

1646

1647

1648

1649

1650

1651

1652

1653

1654

1655

1656

1657

1658

1659

1660

1661

1662

1663

1664

1665

1666

1667

1668

1669

1670

1671

1672

1673

1674

1675

1676

1677

1678

1679

1680

1681

1682

1683

1684

1685

1686

1687

1688

1689

1690

1691

1692

1693

1694

1695

1696

1697

1698

1699

1700

1701

1702

1703

1704

1705

1706

1707

1708

1709

1710

1711

1712

1713

1714

1715

1716

1717

1718

1719

1720

1721

1722

1723

1724

1725

1726

1727

1728

1729

1730

1731

1732

1733

1734

1735

1736

1737

1738

1739

1740

1741

1742

1743

1744

1745

1746

1747

1748

1749

1750

1751

1752

1753

1754

1755

1756

1757

1758

1759

1760

1761

1762

1763

1764

1765

1766

1767

1768

1769

1770

1771

1772

1773

1774

1775

1776

1777

1778

1779

1780

1781

1782

1783

1784

1785

1786

1787

1788

1789

1790

1791

1792

1793

1794

1795

1796

1797

1798

1799

1800

1801

1802

1803

1804

1805

1806

1807

1808

1809

1810

1811

1812

1813

1814

1815

1816

1817

1818

1819

1820

1821

1822

1823

1824

1825

1826

1827

1828

1829

1830

1831

1832

1833

1834

1835

1836

1837

1838

1839

1840

1841

1842

1843

1844

1845

1846

1847

1848

1849

1850

1851

1852

1853

1854

1855

1856

1857

1858

1859

1860

1861

1862

1863

1864

1865

1866

1867

1868

1869

1870

1871

1872

1873

1874

1875

1876

1877

1878

1879

1880

1881

1882

1883

1884

1885

1886

1887

1888

1889

1890

1891

1892

1893

1894

1895

1896

1897

1898

1899

1900

1901

1902

1903

1904

1905

1906

1907

1908

1909

1910

1911

1912

1913

1914

1915

1916

1917

1918

1919

1920

1921

1922

1923

1924

1925

1926

1927

1928

1929

1930

1931

1932

1933

1934

1935

1936

1937

1938

1939

1940

1941

1942

1943

1944

1945

1946

1947

1948

1949

1950

1951

1952

1953

1954

1955

1956

1957

1958

1959

1960

1961

1962

1963

1964

1965

1966

1967

1968

1969

1970

1971

1972

1973

1974

1975

1976

1977

1978

1979

1980

1981

1982

1983

1984

1985

1986

1987

1988

1989

1990

1991

1992

1993

1994

1995

1996

1997

1998

1999

2000

2001

2002

2003

2004

2005

2006

2007

2008

2009

2010

2011

2012

2013

2014

2015

2016

2017

2018

2019

2020

2021

2022

2023

2024

2025

2026

2027

2028

2029

2030

2031

2032

2033

2034

2035

2036

2037

2038

2039

2040

2041

2042

2043

2044

2045

2046

2047

2048

2049

2050

2051

2052

2053

2054

2055

2056

2057

2058

2059

2060

2061

2062

2063

2064

2065

2066

2067

2068

2069

2070

2071

2072

2073

2074

2075

2076

2077

2078

2079

2080

2081

2082

2083

2084

2085

2086

2087

2088

2089

2090

2091

2092

2093

2094

2095

2096

2097

2098

2099

2100

2101

2102

2103

2104

2105

2106

2107

2108

2109

2110

2111

2112

2113

2114

2115

2116

2117

2118

2119

2120

2121

2122

2123

2124

2125

2126

2127

2128

2129

2130

2131

2132

2133

2134

2135

2136

2137

2138

2139

2140

2141

2142

2143

2144

2145

2146

2147

2148

2149

2150

2151

2152

2153

2154

2155

2156

2157

2158

2159

2160

2161

2162

2163

2164

2165

2166

2167

2168

2169

2170

2171

2172

2173

2174

2175

2176

2177

2178

2179

2180

2181

2182

2183

2184

2185

2186

2187

2188

2189

2190

2191

2192

2193

2194

2195

2196

2197

2198

2199

2200

2201

2202

2203

2204

2205

2206

2207

2208

2209

2210

2211

2212

2213

2214

2215

2216

2217

2218

2219

2220

2221

2222

2223

2224

2225

2226

2227

2228

2229

2230

2231

2232

2233

2234

2235

2236

2237

2238

2239

2240

2241

2242

2243

2244

2245

2246

2247

2248

2249

2250

2251

2252

2253

2254

2255

2256

2257

2258

2259

2260

2261

2262

2263

2264

2265

2266

2267

2268

2269

2270

2271

2272

2273

2274

2275

2276

2277

2278

2279

2280

2281

2282

2283

2284

2285

2286

2287

2288

2289

2290

2291

2292

2293

2294

2295

2296

2297

2298

2299

2300

2301

2302

2303

2304

2305

2306

2307

2308

2309

2310

2311

2312

2313

2314

2315

2316

2317

2318

2319

2320

2321

2322

2323

2324

2325

2326

2327

2328

2329

2330

2331

2332

2333

2334

2335

2336

2337

2338

2339

2340

2341

2342

2343

2344

2345

2346

2347

2348

2349

2350

2351

2352

2353

2354

2355

2356

2357

2358

2359

2360

2361

2362

2363

2364

2365

2366

2367

2368

2369

2370

2371

2372

2373

2374

2375

2376

2377

2378

2379

2380

2381

2382

2383

2384

2385

2386

2387

2388

2389

2390

2391

2392

2393

2394

2395

2396

2397

2398

2399

2400

2401

2402

2403

2404

2405

2406

2407

2408

2409

2410

2411

2412

2413

2414

2415

2416

2417

2418

2419

2420

2421

2422

2423

2424

2425

2426

2427

2428

2429

2430

2431

2432

2433

2434

2435

2436

2437

2438

2439

2440

2441

2442

2443

2444

2445

2446

2447

2448

2449

2450

2451

2452

2453

2454

2455

2456

2457

2458

2459

2460

2461

2462

2463

2464

2465

2466

2467

2468

2469

2470

2471

2472

2473

2474

2475

2476

2477

2478

2479

2480

2481

2482

2483

2484

2485

2486

2487

2488

2489

2490

2491

2492

2493

2494

2495

2496

2497

2498

2499

2500

2501

2502

2503

2504

2505

2506

2507

2508

2509

2510

2511

2512

2513

2514

2515

2516

2517

2518

2519

2520

2521

2522

2523

2524

2525

2526

2527

2528

2529

2530

2531

2532

2533

2534

2535

2536

2537

2538

2539

2540

2541

2542

2543

2544

2545

2546

2547

2548

2549

2550

2551

2552

2553

2554

2555

2556

2557

2558

2559

2560

2561

2562

2563

2564

2565

2566

2567

2568

2569

2570

2571

2572

2573

2574

2575

2576

2577

2578

2579

2580

2581

2582

2583

2584

2585

2586

2587

2588

2589

2590

2591

2592

2593

2594

2595

2596

2597

2598

2599

2600

2601

2602

2603

2604

2605

2606

2607

2608

2609

2610

2611

2612

2613

2614

2615

2616

2617

2618

2619

2620

2621

2622

2623

2624

2625

2626

2627

2628

2629

2630

2631

2632

2633

2634

2635

2636

2637

2638

2639

2640

2641

2642

2643

2644

2645

2646

2647

2648

2649

2650

2651

2652

2653

2654

2655

2656

2657

2658

2659

2660

2661

2662

2663

2664

2665

2666

2667

2668

2669

2670

2671

2672

2673

2674

2675

2676

2677

2678

2679

2680

2681

2682

2683

2684

2685

2686

2687

2688

2689

2690

2691

2692

2693

2694

2695

2696

2697

2698

2699

2700

2701

2702

2703

2704

2705

2706

2707

2708

2709

2710

2711

2712

2713

2714

2715

2716

2717

2718

2719

2720

2721

2722

2723

2724

2725

2726

2727

2728

2729

2730

2731

2732

2733

2734

2735

2736

2737

2738

2739

2740

2741

2742

2743

2744

2745

2746

2747

2748

2749

2750

2751

2752

2753

2754

2755

2756

2757

2758

2759

2760

2761

2762

2763

2764

2765

2766

2767

2768

2769

2770

2771

2772

2773

2774

2775

2776

2777

2778

2779

2780

2781

2782

2783

2784

2785

2786

2787

2788

2789

2790

2791

2792

2793

2794

2795

2796

2797

2798

2799

2800

2801

2802

2803

2804

2805

2806

2807

2808

2809

2810

2811

2812

2813

2814

2815

2816

2817

2818

2819

2820

2821

2822

2823

2824

2825

2826

2827

2828

2829

2830

2831

2832

2833

2834

2835

2836

2837

2838

2839

2840

2841

2842

2843

2844

2845

2846

2847

2848

2849

2850

2851

2852

2853

2854

2855

2856

2857

2858

2859

2860

2861

2862

2863

2864

2865

2866

2867

2868

2869

2870

2871

2872

2873

2874

2875

2876

2877

2878

2879

2880

2881

2882

2883

2884

2885

2886

2887

2888

2889

2890

2891

2892

2893

2894

2895

2896

2897

2898

2899

2900

2901

2902

2903

2904

2905

2906

2907

2908

2909

2910

2911

2912

2913

2914

2915

2916

2917

2918

2919

2920

2921

2922

2923

2924

2925

2926

2927

2928

2929

2930

2931

2932

2933

2934

2935

2936

2937

2938

2939

2940

2941

2942

2943

2944

2945

2946

2947

2948

2949

2950

2951

2952

2953

2954

2955

2956

2957

2958

2959

2960

2961

2962

2963

2964

2965

{

"__inputs"``: [],

"__requires"``: [

{

"type"``: "grafana"``,

"id"``: "grafana"``,

"name"``: "Grafana"``,

"version"``: "4.2.0"

},

{

"type"``: "panel"``,

"id"``: "grafana-piechart-panel"``,

"name"``: "Pie Chart"``,

"version"``: "1.1.4"

},

{

"type"``: "panel"``,

"id"``: "graph"``,

"name"``: "Graph"``,

"version"``: ""

},

{

"type"``: "panel"``,

"id"``: "singlestat"``,

"name"``: "Singlestat"``,

"version"``: ""

},

{

"type"``: "panel"``,

"id"``: "table"``,

"name"``: "Table"``,

"version"``: ""

}

],

"annotations"``: {

"list"``: []

},

"description"``: "Dashboard to visualize metrics captured by App Metrics ASP.NET Core Middleware 1.2.0, tested with App.Metrics.Extensions.Reporting.InfluxDB 1.2.0  - http://app-metrics.io/"``,

"editable"``: true``,

"gnetId"``: 2125,

"graphTooltip"``: 1,

"hideControls"``: false``,

"id"``: null``,

"links"``: [],

"refresh"``: "5s"``,

"rows"``: [

{

"collapse"``: true``,

"height"``: "250"``,

"panels"``: [

{

"cacheTimeout"``: null``,

"colorBackground"``: false``,

"colorValue"``: false``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"format"``: "rpm"``,

"gauge"``: {

"maxValue"``: 100,

"minValue"``: 0,

"show"``: false``,

"thresholdLabels"``: false``,

"thresholdMarkers"``: true

},

"id"``: 8,

"interval"``: ""``,

"links"``: [],

"mappingType"``: 1,

"mappingTypes"``: [

{

"name"``: "value to text"``,

"value"``: 1

},

{

"name"``: "range to text"``,

"value"``: 2

}

],

"maxDataPoints"``: 100,

"nullPointMode"``: "connected"``,

"nullText"``: null``,

"postfix"``: ""``,

"postfixFontSize"``: "50%"``,

"prefix"``: ""``,

"prefixFontSize"``: "50%"``,

"rangeMaps"``: [

{

"from"``: "null"``,

"text"``: "N/A"``,

"to"``: "null"

}

],

"span"``: 2,

"sparkline"``: {

"fillColor"``: "rgba(31, 118, 189, 0.18)"``,

"full"``: true``,

"lineColor"``: "rgb(31, 120, 193)"``,

"show"``: true

},

"targets"``: [

{

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__transactions"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"rate1m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: ""``,

"title"``: "Throughput"``,

"type"``: "singlestat"``,

"valueFontSize"``: "80%"``,

"valueMaps"``: [

{

"op"``: "="``,

"text"``: "N/A"``,

"value"``: "null"

}

],

"valueName"``: "current"

},

{

"cacheTimeout"``: null``,

"colorBackground"``: false``,

"colorValue"``: false``,

"colors"``: [

"rgba(50, 172, 45, 0.97)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(245, 54, 54, 0.9)"

],

"datasource"``: "$datasource"``,

"decimals"``: 4,

"editable"``: true``,

"error"``: false``,

"format"``: "percent"``,

"gauge"``: {

"maxValue"``: 100,

"minValue"``: 0,

"show"``: false``,

"thresholdLabels"``: false``,

"thresholdMarkers"``: true

},

"id"``: 6,

"interval"``: null``,

"links"``: [],

"mappingType"``: 1,

"mappingTypes"``: [

{

"name"``: "value to text"``,

"value"``: 1

},

{

"name"``: "range to text"``,

"value"``: 2

}

],

"maxDataPoints"``: 100,

"nullPointMode"``: "connected"``,

"nullText"``: null``,

"postfix"``: ""``,

"postfixFontSize"``: "50%"``,

"prefix"``: ""``,

"prefixFontSize"``: "50%"``,

"rangeMaps"``: [

{

"from"``: ""``,

"text"``: ""``,

"to"``: ""

}

],

"span"``: 2,

"sparkline"``: {

"fillColor"``: "rgba(31, 118, 189, 0.18)"``,

"full"``: true``,

"lineColor"``: "rgb(31, 120, 193)"``,

"show"``: true

},

"targets"``: [

{

"dsType"``: "influxdb"``,

"groupBy"``: [],

"measurement"``: "application.httprequests__one_minute_error_percentage_rate"``,

"policy"``: "default"``,

"query"``: "SELECT  \"value\" FROM \"application.httprequests__percentage_error_requests\" WHERE $timeFilter"``,

"rawQuery"``: false``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: ""``,

"title"``: "Error %"``,

"type"``: "singlestat"``,

"valueFontSize"``: "80%"``,

"valueMaps"``: [

{

"op"``: "="``,

"text"``: "0%"``,

"value"``: "null"

}

],

"valueName"``: "current"

},

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 2,

"id"``: 13,

"interval"``: "$summarize"``,

"legend"``: {

"avg"``: false``,

"current"``: false``,

"max"``: false``,

"min"``: false``,

"show"``: false``,

"total"``: false``,

"values"``: false

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 4,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: ""``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__active"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Active Requests"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"aliasColors"``: {

"application.httprequests__apdex.last"``: "#6ED0E0"

},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"height"``: ""``,

"id"``: 7,

"interval"``: "$summarize"``,

"legend"``: {

"avg"``: false``,

"current"``: false``,

"max"``: false``,

"min"``: false``,

"show"``: false``,

"total"``: false``,

"values"``: false

},

"lines"``: true``,

"linewidth"``: 3,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 4,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__apdex"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"score"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [

{

"colorMode"``: "critical"``,

"fill"``: true``,

"line"``: true``,

"op"``: "lt"``,

"value"``: 0.5

},

{

"colorMode"``: "warning"``,

"fill"``: true``,

"line"``: true``,

"op"``: "gt"``,

"value"``: 0.5

},

{

"colorMode"``: "ok"``,

"fill"``: true``,

"line"``: true``,

"op"``: "gt"``,

"value"``: 0.75

}

],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Apdex score"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "short"``,

"label"``: "apdex"``,

"logBase"``: 1,

"max"``: "1"``,

"min"``: "0"``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: false

}

]

},

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"height"``: "350"``,

"id"``: 1,

"interval"``: "$summarize"``,

"legend"``: {

"avg"``: false``,

"current"``: true``,

"max"``: false``,

"min"``: false``,

"show"``: true``,

"total"``: false``,

"values"``: true

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: "$col"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__transactions"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"rate1m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"1 min rate"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"rate5m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"5 min rate"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"rate15m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"15 min rate"

],

"type"``: "alias"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Throughput"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "rpm"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"height"``: "350"``,

"id"``: 2,

"interval"``: "$summarize"``,

"legend"``: {

"alignAsTable"``: false``,

"avg"``: false``,

"current"``: false``,

"max"``: false``,

"min"``: false``,

"rightSide"``: false``,

"show"``: true``,

"total"``: false``,

"values"``: false

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: "$col"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__transactions"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"p95"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"95th Percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"p98"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"98th Percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"p99"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"99th Percentile"

],

"type"``: "alias"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Response Time"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "ms"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"height"``: ""``,

"id"``: 9,

"interval"``: "$summarize"``,

"legend"``: {

"alignAsTable"``: true``,

"avg"``: false``,

"current"``: true``,

"max"``: false``,

"min"``: false``,

"rightSide"``: true``,

"show"``: false``,

"total"``: false``,

"values"``: true

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: ""``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__one_minute_error_percentage_rate"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Error Rate %"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "percent"``,

"label"``: null``,

"logBase"``: 1,

"max"``: "100"``,

"min"``: "0"``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"decimals"``: 2,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"height"``: "250px"``,

"id"``: 3,

"interval"``: "$summarize"``,

"legend"``: {

"alignAsTable"``: true``,

"avg"``: false``,

"current"``: true``,

"max"``: false``,

"min"``: false``,

"rightSide"``: true``,

"show"``: true``,

"total"``: false``,

"values"``: true

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: "$col"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__error_rate"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"rate1m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"1min rate"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"rate5m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"5min rate"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"rate15m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"15min rate"

],

"type"``: "alias"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Error Rate"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 2,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "rpm"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"aliasColors"``: {},

"cacheTimeout"``: null``,

"combine"``: {

"label"``: "Others"``,

"threshold"``: 0

},

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fontSize"``: "80%"``,

"format"``: "percent"``,

"height"``: "250px"``,

"id"``: 4,

"interval"``: ""``,

"legend"``: {

"percentage"``: true``,

"show"``: true``,

"sort"``: null``,

"sortDesc"``: null``,

"values"``: true

},

"legendType"``: "Right side"``,

"links"``: [],

"maxDataPoints"``: 3,

"nullPointMode"``: "connected"``,

"pieType"``: "pie"``,

"span"``: 5,

"strokeWidth"``: 1,

"targets"``: [

{

"alias"``: "$tag_http_status_code"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"http_status_code"

],

"type"``: "tag"

}

],

"measurement"``: "application.httprequests__errors"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "sum"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"title"``: "Errors"``,

"type"``: "grafana-piechart-panel"``,

"valueName"``: "current"

},

{

"columns"``: [

{

"text"``: "Total"``,

"value"``: "total"

}

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"filterNull"``: true``,

"fontSize"``: "100%"``,

"id"``: 24,

"interval"``: ""``,

"links"``: [],

"pageSize"``: 20,

"scroll"``: true``,

"showHeader"``: true``,

"sort"``: {

"col"``: 1,

"desc"``: true

},

"span"``: 7,

"styles"``: [

{

"dateFormat"``: "YYYY-MM-DD HH:mm:ss"``,

"pattern"``: "Time"``,

"type"``: "date"

},

{

"colorMode"``: null``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"decimals"``: 0,

"pattern"``: "/.*/"``,

"thresholds"``: [],

"type"``: "number"``,

"unit"``: "none"

}

],

"targets"``: [

{

"alias"``: "$tag_exception"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"exception"

],

"type"``: "tag"

}

],

"measurement"``: "application.httprequests__exceptions"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"title"``: "Uncaught Exceptions Thrown"``,

"transform"``: "timeseries_aggregations"``,

"type"``: "table"

}

],

"repeat"``: null``,

"repeatIteration"``: null``,

"repeatRowId"``: null``,

"showTitle"``: true``,

"title"``: "Overview"``,

"titleSize"``: "h6"

},

{

"collapse"``: false``,

"height"``: "300"``,

"panels"``: [

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"height"``: "350"``,

"id"``: 16,

"interval"``: "$summarize"``,

"legend"``: {

"alignAsTable"``: true``,

"avg"``: false``,

"current"``: false``,

"max"``: false``,

"min"``: false``,

"rightSide"``: true``,

"show"``: true``,

"sort"``: "current"``,

"sortDesc"``: true``,

"total"``: false``,

"values"``: false

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: true``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: "$tag_route"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"route"

],

"type"``: "tag"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__transactions_per_endpoint"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"rate1m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Throughput / Endpoint"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 2,

"value_type"``: "individual"

},

"transparent"``: false``,

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "rpm"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"height"``: "350"``,

"id"``: 17,

"interval"``: "$summarize"``,

"legend"``: {

"alignAsTable"``: true``,

"avg"``: false``,

"current"``: false``,

"max"``: false``,

"min"``: false``,

"rightSide"``: true``,

"show"``: true``,

"total"``: false``,

"values"``: false

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: "$tag_route"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"route"

],

"type"``: "tag"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__transactions_per_endpoint"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"p95"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"95th Percentile"

],

"type"``: "alias"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Response Time / Endpoint"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "ms"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"columns"``: [

{

"text"``: "Current"``,

"value"``: "current"

}

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"filterNull"``: false``,

"fontSize"``: "100%"``,

"id"``: 10,

"interval"``: ""``,

"links"``: [],

"pageSize"``: null``,

"scroll"``: true``,

"showHeader"``: true``,

"sort"``: {

"col"``: 1,

"desc"``: true

},

"span"``: 6,

"styles"``: [

{

"dateFormat"``: "YYYY-MM-DD HH:mm:ss"``,

"pattern"``: "Time"``,

"type"``: "date"

},

{

"colorMode"``: null``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"decimals"``: 2,

"pattern"``: "/.*/"``,

"thresholds"``: [],

"type"``: "number"``,

"unit"``: "ms"

}

],

"targets"``: [

{

"alias"``: "$tag_route"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"route"

],

"type"``: "tag"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__transactions_per_endpoint"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"p95"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"title"``: "Response Times / Endpoint"``,

"transform"``: "timeseries_aggregations"``,

"type"``: "table"

},

{

"columns"``: [

{

"text"``: "Current"``,

"value"``: "current"

}

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"filterNull"``: false``,

"fontSize"``: "100%"``,

"id"``: 12,

"interval"``: ""``,

"links"``: [],

"pageSize"``: null``,

"scroll"``: true``,

"showHeader"``: true``,

"sort"``: {

"col"``: 1,

"desc"``: true

},

"span"``: 6,

"styles"``: [

{

"dateFormat"``: "YYYY-MM-DD HH:mm:ss"``,

"pattern"``: "Time"``,

"type"``: "date"

},

{

"colorMode"``: null``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"decimals"``: 2,

"pattern"``: "/.*/"``,

"thresholds"``: [],

"type"``: "number"``,

"unit"``: "rpm"

}

],

"targets"``: [

{

"alias"``: "$tag_route"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"route"

],

"type"``: "tag"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__transactions_per_endpoint"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"rate1m"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"title"``: "Throughput / Endpoint"``,

"transform"``: "timeseries_aggregations"``,

"type"``: "table"

},

{

"columns"``: [

{

"text"``: "Current"``,

"value"``: "current"

}

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"filterNull"``: false``,

"fontSize"``: "100%"``,

"id"``: 11,

"interval"``: ""``,

"links"``: [],

"pageSize"``: null``,

"scroll"``: true``,

"showHeader"``: true``,

"sort"``: {

"col"``: null``,

"desc"``: false

},

"span"``: 6,

"styles"``: [

{

"dateFormat"``: "YYYY-MM-DD HH:mm:ss"``,

"pattern"``: "Time"``,

"type"``: "date"

},

{

"colorMode"``: null``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"decimals"``: 0,

"pattern"``: "/.*/"``,

"thresholds"``: [],

"type"``: "number"``,

"unit"``: "percent"

}

],

"targets"``: [

{

"alias"``: "$tag_route"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"route"

],

"type"``: "tag"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__one_minute_error_percentage_rate_per_endpoint"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"title"``: "Error Request Percentage / Endpoint"``,

"transform"``: "timeseries_aggregations"``,

"type"``: "table"

},

{

"columns"``: [

{

"text"``: "Total"``,

"value"``: "total"

}

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"filterNull"``: false``,

"fontSize"``: "100%"``,

"id"``: 25,

"interval"``: ""``,

"links"``: [],

"pageSize"``: null``,

"scroll"``: true``,

"showHeader"``: true``,

"sort"``: {

"col"``: 1,

"desc"``: true

},

"span"``: 6,

"styles"``: [

{

"dateFormat"``: "YYYY-MM-DD HH:mm:ss"``,

"pattern"``: "Time"``,

"type"``: "date"

},

{

"colorMode"``: null``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"decimals"``: 0,

"pattern"``: "/.*/"``,

"thresholds"``: [],

"type"``: "number"``,

"unit"``: "none"

}

],

"targets"``: [

{

"alias"``: "$tag_route [$tag_exception]"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"route"

],

"type"``: "tag"

},

{

"params"``: [

"exception"

],

"type"``: "tag"

}

],

"measurement"``: "application.httprequests__exceptions"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"title"``: "Uncaught Exceptions Thrown / Endpoint"``,

"transform"``: "timeseries_aggregations"``,

"type"``: "table"

}

],

"repeat"``: null``,

"repeatIteration"``: null``,

"repeatRowId"``: null``,

"showTitle"``: true``,

"title"``: "Endpoints"``,

"titleSize"``: "h6"

},

{

"collapse"``: false``,

"height"``: "250"``,

"panels"``: [

{

"columns"``: [

{

"text"``: "Current"``,

"value"``: "current"

}

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"filterNull"``: false``,

"fontSize"``: "100%"``,

"hideTimeOverride"``: true``,

"id"``: 22,

"interval"``: ""``,

"links"``: [],

"pageSize"``: null``,

"scroll"``: true``,

"showHeader"``: true``,

"sort"``: {

"col"``: 0,

"desc"``: true

},

"span"``: 9,

"styles"``: [

{

"dateFormat"``: "YYYY-MM-DD HH:mm:ss"``,

"pattern"``: "Time"``,

"type"``: "date"

},

{

"colorMode"``: "row"``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"decimals"``: 1,

"pattern"``: "/.*/"``,

"thresholds"``: [

"0.5"``,

"1"

],

"type"``: "number"``,

"unit"``: "short"

}

],

"targets"``: [

{

"alias"``: "$tag_health_check_name"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"health_check_name"

],

"type"``: "tag"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.health__results"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"timeFrom"``: null``,

"title"``: "Results"``,

"transform"``: "timeseries_aggregations"``,

"transparent"``: true``,

"type"``: "table"

},

{

"cacheTimeout"``: null``,

"colorBackground"``: true``,

"colorValue"``: false``,

"colors"``: [

"rgba(245, 54, 54, 0.9)"``,

"rgba(237, 129, 40, 0.89)"``,

"rgba(50, 172, 45, 0.97)"

],

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"format"``: "none"``,

"gauge"``: {

"maxValue"``: 100,

"minValue"``: 0,

"show"``: false``,

"thresholdLabels"``: false``,

"thresholdMarkers"``: true

},

"hideTimeOverride"``: true``,

"id"``: 19,

"interval"``: null``,

"links"``: [

{

"type"``: "dashboard"

}

],

"mappingType"``: 2,

"mappingTypes"``: [

{

"name"``: "value to text"``,

"value"``: 1

},

{

"name"``: "range to text"``,

"value"``: 2

}

],

"maxDataPoints"``: 100,

"nullPointMode"``: "connected"``,

"nullText"``: null``,

"postfix"``: ""``,

"postfixFontSize"``: "50%"``,

"prefix"``: ""``,

"prefixFontSize"``: "50%"``,

"rangeMaps"``: [

{

"from"``: "0"``,

"text"``: "Unhealthy"``,

"to"``: "0.49"

},

{

"from"``: "0.5"``,

"text"``: "Degraded"``,

"to"``: "0.9"

},

{

"from"``: "1.0"``,

"text"``: "Healthy"``,

"to"``: "2.0"

}

],

"span"``: 3,

"sparkline"``: {

"fillColor"``: "rgba(31, 118, 189, 0.18)"``,

"full"``: false``,

"lineColor"``: "rgb(31, 120, 193)"``,

"show"``: false

},

"targets"``: [

{

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.health__score"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"value"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

}

]

],

"tags"``: [

{

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: "0.5,1"``,

"timeFrom"``: null``,

"title"``: ""``,

"transparent"``: true``,

"type"``: "singlestat"``,

"valueFontSize"``: "80%"``,

"valueMaps"``: [

{

"op"``: "="``,

"text"``: "Unhealthy"``,

"value"``: "0"

},

{

"op"``: "="``,

"text"``: "Degraded"``,

"value"``: "0.5"

},

{

"op"``: "="``,

"text"``: "Healthy"``,

"value"``: "1.0"

}

],

"valueName"``: "current"

}

],

"repeat"``: null``,

"repeatIteration"``: null``,

"repeatRowId"``: null``,

"showTitle"``: true``,

"title"``: "Health"``,

"titleSize"``: "h6"

},

{

"collapse"``: false``,

"height"``: "300"``,

"panels"``: [

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"id"``: 14,

"interval"``: "$summarize"``,

"legend"``: {

"alignAsTable"``: true``,

"avg"``: false``,

"current"``: true``,

"hideEmpty"``: false``,

"max"``: false``,

"min"``: false``,

"rightSide"``: true``,

"show"``: true``,

"total"``: false``,

"values"``: true

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: "$col"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__post_size"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"p95"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"95th percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"p98"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"98th percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"p99"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"99th percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"last"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "median"

},

{

"params"``: [

"median"

],

"type"``: "alias"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Post Request Size"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "decbytes"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

},

{

"aliasColors"``: {},

"bars"``: false``,

"datasource"``: "$datasource"``,

"editable"``: true``,

"error"``: false``,

"fill"``: 1,

"id"``: 15,

"interval"``: "$summarize"``,

"legend"``: {

"alignAsTable"``: true``,

"avg"``: false``,

"current"``: true``,

"max"``: false``,

"min"``: false``,

"rightSide"``: true``,

"show"``: true``,

"total"``: false``,

"values"``: true

},

"lines"``: true``,

"linewidth"``: 1,

"links"``: [],

"nullPointMode"``: "connected"``,

"percentage"``: false``,

"pointradius"``: 5,

"points"``: false``,

"renderer"``: "flot"``,

"seriesOverrides"``: [],

"span"``: 6,

"stack"``: false``,

"steppedLine"``: false``,

"targets"``: [

{

"alias"``: "$col"``,

"dsType"``: "influxdb"``,

"groupBy"``: [

{

"params"``: [

"$interval"

],

"type"``: "time"

},

{

"params"``: [

"null"

],

"type"``: "fill"

}

],

"measurement"``: "application.httprequests__put_size"``,

"policy"``: "default"``,

"refId"``: "A"``,

"resultFormat"``: "time_series"``,

"select"``: [

[

{

"params"``: [

"p95"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"95th percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"p98"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"98th percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"p99"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "last"

},

{

"params"``: [

"99th percentile"

],

"type"``: "alias"

}

],

[

{

"params"``: [

"median"

],

"type"``: "field"

},

{

"params"``: [],

"type"``: "median"

},

{

"params"``: [

"median"

],

"type"``: "alias"

}

]

],

"tags"``: [

{

"key"``: "app"``,

"operator"``: "=~"``,

"value"``: "/^$application$/"

},

{

"condition"``: "AND"``,

"key"``: "env"``,

"operator"``: "=~"``,

"value"``: "/^$environment$/"

},

{

"condition"``: "AND"``,

"key"``: "server"``,

"operator"``: "=~"``,

"value"``: "/^$server$/"

}

]

}

],

"thresholds"``: [],

"timeFrom"``: null``,

"timeShift"``: null``,

"title"``: "Put Request Size"``,

"tooltip"``: {

"msResolution"``: false``,

"shared"``: true``,

"sort"``: 0,

"value_type"``: "individual"

},

"type"``: "graph"``,

"xaxis"``: {

"mode"``: "time"``,

"name"``: null``,

"show"``: true``,

"values"``: []

},

"yaxes"``: [

{

"format"``: "bytes"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

},

{

"format"``: "short"``,

"label"``: null``,

"logBase"``: 1,

"max"``: null``,

"min"``: null``,

"show"``: true

}

]

}

],

"repeat"``: null``,

"repeatIteration"``: null``,

"repeatRowId"``: null``,

"showTitle"``: true``,

"title"``: "PUT & POST Request Size"``,

"titleSize"``: "h6"

}

],

"schemaVersion"``: 14,

"style"``: "dark"``,

"tags"``: [

"influxdb"

],

"templating"``: {

"list"``: [

{

"allValue"``: null``,

"current"``: {},

"datasource"``: "$datasource"``,

"hide"``: 0,

"includeAll"``: false``,

"label"``: null``,

"multi"``: false``,

"name"``: "environment"``,

"options"``: [],

"query"``: "SHOW TAG VALUES WITH KEY = \"env\""``,

"refresh"``: 1,

"regex"``: ""``,

"sort"``: 1,

"tagValuesQuery"``: null``,

"tags"``: [],

"tagsQuery"``: null``,

"type"``: "query"``,

"useTags"``: false

},

{

"allValue"``: null``,

"current"``: {},

"datasource"``: "$datasource"``,

"hide"``: 0,

"includeAll"``: false``,

"label"``: null``,

"multi"``: false``,

"name"``: "application"``,

"options"``: [],

"query"``: "SHOW TAG VALUES WITH KEY = \"app\""``,

"refresh"``: 1,

"regex"``: ""``,

"sort"``: 1,

"tagValuesQuery"``: null``,

"tags"``: [],

"tagsQuery"``: null``,

"type"``: "query"``,

"useTags"``: false

},

{

"current"``: {

"text"``: "AppMetricsSandbox"``,

"value"``: "AppMetricsSandbox"

},

"hide"``: 0,

"label"``: null``,

"name"``: "datasource"``,

"options"``: [],

"query"``: "influxdb"``,

"refresh"``: 1,

"regex"``: ""``,

"type"``: "datasource"

},

{

"auto"``: false``,

"auto_count"``: 30,

"auto_min"``: "10s"``,

"current"``: {

"text"``: "5s"``,

"value"``: "5s"

},

"hide"``: 0,

"label"``: null``,

"name"``: "summarize"``,

"options"``: [

{

"selected"``: true``,

"text"``: "5s"``,

"value"``: "5s"

},

{

"selected"``: false``,

"text"``: "10s"``,

"value"``: "10s"

},

{

"selected"``: false``,

"text"``: "30s"``,

"value"``: "30s"

},

{

"selected"``: false``,

"text"``: "1m"``,

"value"``: "1m"

},

{

"selected"``: false``,

"text"``: "10m"``,

"value"``: "10m"

},

{

"selected"``: false``,

"text"``: "30m"``,

"value"``: "30m"

},

{

"selected"``: false``,

"text"``: "1h"``,

"value"``: "1h"

},

{

"selected"``: false``,

"text"``: "6h"``,

"value"``: "6h"

},

{

"selected"``: false``,

"text"``: "12h"``,

"value"``: "12h"

},

{

"selected"``: false``,

"text"``: "1d"``,

"value"``: "1d"

},

{

"selected"``: false``,

"text"``: "7d"``,

"value"``: "7d"

},

{

"selected"``: false``,

"text"``: "14d"``,

"value"``: "14d"

},

{

"selected"``: false``,

"text"``: "30d"``,

"value"``: "30d"

}

],

"query"``: "5s,10s,30s,1m,10m,30m,1h,6h,12h,1d,7d,14d,30d"``,

"refresh"``: 2,

"type"``: "interval"

},

{

"allValue"``: null``,

"current"``: {},

"datasource"``: "$datasource"``,

"hide"``: 0,

"includeAll"``: true``,

"label"``: null``,

"multi"``: true``,

"name"``: "server"``,

"options"``: [],

"query"``: "SHOW TAG VALUES WITH KEY = \"server\""``,

"refresh"``: 1,

"regex"``: ""``,

"sort"``: 0,

"tagValuesQuery"``: ""``,

"tags"``: [],

"tagsQuery"``: ""``,

"type"``: "query"``,

"useTags"``: false

}

]

},

"time"``: {

"from"``: "now-5m"``,

"to"``: "now"

},

"timepicker"``: {

"refresh_intervals"``: [

"5s"``,

"10s"``,

"30s"``,

"1m"``,

"5m"``,

"15m"``,

"30m"``,

"1h"``,

"2h"``,

"1d"

],

"time_options"``: [

"5m"``,

"15m"``,

"1h"``,

"6h"``,

"12h"``,

"24h"``,

"2d"``,

"7d"``,

"30d"

]

},

"timezone"``: "browser"``,

"title"``: "App Metrics - Web Monitoring - InfluxDB"``,

"version"``: 21

}

AutoMapper完成Dto与Model的转换 - kuangkro - 博客园

Excerpt

在实际的软件开发项目中,我们的“业务逻辑”常常需要我们对同样的数据进行各种变换。例如,一个Web应用通过前端收集用户的输入成为Dto,然后将Dto转换成领域模型并持久化到数据库中。相反,当用户请求数据时,我们又需要做相反的工作:将从数据库中查询出来的领域模型以相反的方式转换成Dto再呈现给用户。有时


在实际的软件开发项目中,我们的“业务逻辑”常常需要我们对同样的数据进行各种变换。

例如,一个Web应用通过前端收集用户的输入成为Dto,然后将Dto转换成领域模型并持久化到数据库中。相反,当用户请求数据时,我们又需要做相反的工作:将从数据库中查询出来的领域模型以相反的方式转换成Dto再呈现给用户。

有时候我们还会面临更多的数据使用需求,例如有多个数据使用的客户端,每个客户端都有自己对数据结构的不同需求,而这也需要我们进行更多的数据转换。 
频繁的数据转换琐碎而又凌乱,很多时候我们不得不做: 
(1)在两个类型几乎只是名字不同而结构大体相似,却只能以手工的、逐个属性赋值的方式实现数据在类型间的“传递”。 
(2)每遇到一个新的数据转换场景就手动实现一套转换逻辑,导致数据转换操作重复而又分散到应用的各个角落。 
如果有这样一个“变形金刚”般的工具,把“橘子”变成我们想要的“苹果”,而我们需要做的只是定义好转换规则——做我们真正的业务逻辑,或者甚至在简单场景下连规则都不需要定义(Convention Over Configuration),那将会是非常美好的事情。事实上在.NET中我们不用重复发明轮子,因为我们有——AutoMapper,一个强大的Object-Object Mapping工具。 
好吧,我承认自己有一点小小的激动,事实上我所做的项目正在经历以上的“困惑”,而AutoMapper确实带给我眼前一亮的感觉。因此我花了一点周末休息时间小小尝试了一把AutoMapper,通过做小的应用场景实现Dto到领域模型的映射,确实感觉到了它的“强大气场”。我将在文章中分享自己的使用心得,希望能给同样处于困惑中的你带来一点帮助。完整的项目代码我会在晚一些时候发布到自己的git repository中,欢迎大家自由参考使用。

【一】 将Model转换为Dto

先来看看我所”虚拟“的领域模型。这一次我定义了一个书店(BookStore):

1
<span>   1:  </span>    <span>public</span> <span>class</span> BookStore
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Name { get; set; }
1
<span>   4:  </span>        <span>public</span> List&lt;Book&gt; Books { get; set; }
1
<span>   5:  </span>        <span>public</span> Address Address { get; set; }
1
<span>   6:  </span>    }

书店有自己的地址(Address):

1
<span>   1:  </span>    <span>public</span> <span>class</span> Address
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Country { get; set; }
1
<span>   4:  </span>        <span>public</span> <span>string</span> City { get; set; }
1
<span>   5:  </span>        <span>public</span> <span>string</span> Street { get; set; }
1
<span>   6:  </span>        <span>public</span> <span>string</span> PostCode { get; set; }
1
<span>   7:  </span>    }

同时书店里放了n本书(Book):

1
<span>   1:  </span>    <span>public</span> <span>class</span> Book
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Title { get; set; }
1
<span>   4:  </span>        <span>public</span> <span>string</span> Description { get; set; }
1
<span>   5:  </span>        <span>public</span> <span>string</span> Language { get; set; }
1
<span>   6:  </span>        <span>public</span> <span>decimal</span> Price { get; set; }
1
<span>   7:  </span>        <span>public</span> List&lt;Author&gt; Authors { get; set; }
1
<span>   8:  </span>        <span>public</span> DateTime? PublishDate { get; set; }
1
<span>   9:  </span>        <span>public</span> Publisher Publisher { get; set; }
1
<span>  10:  </span>        <span>public</span> <span>int</span>? Paperback { get; set; }
1
<span>  11:  </span>    }

每本书都有出版商信息(Publisher):

1
<span>   1:  </span>    <span>public</span> <span>class</span> Publisher
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Name { get; set; }
1
<span>   4:  </span>    }

每本书可以有最多2个作者的信息(Author):

1
<span>   1:  </span>    <span>public</span> <span>class</span> Author
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Name { get; set; }
1
<span>   4:  </span>        <span>public</span> <span>string</span> Description { get; set; }
1
<span>   5:  </span>        <span>public</span> ContactInfo ContactInfo { get; set; }
1
<span>   6:  </span>    }

每个作者都有自己的联系方式(ContactInfo):

1
<span>   1:  </span>    <span>public</span> <span>class</span> ContactInfo
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Email { get; set; }
1
<span>   4:  </span>        <span>public</span> <span>string</span> Blog { get; set; }
1
<span>   5:  </span>        <span>public</span> <span>string</span> Twitter { get; set; }
1
<span>   6:  </span>    }

差不多就是这样了,一个有着层级结构的领域模型。 
再来看看我们的Dto结构。 
在Dto中我们有与BookStore对应的BookStoreDto:

1
<span>   1:  </span>    <span>public</span> <span>class</span> BookStoreDto
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Name { get; set; }
1
<span>   4:  </span>        <span>public</span> List&lt;BookDto&gt; Books { get; set; }
1
<span>   5:  </span>        <span>public</span> AddressDto Address { get; set; }
1
<span>   6:  </span>    }

其中包含与Address对应的AddressDto:

1
<span>   1:  </span>    <span>public</span> <span>class</span> AddressDto
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Country { get; set; }
1
<span>   4:  </span>        <span>public</span> <span>string</span> City { get; set; }
1
<span>   5:  </span>        <span>public</span> <span>string</span> Street { get; set; }
1
<span>   6:  </span>        <span>public</span> <span>string</span> PostCode { get; set; }
1
<span>   7:  </span>    }

以及与Book相对应的BookDto:

1
<span>   1:  </span>    <span>public</span> <span>class</span> BookDto
1
<span>   2:  </span>    {
1
<span>   3:  </span>        <span>public</span> <span>string</span> Title { get; set; }
1
<span>   4:  </span>        <span>public</span> <span>string</span> Description { get; set; }
1
<span>   5:  </span>        <span>public</span> <span>string</span> Language { get; set; }
1
<span>   6:  </span>        <span>public</span> <span>decimal</span> Price { get; set; }
1
<span>   7:  </span>        <span>public</span> DateTime? PublishDate { get; set; }
1
<span>   8:  </span>        <span>public</span> <span>string</span> Publisher { get; set; }
1
<span>   9:  </span>        <span>public</span> <span>int</span>? Paperback { get; set; }
1
<span>  10:  </span>        <span>public</span> <span>string</span> FirstAuthorName { get; set; }
1
<span>  11:  </span>        <span>public</span> <span>string</span> FirstAuthorDescription { get; set; }
1
<span>  12:  </span>        <span>public</span> <span>string</span> FirstAuthorEmail { get; set; }
1
<span>  13:  </span>        <span>public</span> <span>string</span> FirstAuthorBlog { get; set; }
1
<span>  14:  </span>        <span>public</span> <span>string</span> FirstAuthorTwitter { get; set; }
1
<span>  15:  </span>        <span>public</span> <span>string</span> SecondAuthorName { get; set; }
1
<span>  16:  </span>        <span>public</span> <span>string</span> SecondAuthorDescription { get; set; }
1
<span>  17:  </span>        <span>public</span> <span>string</span> SecondAuthorEmail { get; set; }
1
<span>  18:  </span>        <span>public</span> <span>string</span> SecondAuthorBlog { get; set; }
1
<span>  19:  </span>        <span>public</span> <span>string</span> SecondAuthorTwitter { get; set; }
1
<span>  20:  </span>    }

注意到我们的BookDto”拉平了“整个Book的层级结构,一个BookDto里携带了Book及其所有Author、Publisher等所有模式的数据。 
正好我们来看一下Dto到Model的映射规则。 
(1)BookStoreDto –> BookStore

BookStoreDto中的字段BookStore中的字段
NameName
BooksBooks
AddressAddress

(2)AddressDto –> Address

AddressDto中的字段Address中的字段
CountryCountry
CityCity
StreetStreet
PostCodePostCode

(3)BookDto -> Book。 
BookDto中的一些基本字段可以直接对应到Book中的字段。

BookDto中的字段Book中的字段
TitleTitle
DescriptionDescription
LanguageLanguage
PricePrice
PublishDatePublishDate
PaperbackPaperback

每本书至多有2个作者,在BookDto中分别使用”First“前缀和”Second“前缀的字段来表示。因此,所有FirstXXX字段都将映射成Book的Authors中的第1个Author对象,而所有SecondXXX字段则将映射成Authors中的第2个Author对象。

BookDto中的字段Book中的Authors中的第1个Author对象中的字段
FirstAuthorNameName
FirstAuthorDescriptionDescription
FirstAuthorEmailContactInfo.Email
FirstAuthorBlogContactInfo.Blog
FirstAuthorTwitterContactInfo.Twitter

注意上表中的ContactInfo.Email表示对应到Author对象的ContactInfo的Email字段,依次类推。类似的我们有:

BookDto中的字段Book中的Authors中的第2个Author对象中的字段
SecondAuthorNameName
SecondAuthorDescriptionDescription
SecondAuthorEmailContactInfo.Email
SecondAuthorBlogContactInfo.Blog
SecondAuthorTwitterContactInfo.Twitter

最后还有Publisher字段,它将对应到一个独立的Publisher对象。

BookDto中的字段Publisher中的字段
PublisherName

差不多就是这样了,我们的需求是要实现这一大坨Dto到另一大坨的Model之间的数据转换。

【二】将Dto转换为Model

1,以Convention方式实现零配置的对象映射

我们的AddressDto和Address结构完全一致,且字段名也完全相同。对于这样的类型转换,AutoMapper为我们提供了Convention,正如它的官网上所说的:

引用

AutoMapper uses a convention-based matching algorithm to match up source to destination values.

我们要做的只是将要映射的两个类型告诉AutoMapper(调用Mapper类的Static方法CreateMap并传入要映射的类型):

C#代码

    Mapper.CreateMap<AddressDto, Address>(); 

然后就可以交给AutoMapper帮我们搞定一切了:

1
<span>   1:  </span>            AddressDto dto = <span>new</span> AddressDto
1
<span>   2:  </span>            {
1
<span>   3:  </span>                Country = <span>"China"</span>,
1
<span>   4:  </span>                City = <span>"Beijing"</span>,
1
<span>   5:  </span>                Street = <span>"Dongzhimen Street"</span>,
1
<span>   6:  </span>                PostCode = <span>"100001"</span>
1
<span>   7:  </span>            };
1
<span>   8:  </span>            Address address = Mapper.Map&lt;AddressDto,Address&gt;(Dto);
1
<span>   9:  </span>            address.Country.ShouldEqual(<span>"China"</span>);
1
<span>  10:  </span>            address.City.ShouldEqual(<span>"Beijing"</span>);
1
<span>  11:  </span>            address.Street.ShouldEqual(<span>"Dongzhimen Street"</span>);
1
<span>  12:  </span>            address.PostCode.ShouldEqual(<span>"100001"</span>);

如果AddressDto中有值为空的属性,AutoMapper在映射的时候会把Address中的相应属性也置为空:

1
<span>   1:  </span>            Address address = Mapper.Map&lt;AddressDto,Address&gt;(<span>new</span> AddressDto
1
<span>   2:  </span>                                                                   {
1
<span>   3:  </span>                                                                       Country = <span>"China"</span>
1
<span>   4:  </span>                                                                   });
1
<span>   5:  </span>            address.City.ShouldBeNull();
1
<span>   6:  </span>            address.Street.ShouldBeNull();
1
<span>   7:  </span>            address.PostCode.ShouldBeNull();

甚至如果传入一个空的AddressDto,AutoMapper也会帮我们得到一个空的Address对象。

1
<span>   1:  </span>            Address address = Mapper.Map&lt;AddressDto,Address&gt;(<span>null</span>);
1
<span>   2:  </span>            address.ShouldBeNull();

千万不要把这种Convention的映射方式当成“玩具”,它在映射具有相同字段名的复杂类型的时候还是具有相当大的威力的。 
例如,考虑我们的BookStoreDto到BookStore的映射,两者的字段名称完全相同,只是字段的类型不一致。如果我们定义好了BookDto到Book的映射规则,再加上上述Convention方式的AddressDto到Address的映射,就可以用“零配置”实现BookStoreDto到BookStore的映射了:

C#代码

1
<span>   1:  </span>            IMappingExpression&lt;BookDto, Book&gt; expression = Mapper.CreateMap&lt;BookDto,Book&gt;();
1
<span>   2:  </span>            <span>// Define mapping rules from BookDto to Book here</span>
1
<span>   3:  </span>            Mapper.CreateMap&lt;AddressDto, Address&gt;();
1
<span>   4:  </span>            Mapper.CreateMap&lt;BookStoreDto, BookStore&gt;();

然后我们就可以直接转换BookStoreDto了:

1
<span>   1:  </span>            BookStoreDto dto = <span>new</span> BookStoreDto
1
<span>   4:  </span>                                       Address = <span>new</span> AddressDto
1
<span>   8:  </span>                                       Books = <span>new</span> List&lt;BookDto&gt;
1
<span>  10:  </span>                                                       <span>new</span> BookDto {Title = <span>"RESTful Web Service"</span>},
1
<span>  11:  </span>                                                       <span>new</span> BookDto {Title = <span>"Ruby for Rails"</span>},
1
<span>  14:  </span>            BookStore bookStore = Mapper.Map&lt;BookStoreDto,BookStore&gt;(dto);
1
<span>  15:  </span>            bookStore.Name.ShouldEqual(<span>"My Store"</span>);
1
<span>  16:  </span>            bookStore.Address.City.ShouldEqual(<span>"Beijing"</span>);
1
<span>  17:  </span>            bookStore.Books.Count.ShouldEqual(2);
1
<span>  18:  </span>            bookStore.Books.First().Title.ShouldEqual(<span>"RESTful Web Service"</span>);
1
<span>  19:  </span>            bookStore.Books.Last().Title.ShouldEqual(<span>"Ruby for Rails"</span>);

以下为自己补充:

(Begin)—————————————————————

1, 要实现BookDto到Book之间的转换还是有一断路需要走的因为他嵌套了相应的子类型如:Publisher ->ContactInfo,

Author。废话少说,直接上答案:

1
<span>   1:  </span>var exp = Mapper.CreateMap&lt;BookDto, Book&gt;(); 
1
<span>   2:  </span>exp.ForMember(bok=&gt; bok.Publisher<span>/*(变量)*/</span>,
1
<span>   3:  </span> (map) =&gt; map.MapFrom(dto=&gt;<span>new</span> Publisher(){Name= dto.Publisher<span>/*(DTO的变量)*/</span>}));
1
一般在我们写完规则之后通常会调用
1
2
<span>         //该方法主要用来检查还有那些规则没有写完。
</span><span>Mapper</span>.AssertConfigurationIsValid();

参见:http://stackoverflow.com/questions/4928487/how-to-automap-thismapping-sub-members

其它的就以此类推。

2,如果要完成 BookStore 到 BookStoreDto 具体的应该如何映射呢 
相同的类型与名字就不说了,如BookStore.Name->BookStoreDto.Name AutoMapper会自动去找。

而对于List与List者我们必须在配置下面代码之前

1
2
<span>var </span>exp = <span>Mapper</span>.CreateMap&lt;<span>BookStore</span>, <span>BookStoreDto</span>&gt;();
exp.ForMember(dto =&gt; dto.<span>Books</span>, (map) =&gt; map.MapFrom(m =&gt; m.<span>Books</span>));

告诉AutoMapper,Book与BookDto的映射,最后效果为:

1
2
3
<span>Mapper</span>.CreateMap&lt;<span>Book</span>, <span>BookDto</span>&gt;();
<span>var </span>exp = <span>Mapper</span>.CreateMap&lt;<span>BookStore</span>, <span>BookStoreDto</span>&gt;();
exp.ForMember(dto =&gt; dto.<span>Books</span>, (map) =&gt; map.MapFrom(m =&gt; m.<span>Books</span>));

Address同理。

3,如果要完成不同类型之间的转换用AutoMapper,如string到int,string->DateTime,以及A->B之间的类型转换我们可以参照如下例子:

http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters&referringTitle=Home

4, 对于我们不想要某属性有值我们可以采用下面的方式。

1
2
exp.ForMember(ads =&gt; ads.ZipCode, dto =&gt; dto.Ignore()); <span>//如果对于不想某属性有值,我们可以通过Ignore来忽略他,这样在调用AssertConfigurationIsValid时也不会报错.
</span>
1
(End)------------------------------------------------------------------------------------

【三】定义类型间的简单映射规则

前面我们看了Convention的映射方式,客观的说还是有很多类型间的映射是无法通过简单的Convention方式来做的,这时候就需要我们使用Configuration了。好在我们的Configuration是在代码中以“强类型”的方式来写的,比写繁琐易错的xml方式是要好的多了。 
先来看看BookDto到Publisher的映射。

回顾一下前面中定义的规则:BookDto.Publisher -> Publisher.Name。

在AutoMapperzhong,我们可以这样映射:

1
<span>   1:  </span>var map = Mapper.CreateMap&lt;BookDto,Publisher&gt;();
1
<span>   2:  </span>map.ForMember(d =&gt; d.Name, opt =&gt; opt.MapFrom(s =&gt; s.Publisher));

AutoMapper使用ForMember来指定每一个字段的映射规则:

引用

The each custom member configuration uses an action delegate to configure each member.

还好有强大的lambda表达式,规则的定义简单明了。 
此外,我们还可以使用ConstructUsing的方式一次直接定义好所有字段的映射规则。例如我们要定义BookDto到第一作者(Author)的ContactInfo的映射,使用ConstructUsing方式,我们可以:

C#代码

1
<span>   1:  </span>var map = Mapper.CreateMap&lt;BookDto,ContactInfo&gt;();
1
<span>   2:  </span>map.ConstructUsing(s =&gt; <span>new</span> ContactInfo
1
<span>   3:  </span>                                          {
1
<span>   4:  </span>                                              Blog = s.FirstAuthorBlog,
1
<span>   5:  </span>                                              Email = s.FirstAuthorEmail,
1
<span>   6:  </span>                                              Twitter = s.FirstAuthorTwitter
1
<span>   7:  </span>                                          });

然后,就可以按照我们熟悉的方式来使用了:

1
<span>   1:  </span>            BookDto dto = <span>new</span> BookDto
1
<span>   2:  </span>                                    {
1
<span>   3:  </span>                                        FirstAuthorEmail = <span>"matt.rogen@abc.com"</span>,
1
<span>   4:  </span>                                        FirstAuthorBlog = <span>"matt.amazon.com"</span>,
1
<span>   5:  </span>                                    };
1
<span>   6:  </span>            ContactInfo contactInfo = Mapper.Map&lt;BookDto, ContactInfo&gt;(dto);

如果需要映射的2个类型有部分字段名称相同,又有部分字段名称不同呢?还好AutoMapper给我们提供的Convention或Configuration方式并不是“异或的”,我们可以结合使用两种方式,为名称不同的字段配置映射规则,而对于名称相同的字段则忽略配置。 
例如对于前面提到的AddressDto到Address的映射,假如AddressDto的字段Country不叫Country叫CountryName,那么在写AddressDto到Address的映射规则时,只需要:

1
<span>   1:  </span>var map = Mapper.CreateMap&lt;AddressDto, Address&gt;();
1
<span>   2:  </span>map.ForMember(d =&gt; d.Country, opt =&gt; opt.MapFrom(s =&gt; s.CountryName));

对于City、Street和PostCode无需定义任何规则,AutoMapper仍然可以帮我们进行正确的映射。

该文转自:http://zz8ss5ww6.iteye.com/blog/1126219