ASP.NET MVC 学习笔记-2.Razor语法
1. 表达式
表达式必须跟在“@”符号之后,
2. 代码块
代码块必须位于“@{}”中,并且每行代码必须以“;”结尾。代码块中定义的变量可能会被同一个域中的其他块使用。比如,定义在视图顶部的变量可以被同一视图中的代码块和代码段访问。
3. 布局
Razor通过layouts保持网页外观布局的一致性。布局模板包含基本的标签,并可以指定渲染视图内容的位置。比如
基本布局文件(_Layout.cshtml)
<!DOCTYPE Html> <html lang=”en”> <head> <mete charset=”utf-8”/> <title>@View.Title</title> </head> <body> <div class=”header”> @RenderSection(“Header”); </div> @RenderBody() <div class=”footer”> @RenderSection(“Footer”); </div> </body> </html>
布局页面定义完成后,其他视图页面就可以引用该布局文件,比如
@{Layout=”~/_Layout.cshtml”;} @section Header { <h1>Page Header Content</h1> } @section Footer { Copyright @DateTime.Now.Year } <div class=”main”> Page Main Content </div>
使用Razor布局和内容视图将页面组合在一起,展示了一个完整的页面,其中的每一块定义了页面的不同部分。
4. 部分视图
使用布局通过重用部分HTML代码做到网站外观的一致性,但是有一些情况,布局则无法实现,比如,网页上的一部分信息需要多次重复出现(格式一致,显示内容不一致),例如,购物网站页面上的交易列表,只显示交易名称、当前价格和摘要信息。
ASP.NET MVC通过部分视图的技术实现了这个需求。
首先,先定义部分视图,并保存为单独的视图文件(例如,~/Views/Shared/Acution.cshtml)。
@model Auction <div class=”auction”> <a href=”@Model.Url”><img src=”@Model.ImageUrl”</a> <h4><a href=”@Model.Url”>@Model.Title</a></h4> <p>Current Price :@Model.CurrentPrice</p> </div>
然后,在需要使用该部分视图的位置,调用ASP.NET MVC自带的HTML方法调用它,比如:
@model IEnumerable<Auction> <h2>Search Result</h2> @foreach(var auction in Model){ @Html.Partial(“Auction”,auction); }
其中,Html.Partial()方法的第一个参数“Auction”是部分视图名称,并需要包含扩展名。第二个参数则是传递到部分视图的数据模型。第二个参数不是必须的,如果不传递,系统会默认将调用该部分视图的数据模型进行传递,比如,本例中的IEnumerable<Auction>。
由此可见,使用部分视图能够降低网页中的代码重复和编码复杂性,增强可读性。
5. 显示数据
MVC架构分为三层,模型、视图和控制器。三层之间彼此分离且协同工作,其中,就需要控制器担当“协调”的角色,视图将请求交给控制器,控制器对模型进行操作,并将操作结果反馈给视图,视图对模型的数据进行呈现。
控制器和视图之间的数据传递方式,ASP.NET MVC 提供了以下几种实现方式:
1) ViewData
ViewData实现方式,它的实现类似于Dictionary的操作,使得数据传递变得非常简单。
在控制器方法中使用类似于ViewData[“DataKey”]=dataValue进行赋值,在视图文件中,使用 var dataValue=ViewData[“DataKey”]获取数据。
2) ViewBag
ViewBag的使用类型与C#中的dynamic类型,可以直接操作其属性,比如,
控制器方法:ViewBag.DataProperty=dataValue;
视图文件:var dataValue=ViewBag.DataProperty;
3) Model属性
Model属性属于强类型,并且是动态类型,可以在视图上输入”@Model”就可以直接访问。
6. HTMLHelper和URLHelper
Web请求的目标就是向用户发送HTML代码,在Razor语法中,ASP.NET MVC有两个重要的帮助类来生成对应的HTML代码,分别是HTMLHelper和URLHelper。HTMLHelper类用来生成HTML标记代码,URLHelper用来生成URL地址链接。
<img src=’@Url.Content(“~/Content/images/header.jps”)’/> @Html.ActionLink(“Home”,”Index”,”Home”)
渲染得到的HMTL代码为:
<img src=’/vdir/Content/images/header.jpg’/> <a href=”/vdir/Home/Index”>HomePage</a>
ASP.NET MVC 学习笔记-1.ASP.NET MVC 基础
ASP.NET MVC在原来ASP.NET的基础上抛弃了基于页面的架构风格,使用了全新的MVC(模型-视图-控制器)架构的一种技术。
目前,它和ASP.NET都共存在.NET Framework之上。
1. MVC架构
MVC架构是一种严格实现应用程序各个部分隔离的架构模式,这种隔离称之为“松耦合”。这种架构模式的好处如下:
1) 开发
开发单个组件时不再依赖其他组件,减少了组件间的影响。
2) 测试
组件间的松耦合,允许测试代码能够替换其他组件,减少其他不必要的操作,简化了测试流程。
3) 维护
组件间的依赖关系降低,修改一个组件时,对其他组件的影响降低到最小,更利于维护。
2. MVC架构的组成
MVC架构将应用程序严格地划分为三层:模型、视图和控制,每层有具有各自的职责,不需要关注其他层具体实现。三层之间的关系如下图所示:
模型(Model):代表着核心的业务逻辑和数据,封装了一些属性和行为。
视图(View):负责将模型数据转换并呈现在表示层上。
控制器(Controller):控制程序逻辑,并协调视图和模型之间的关系,它从视图上接受请求,使用模型执行对应的操作,并将执行的结构反馈给视图。
3. 路由
对ASP.NET MVC网站的请求都是使用URL的形式,因此,系统能够根据URL的信息作出相应的动作,这就是ASP.NET Routing模块的主要作用,所以说,ASP.NET Routing是ASP.NET MVC请求的核心。
简单来说,ASP.NET Routing是一个模式匹配系统,程序运行初始化时,根据Route表的信息进行模式注册,即告诉Routing模块如何处理与模式匹配的请求,程序运行时,Routing模块接受请求,先在路由表进行模式匹配,匹配成功时,将请求转发给特定的控制器处理,匹配不成功时,则返回404错误码。
4. 路由配置
创建程序时,默认已经配置ASP.NET路由,主要包括两个位置
首先,在Web.config文件中配置有路由相关的配置节,<system.web.httpModules/>、<sytem.web.httpHandlers/>、<system.webserver.modules/>、<sytem.webserver.handlers/>。
其次,程序启动时调用的注册信息,文件位置App_Start/RouteConfig.cs,调用扩展方法MapRoute()方法。
routes.MapRoute(
“Default”,//路由名字
“{controller}/{action}/{id}”,//URL参数信息
new {controller=””,action=””,id=UrlParameter.Optional }//默认URL参数
还可以添加自定义路由,但是,代码位置必须放置在默认路由的前面,否则永远得不到匹配。
URL |
Controller |
Action |
ID |
|
/auctions/auction/1234 |
AuctionsController |
Auction |
1234 |
完美匹配 |
/auctions/recent |
AuctionsController |
Recent |
无参数 |
|
/acutions |
AuctionsController |
Index |
默认Action |
|
/ |
HomeController |
Index |
系统默认 |
5. Controller
MVC架构中,Controller能够相应视图发来的请求,并获取相应的模型数据,反馈给视图。在ASP.NET MVC程序,Controller是包含被路由模块处理请求时调用的方法的类。
Controller是一个类,统一派生于System.Web.Mvc.Controller基类。
ASP.NET MVC程序有个约定就是创建的Controller类,都必须以Controller结尾,比如HomeController。系统启动时,会自动查找派生于Controller的子类,并且名字后缀为Controller的类进行实例化操作。程序中使用该控制器时,不需要填写后面的Controller后缀,直接使用名称即可。
6. ControllerAction
Controller类上的公共方法称为ControlAction。ControlAction不能重载,也不能为静态方法。
7. ActionResult
ControllerAction处理完请求后,如果需要返回处理结果,则需要使用ActionResult进行返回。Controller中每个ControllerAction都要返回ActionResult,但不需要手动编码完成,只需要调用Controller基类中的对应方法即可。
Content() |
返回文本类型的ContentResult |
“Hello World” |
File() |
返回文件内容的FileResult |
|
HttpNotFound() |
返回包含404错误码的HTTPNotFoundResult |
|
JavaSript() |
返回包含JavaScript内容的JavaScriptResult |
“function hello(){ alert(hello world); }” |
Json() |
返回Json格式数据的JsonResult |
|
PartialView() |
返回包含部分视图内容的PartialViewResult |
|
Redirect() |
跳转到给定的URL上 |
|
View() |
返回渲染视图的ViewResult |
8. 操作参数
ControllerAction在被执行时,能够使用请求时发送的参数信息,这种功能称为“模型绑定”。比如:
ublic ActionResult Index(string name,string password)
{
}
当ASP.NET MVC框架执行该方法时,会自动将传递的参数值赋值到对应的参数上。ASP.NET MVC框架处理大量的基础工作,让我们能够专注于业务逻辑,而且代码也有更高的可读性。
9. 视图
ControllerAction向视图返回了ActionResult类型的ViewResult实例,当渲染视图时,ASP.NETMVC框架会使用到Controller和ControllerAction的名字。以HomeController中的Index为例:
public ActionResult Index()
{
return View();
}
ASP.NET MVC框架中一个约定,Controller中使用到的视图文件都需要放置在Views下以Controller名称命名的文件夹中。而且,若返回值中没有传递视图名称参数,则直接ControllerAction的名称作为视图的文件名称。如果没有找到对应的视图文件,则会继续在Views下的Shared文件夹中查找。
反射的具体应用
1.业务场景
随着车间中上位机数目的增加,系统运维人员的工作量也在增加,有时候修改一些配置信息,可能需要运维人员挨个在上位机系统中进行修改,因此,把一些配置信息的修改调整到后台系统中,上位机在交接班和系统重启或者上位机启动后台线程每个一段时间查询一次,获取到对应的配置信息,然后进行修改,这样操作的话,能够极大地减轻运维人员的工作量。
2.后台程序设计
首先,创建配置类别表,用来描述配置项的类别信息,比如,打印配置、系统开关配置、扫描条码长度配置等。
系统配置类别表:Configure_Class | ||
ID(PK) | 数据主键 | |
Name | 类别名称 | |
Description | 类别描述 | |
Category | 分类属性(Public、Private) |
Public:所有上位机公用 Private:上位机专属配置 |
Delete_Flag | 删除标记 | 用于数据的逻辑删除 |
然后,创建配置项表,用于描述配置项的详细信息。
系统配置项表Configure_Item | ||
ID(PK) | 数据主键 | |
Class_ID | 配置类别ID | |
Name | 名称 | |
Description | 描述 | |
Date_Type | 数据类型 | String、Boolean、SerialPort、Class等 |
Instruction | 配置项说明 | 配置项的详细说明 |
Delete_Flag | 删除标记 | 用于数据的逻辑删除 |
最后,创建系统配置表,用于描述上位机的详细配置信息。
系统配置表Configure | ||
ID(PK) | 数据主键 | |
Site_Name | 部门名称 | 名称也为其唯一标识信息 |
Area_Name | 区域名称 | |
PLine_Name | 生产线名称 | |
Equipment_Name | 机台名称 | |
Equipment_Part | 机台部位 | 较大的设备会使用两个上位机控制,分为不同的部位 |
Item_ID | 配置项ID | |
Item_Value | 配置项值 | 特殊的信息使用Json字符串标识,上位机进行解析 |
Delete_Flag | 删除标记 | 用于数据的逻辑删除 |
完成以上数据表的创建后,后台系统需要根据表结构进行编程实现,最后系统配置表中数据规则为
ID | Site_Name | Area_Name | PLine_Name | Equipment_Name | Equipment_Part | Item_ID | Item_Value | Delete_Flag |
1 | AAA | BBB | CCC | DDD | UP | 1 | 1(boolean类型) | |
2 | AAA | BBB | CCC | DDD | DOWN | 2 | 1(string类型) |
3.上位机程序设计
上位机程序在特定条件下触发获取后台系统配置的操作,从数据库中获取到机台对应的系统配置,并转换为对应的实体类;
上位机系统配置实体类:
public class RemoteConfigItem { /// <summary> /// 数据键值 /// </summary> public string ID { get; set; } /// <summary> /// 配置分类 /// </summary> public string Category { get; set; } /// <summary> /// 机台名称 /// </summary> public string EquipmentName { get; set; } /// <summary> /// 机台部位 /// </summary> public string EquipmentPart { get; set; } /// <summary> /// 名称 /// </summary> public string Name { get; set; } /// <summary> /// 值 /// </summary> public string Value { get; set; } }
获取配置项的数据后,使用反射机制将配置项的值设置到系统配置实体类的对应属性上,实际上,此处的默认约定就是,系统配置项的名称必须和上位机系统配置类的属性名称相同,否则会无法执行反射操作。
public partial class SystemConfig { /// <summary> /// 设置远程配置 /// </summary> private void SetRemoteConfig(List<RemoteConfigItem> itemList) { var propertyInfoArray = this.GetType().GetProperties(); List<PropertyInfo> propertyInfoList = new List<PropertyInfo>(propertyInfoArray); var publicItemList = itemList.FindAll(item => item.Category.Equals(ConfigCategoryConst.Public)); foreach (var configItem in publicItemList) { var propertyInfo = propertyInfoList.Find(item => item.Name.Equals(configItem.Name)); if (propertyInfo== null) { continue; } if (!propertyInfo.CanWrite) { continue; } //后期此处需要优化,需要对不是基元类型的属性,需要使用Json序列化方法,比如Json字符串序列化为对应的类实例 propertyInfo.SetValue(this, Convert.ChangeType(configItem.Value, propertyInfo.PropertyType), null); } } }
策略模式的具体应用
1.业务场景
随着信息化水平逐渐提升,业务部门在数据可视化方面提报了新的需求,上位机软件在扫描枪扫描投入料时,需要将物料的详细发送到对应的LED屏幕上。
2.初步设计
一开始,只有一个工厂提出这个需求,直接在扫描投入料的代码的后面,增加了调用LED的委托方法(通过委托方式实现线程级别的发送,从未不影响正常业务)。满足需求后,向LED发送数据的业务场景越来越多,比如,切换计划、减料、扫描工位、扫描投入料。这几种场景下可能会导致LED发送方法的同时调用,造成多线程同时出发LED自带的DLL方法的调用,造成发送失败的问题,基于该问题,在实际发送方法增加lock(mLockObj)的锁控制,从未避免出现多线程调用问题。
随着典型业务的推广,其他工厂也逐渐开始有这种需求,但是采购的LED屏幕厂家却不一样,如果仅仅为了实现需求,可以在代码中根据工厂标志位,进行if...else if...或switch的判断。
3.设计提升
3.1创建发送信息类
各工厂实际业务可能会出现不一致的问题, 导致LED显示的信息不一样,如果把信息直接以参数的形式进行传递,后期需要增加显示时,涉及的修改太多,因此,将发送方法的参数提取成一个信息类,以后再扩展直接在类中扩展属性即可,需要使用该参数时,直接调用,涉及到的修改降低到最小。
比如:
1 public class LEDParameter 2 { 3 private int mScreenID; 4 5 private string mIPAddress; 6 7 private string mMessage; 8 9 private Color mForeColor; 10 11 private int mFontSize; 12 }
3.2 定义发送方法的接口,每个工厂对应一个具体的类来实现该接口。
发送接口
1 public interface ILEDDisplay 2 { 3 void DisplayMessage(LEDParameter parameter); 4 }
各工厂的实现类
1 public class ALEDDisplay : ILEDDisplay 2 { 3 public void DisplayMessage(LEDParameter parameter) 4 { 5 Console.WriteLine("Using AFactory LED Display Method"); 6 } 7 } 8 9 public class BLEDDisplay : ILEDDisplay 10 { 11 public void DisplayMessage(LEDParameter parameter) 12 { 13 Console.WriteLine("Using BFactory LED Display Method"); 14 } 15 }
3.3 根据实际情况初始化实现类
1 ILEDDisplay dislay = null; 2 switch (factoryName) 3 { 4 case "A": 5 dislay = new ALEDDisplay(); 6 break; 7 case "B": 8 dislay = new BLEDDisplay(); 9 break; 10 } 11 12 //实际调用时,直接调用接口方法即可 13 var para = new LEDParameter(); 14 dislay.DisplayMessage(para);
随着应用工厂的增加,直接创建对应的实现类即可,主程序模块的代码不需要进行太多变化(LED显示参数类可能会增加属性,需要主程序模块进行传递)。
另附:
策略模式的定义:对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象负责。策略模式通常把一系列的算法包装到一系列的策略类里面。用一句话慨括策略模式就是——“将每个算法封装到不同的策略类中,使得它们可以互换”。
策略模式的结构图:
该模式涉及到三个角色:
- 环境角色(Context):持有一个Strategy类的引用
- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类来实现。此角色给出所有具体策略类所需实现的接口。
- 具体策略角色(ConcreteStrategy):包装了相关算法或行为。
责任链模式的具体应用
1.业务场景
生产车间中使用的条码扫描,往往一把扫描枪需要扫描不同的条码来处理不同的业务逻辑,比如,扫描投入料工位条码、扫描投入料条码、扫描产出工装条码等,每种类型的条码位数是不一样,因此通过条码长度来进行业务区分。
2.初步设计
面对此场景,能够想到的最简单的设计就是使用if...else if...或者swith进行判断,因此,我们编写的代码如下
1 switch(barCode.length) 2 { 3 case 3: 4 DoSomething1(); 5 break; 6 case 4: 7 DoSomething2(); 8 break; 9 case 5: 10 DoSomething3(); 11 break; 12 default: 13 DoSomething4(); 14 break; 15 }
使用是if...else if...或者switch已经基本上满足了需求,以后需要添加扫描场景,只需要增加判断,完成对应的方法即可。
作为一个程序员,仅仅满足需求的话,往往降低了对自己的要求,同时,随着扫描业务的增加,switch中的代码逐渐增加,多到我们自己都觉得这段代码读起来就像是吃着前天剩下的硬馒头一样,难以下咽。
3.设计提升
上述场景完全可以使用设计模式中的责任链模式来进行优化,实施步骤如下:
3.1 定义处理结果
一个处理在责任链上流动时,有两种结果,一是不能处理,转给其后继者,二是可以处理,同时又引出两种结果,处理成功和处理失败。因此,对处理结果统一定义为枚举类型
1 public enum HandleResult 2 { 3 /// <summary> 4 /// 成功 5 /// </summary> 6 Success=0, 7 /// <summary> 8 /// 失败 9 /// </summary> 10 Failed = 1, 11 /// <summary> 12 /// 未处理 13 /// </summary> 14 Unhandle = 2 15 }
3.2定义业务抽象类AbstractBarCodeHandler
定义业务抽象类,包含一个属性“Successor”,用于标记其后继者,一个公共方法“HandleRequest”,当调用真正的业务处理方法时返回未处理,则调用其后继者进行处理,一个抽象方法“HandleRequestCore”,每个扫描业务具体类,需要实现此方法,并对条码进行处理。
1 public abstract class AbstractBarCodeHandler 2 { 3 /// <summary> 4 /// 责任处理的后继者 5 /// </summary> 6 private AbstractBarCodeHandler mSuccessor; 7 8 /// <summary> 9 /// 责任处理的后继者 10 /// </summary> 11 public AbstractBarCodeHandler Successor 12 { 13 get 14 { 15 return mSuccessor; 16 } 17 18 set 19 { 20 mSuccessor = value; 21 } 22 } 23 24 /// <summary> 25 /// 请求处理方法 26 /// </summary> 27 public HandleResult HandleRequest(string barCode) 28 { 29 var result = HandleRequestCore(barCode); 30 if (result == HandleResult.Unhandle) 31 { 32 if (Successor != null) 33 { 34 Successor.HandleRequest(barCode); 35 } 36 else 37 { 38 Console.WriteLine($"The BarCode:{barCode} do not handle."); 39 } 40 } 41 return result; 42 } 43 44 protected abstract HandleResult HandleRequestCore(string barCode); 45 }
3.3定义业务实现具体类
每个具体的条码扫描业务,都定义一个类,继承自AbstractBarCodeHandler,并实现其抽象方法,比如,
LocationBarCodeHandler:工位条码处理类
LocationInBarCodeHandler:入库条码处理类
LocationOutBarCodeHandler:出库条码处理类
MaterialBarCodeHandler:物料条码处理类
ToolingBarCodeHandler:工装条码处理类
3.4 生成责任链
依次对每个类进行初始化,并设置其后继者,比如:
工位条码处理类实例.Successor=入库条码处理类实例;
入库条码处理类实例.Successor=出库条码处理类实例;
出库条码处理类实例.Successor=物料条码处理类实例;
物料条码处理类实例.Successor=工装条码处理类实例;
3.5处理过程
当条码枪扫描一个条码,便发起了一个处理请求,该请求在责任链上依次流动(工位->入库->出库->物料->工装),若某个节点不能够处理,则需要交接去后继者,若能够处理,则返回处理结果,当所有的节点都无法处理,需要给出对应的提示。
追加责任链模式的定义和结构图:
定义:责任链模式指的是——某个请求需要多个对象进行处理,从而避免请求的发送者和接收之间的耦合关系。将这些对象连成一条链子,并沿着这条链子传递该请求,直到有对象处理它为止。
结构图:
ServiceStack.Redis订阅发布服务的调用
1.Redis订阅发布介绍
Redis订阅发布是一种消息通信模式:发布者(publisher)发送消息,订阅者(Subscriber)接受消息。类似于设计模式中的观察者模式。
发布者和订阅者之间使用频道进行通信,当需要发送消息时,发布者通过publish命令将消息发送到频道上,该消息就会发送给订阅这个频道的订阅者。
图片来自于http://www.runoob.com/redis/redis-pub-sub.html
2.ServiceStack.Redis
ServiceStack.Redis是Redis的C#版本的客户端,是ServiceStack的一部分。
ServiceStack的地址为https://servicestack.net
3.订阅者
首先创建RedisClient,然后调用CreateSubscription()方法创建订阅客户端,然后设置订阅客户端的几个事件:
OnMessage:接受到消息时。
OnSubscribe:订阅频道时。
OnUnSubscribe:取消订阅频道时。
最后,调用subscribeToChannels(channelName),订阅频道。
详细代码如下:
1 /// <summary> 2 /// Redis订阅 3 /// </summary> 4 public static void Subscribe() 5 { 6 using (RedisClient consumer = new RedisClient("127.0.0.1", 6379)) 7 { 8 //创建订阅 9 IRedisSubscription subscription = consumer.CreateSubscription(); 10 //接受到消息时 11 subscription.OnMessage = (channel, msg) => 12 { 13 Console.WriteLine($"从频道:{channel}上接受到消息:{msg},时间:{DateTime.Now.ToString("yyyyMMdd HH:mm:ss")}"); 14 Console.WriteLine($"频道订阅数目:{subscription.SubscriptionCount}"); 15 Console.WriteLine("___________________________________________________________________"); 16 }; 17 //订阅频道时 18 subscription.OnSubscribe = (channel) => 19 { 20 Console.WriteLine("订阅客户端:开始订阅" + channel); 21 }; 22 //取消订阅频道时 23 subscription.OnUnSubscribe = (a) => { Console.WriteLine("订阅客户端:取消订阅"); }; 24 25 //订阅频道 26 subscription.SubscribeToChannels("channel1"); 27 } 28 }
4.发布者
首先创建RedisClient,然后调用PublishMessage(channelName,message)发布消息。
详细代码如下:
RedisClient client = new RedisClient("127.0.0.1", 6379);
string message = "发布消息测试";
client.PublishMessage("channel1", message);
到目前为止,一个简单的Redis发布订阅就完成了。
5.Redis发布服务
使用发布者仅仅能够发布消息,但是不能够检测一些事件的变化,Redis中还有一个RedisPublishServer的类,里面包括一些事件能够使我们很好地检测服务的运行。
OnMessage:接受到消息;
OnStart:发布服务开始运行时;
OnStop:发布服务停止运行时;
OnUnSubscribe:订阅者取消订阅时;
OnError:发布出现错误时;
OnFailover:Redis服务器冗余切换时;
发布服务端初始化完成后,调用Start()方法,开始执行发布服务。
发布服务执行后,执行消息的发布client.PublishMessage时,发布服务端也能够接受到发布的消息。
详细代码如下:
1 public void Publish() 2 { 3 //PooledRedisClientManager 4 IRedisClientsManager redisClientManager = new PooledRedisClientManager("127.0.0.1:6379"); 5 //发布、订阅服务 IRedisPubSubServer 6 RedisPubSubServer pubSubServer = new RedisPubSubServer(redisClientManager, "channel1") 7 { 8 OnMessage = (channel, msg) => 9 { 10 Console.WriteLine($"从频道:{channel}上接受到消息:{msg},时间:{DateTime.Now.ToString("yyyyMMdd HH:mm:ss")}"); 11 Console.WriteLine("___________________________________________________________________"); 12 }, 13 OnStart = () => 14 { 15 Console.WriteLine("发布服务已启动"); 16 Console.WriteLine("___________________________________________________________________"); 17 }, 18 OnStop = () => { Console.WriteLine("发布服务停止"); }, 19 OnUnSubscribe = channel => { Console.WriteLine(channel); }, 20 OnError = e => { Console.WriteLine(e.Message); }, 21 OnFailover = s => { Console.WriteLine(s); }, 22 }; 23 //接收消息 24 pubSubServer.Start(); 25 }
C#读取XML文件的基类实现
刚到新单位,学习他们的源代码,代码里读写系统配置文件的XML代码比较老套,直接写在一个系统配置类里,没有进行类的拆分,造成类很庞大,同时,操作XML的读写操作都是使用SetAttribute和node.Attribute(name)方法,因此,想到结合之前所做的XML操作,完成了一个能够读取XML文件的基类,便于以后的使用。
PS:即使再老套的代码,目前也不敢进行优化,一是水平不行,二是不敢。
使用静态扩展类,扩展了几个经常使用的类型,能够方便数据的读写。
操作XML的类,可以直接继承BaseLinqXmlFileInfo,只需要设置protected类型的变量mPathName,mFileName,然后重写抽象方法即可实现XML文档的读取操作。
PS:能力有限,有不对之处请指正,谢谢。
1 using System; 2 using System.IO; 3 using System.Xml.Linq; 4 5 namespace Common 6 { 7 public abstract class BaseLinqXmlFileInfo 8 { 9 protected string mPathName; 10 11 protected string mFileName; 12 13 protected virtual string PathName 14 { 15 get { return mPathName; } 16 set { mPathName = value; } 17 } 18 19 protected virtual string FileName 20 { 21 get { return mFileName; } 22 set { mFileName = value; } 23 } 24 25 protected virtual string FilePathName 26 { 27 get 28 { 29 return Path.Combine(PathName, FileName); 30 } 31 } 32 33 public virtual bool Load() 34 { 35 bool result = false; 36 try 37 { 38 string filePathName = this.FilePathName; 39 if (File.Exists(filePathName)) 40 { 41 this.LoadDocument(filePathName); 42 result = true; 43 } 44 } 45 catch(Exception ex) 46 { 47 //异常信息输出 48 } 49 return result; 50 } 51 52 public virtual bool Save() 53 { 54 bool result = false; 55 try 56 { 57 58 string pathName = this.PathName; 59 if (!Directory.Exists(pathName)) 60 { 61 Directory.CreateDirectory(PathName); 62 } 63 string tempFileName = "~" + FileName; 64 string tempfilePathName = Path.Combine(pathName, tempFileName); 65 this.SaveDocument(tempfilePathName); 66 string filePathName = Path.Combine(PathName, FileName); 67 if (File.Exists(filePathName)) 68 { 69 FileAttributes att = File.GetAttributes(filePathName); 70 if((att & FileAttributes.ReadOnly)== FileAttributes.ReadOnly) 71 { 72 File.SetAttributes(filePathName, att & ~FileAttributes.ReadOnly); 73 } 74 } 75 File.Copy(tempfilePathName, filePathName, true); 76 if (File.Exists(tempfilePathName)) 77 { 78 File.Delete(tempfilePathName); 79 } 80 result = true; 81 } 82 catch (Exception ex) 83 { 84 //异常信息输出 85 } 86 return result; 87 } 88 89 private void LoadDocument(string fileName) 90 { 91 try 92 { 93 XDocument doc = XDocument.Load(fileName); 94 this.ReadXml(doc); 95 } 96 catch(Exception ex) 97 { 98 //异常信息输出 99 } 100 } 101 102 private void SaveDocument(string fileName) 103 { 104 try 105 { 106 XDocument doc = new XDocument(); 107 this.WriteXml(doc); 108 doc.Save(fileName); 109 } 110 catch(Exception ex) 111 { 112 //异常信息输出 113 } 114 } 115 116 private void ReadXml(XDocument doc) 117 { 118 XElement root = doc.Root; 119 this.ReadXml(root); 120 } 121 122 private void WriteXml(XDocument doc) 123 { 124 XElement root = new XElement("root"); 125 doc.Add(root); 126 this.WriteXml(root); 127 } 128 129 protected abstract void ReadXml(XElement node); 130 131 protected abstract void WriteXml(XElement node); 132 } 133 134 public static class XMLSerializeExtensionClass 135 { 136 public static void Write(this XElement node,string name,string value) 137 { 138 node.SetAttributeValue(name, value); 139 } 140 141 public static string ReadString(this XElement node, string name, string defaultValue) 142 { 143 XAttribute att = node.Attribute(name); 144 return att != null ? att.Value : defaultValue; 145 } 146 147 public static void Write(this XElement node,string name,long value) 148 { 149 node.SetAttributeValue(name, value); 150 } 151 152 public static long ReadLong(this XElement node,string name,long defaultValue) 153 { 154 XAttribute att = node.Attribute(name); 155 return att != null ? Convert.ToInt64(att.Value) : defaultValue; 156 } 157 158 public static void Write(this XElement node,string name,decimal value) 159 { 160 node.SetAttributeValue(name, value); 161 } 162 163 public static decimal ReadDecimal(this XElement node,string name,decimal defaultValue) 164 { 165 XAttribute att = node.Attribute(name); 166 return att != null ? Convert.ToDecimal(att.Value) : defaultValue; 167 } 168 169 public static void Write(this XElement node ,string name,DateTime value) 170 { 171 node.SetAttributeValue(name, value); 172 } 173 174 public static DateTime ReadDateTime(this XElement node,string name,DateTime defaultValue) 175 { 176 XAttribute att = node.Attribute(name); 177 return att != null ? Convert.ToDateTime(att.Value) : defaultValue; 178 } 179 180 public static void Write(this XElement node,string name,int value) 181 { 182 node.SetAttributeValue(name, value); 183 } 184 185 public static int ReadInt(this XElement node,string name,int defaultValue) 186 { 187 XAttribute att = node.Attribute(name); 188 return att != null ? Convert.ToInt32(att.Value) : defaultValue; 189 } 190 191 public static void Write(this XElement node, string name,bool value) 192 { 193 node.SetAttributeValue(name, value); 194 } 195 196 public static bool ReadBoolean(this XElement node, string name, bool defaultValue) 197 { 198 XAttribute att = node.Attribute(name); 199 return att != null ? Convert.ToBoolean(att.Value) : defaultValue; 200 } 201 } 202 }