快看Sample代码,速学Swift语言(2)-基础介绍
Swift语言是一个新的编程语言,用于iOS, macOS, watchOS, 和 tvOS的开发,不过Swift很多部分内容,我们可以从C或者Objective-C的开发经验获得一种熟悉感。Swift提供很多基础类型,如Int,String,Double,Bool等类型,它和Objective-C的相关类型对应,不过他是值类型,而Objective-C的基础类型是引用类型,另外Swift还提供了几个集合类型,如
Array
,
Set
, 和
Dictionary;Swift引入一些Objective-C里面没有的元祖类型,这个在C#里倒是有类似的,也是这个名词。 Swift语言是一种类型安全的强类型语言,不是类似JavaScript的弱类型,能够在提供开发效率的同时,减少常规出错的可能,使我们在开发阶段尽量发现一些类型转换的错误并及时处理。
常量和变量
let maximumNumberOfLoginAttempts = 10 var currentLoginAttempt = 0
常量用let定义,变量用var定义,它们均可以通过自动推导类型,如上面的就是指定为整形的类型。
也可以通过逗号分开多个定义,如下所示
var x = 0.0, y = 0.0, z = 0.0
如果我们的变量没有初始化值来确定它的类型,我们可以通过指定类型来定义变量,如下所示
var welcomeMessage: String var red, green, blue: Double
变量的打印,可以在输出字符串中用括号包含变量输出,括号前加斜杠 \ 符号。
print(friendlyWelcome)//Prints "Bonjour!" print("The current value of friendlyWelcome is \(friendlyWelcome)")//Prints "The current value of friendlyWelcome is Bonjour!"
注释符
// This is a comment. /* This is also a comment but is written over multiple lines. */ /* This is the start of the first multiline comment. /* This is the second, nested multiline comment. */ This is the end of the first multiline comment. */
上面分别是常规的的注释,以及Swift支持嵌套的注释符号
分号
Swift语句的划分可以不用分号,不过你加分号也可以,如果加分号,则可以多条语句放在一行。
let cat = "
快看Sample代码,速学Swift语言(3)-运算符
运算符是用来检查,更改或组合值的特殊符号或短语。Swift提供的很多常规的运算符,如+、-、*、/、%、=、==等,以及逻辑运算的&&、||等等,基本上不需要重复介绍,我们在这里只需要了解一些不太一样的运算符就可以了。如Swift引入的新运算符,范围操作符号,包括..<和...两个,该随笔介绍Swift常规的运算符中,以及和其他语言有所差异的部分。
赋值运算符
let b = 10 var a = 5 a = b // a is now equal to 10
赋值语句,处理和其他语言一样。
let (x, y) = (1, 2) // x is equal to 1, and y is equal to 2
这种代码是类似ECMAScript 6的脚本写法,通过把右边元祖对象解构赋值给左边对应的参数。
数学运算符
1 + 2 // equals 3 5 - 3 // equals 2 2 * 3 // equals 6 10.0 / 2.5 // equals 4.0
这些都是和其他语言没有什么不同,循例列出参考下
对于字符,也可以使用+符号进行连接新的字符串
"hello, " + "world" // equals "hello, world"
一元操作符中的-、+运算,和算术里面的负负得正,正负得负的意思一样了。
let three = 3 let minusThree = -three // minusThree equals -3 let plusThree = -minusThree // plusThree equals 3, or "minus minus three"
let minusSix = -6 let alsoMinusSix = +minusSix // alsoMinusSix equals -6
组合运算符提供+= 、-=的运算符操作
var a = 1 a += 2 // a is now equal to 3
对比运算符和其他语言差不多
等于 (
a == b
)不等于 (
a != b
)大于 (
a > b
)小于 (
a < b
)大于等于 (
a >= b
)小于等于 (
a <= b
)
另外值得注意的是,Swift提供了对比引用的两个操作符号,
===
和
!==,用来检查两个引用是否完全相等;或者不相等的。而==只是用来对比两个对象的值是否一致。
1 == 1 // true because 1 is equal to 1 2 != 1 // true because 2 is not equal to 1 2 > 1 // true because 2 is greater than 1 1 < 2 // true because 1 is less than 2 1 >= 1 // true because 1 is greater than or equal to 1 2 <= 1 // false because 2 is not less than or equal to 1
对比运算符也经常用来If条件语句里面
let name = "world" if name == "world" { print("hello, world") } else { print("I'm sorry \(name), but I don't recognize you") } // Prints "hello, world", because name is indeed equal to "world".
三元运算符
三元运算符 ? :和C#里面表现是一样的
question ? answer1 : answer2
let contentHeight = 40 let hasHeader = true let rowHeight = contentHeight + (hasHeader ? 50 : 20)
空值转换操作符
空值转换符是对可空类型(可选类型)的一个值得转换出来(
a ?? b
)。
let defaultColorName = "red" var userDefinedColorName: String? // defaults to nil var colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName is nil, so colorNameToUse is set to the default of "red"
userDefinedColorName = "green" colorNameToUse = userDefinedColorName ?? defaultColorName // userDefinedColorName is not nil, so colorNameToUse is set to "green"
范围操作符
闭合范围运算符 ... 和半闭合范围运算符 ..< 两个
for index in 1...5 { print("\(index) times 5 is \(index * 5)") } // 1 times 5 is 5 // 2 times 5 is 10 // 3 times 5 is 15 // 4 times 5 is 20 // 5 times 5 is 25
半闭合的范围运算符
let names = ["Anna", "Alex", "Brian", "Jack"] let count = names.count for i in 0..<count { print("Person \(i + 1) is called \(names[i])") } // Person 1 is called Anna // Person 2 is called Alex // Person 3 is called Brian // Person 4 is called Jack
或者如下使用
for name in names[..<2] { print(name) } // Anna // Alex
以及一侧范围的运算符,包括左侧和右侧两个部分
for name in names[2...] { print(name) } // Brian // Jack for name in names[...2] { print(name) } // Anna // Alex // Brian
let range = ...5 range.contains(7) // false range.contains(4) // true range.contains(-1) // true
逻辑运算符
let allowedEntry = false if !allowedEntry { print("ACCESS DENIED") } // Prints "ACCESS DENIED"
let enteredDoorCode = true let passedRetinaScan = false if enteredDoorCode && passedRetinaScan { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "ACCESS DENIED"
let hasDoorKey = false let knowsOverridePassword = true if hasDoorKey || knowsOverridePassword { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "Welcome!"
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "Welcome!"
或者使用括号使之更加方便阅读
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword { print("Welcome!") } else { print("ACCESS DENIED") } // Prints "Welcome!"
在Winform混合式框架中整合外部API接口的调用
在我们常规的业务处理中,一般内部处理的接口多数都是以数据库相关的,基于混合式开发的Winform开发框架,虽然在客户端调用的时候,一般选择也是基于Web API的调用,不过后端我们可能不仅仅是针对我们业务数据库的处理,也可以能是调用其他外部接口,如物流、供应商接口等接口,本随笔就是主要介绍基于混合式开发框架如何整合外部API接口的调用。
1、混合式框架的结构介绍
我们知道,混合式的框架是可以在客户端访问Web API服务、WCF服务或者直接连接数据库几种方式的综合,而尤以Web API为最广泛的应用,它的整个框架的结构如下所示。
在客户端中,通过统一的工厂类CallerFactory<T>对相应的接口进行访问,这里主要就是服务器端Web API服务接口的处理,以及客户端对Web API接口的封装,两部分通过一些基类进行简化处理,可以极大提高开发效率。
对于外部第三方的Web API接口,我们也可以在自己的Web API接口中进行包装,使得客户端通过相应的接口进行交互即可,不需要理会内部还是外部的接口,从而实现透明的接口调用。
2、RFID外部接口的整合处理
在一个客户的应用案例中,需要整合服务商RFID接口实现相应的数据交互,本篇随笔也是基于这个案例进行整个过程的分析和操作,使得我们了解在混合框架中如何整合第三方Web API接口为我们内部框架所用。
一般来说,Web API接口,需要明确API的URL、数据提交方式(POST/GET)、提交参数、返回集合,以及一些特殊的数据等,而一般接口的操作,也是需要一个访问令牌的,这些都是Web API接口调用的关键。
基本上我们有了上面Web API的1/2/3步骤的信息就可以进行接口编程了,这些是Web API开发非常重要的信息。
我们需要特别主要到,步骤1中的信息
这里面的token是额外的接口信息,是需要设置Http Request请求的头部信息里面的,是用户身份的重要信息,所以我们一般需要先通过指定的授权接口获取这个token信息。
在这个外部的接口集合里面,我们找到统一登录验证的接口定义如下所示。
通过上面的分析,我们首先需要需要处理好登录验证接口,然后通过接口传递令牌token给其他接口进行数据处理的。
结合我们的混合框架结构,这里我以测试项目TestProject项目为例进行介绍,我们调整WHC.TestProject.Caller项目的对应类,如下所示。
其中Facade层接口类IRFIDService.cs代码如下所示。
/// <summary> ///RFID服务外部接口/// </summary> [ServiceContract]public interfaceIRFIDService
{/// <summary> ///终端用户统一登录验证/// </summary> [OperationContract]
CheckinResult CheckIn(string username, string password, string device_uuid, string device_type, string last_app_version, stringapp_id);/// <summary> ///获取标签发货通知单批量/// </summary> [OperationContract]
TagOrderAsnResult TagOrderAsn(int brand_id, string factcode, string start_time, string end_time, PagerInfo pagerInfo, stringtoken);/// <summary> ///标签订单出库物流信息回写/// </summary> [OperationContract]
CommonResult TagOutPost(string docno_asn, string factcode, string dest_factcode, List<FreightInfo> freight, stringtoken);
}
这里面的接口定义,我们是根据输入参数、输出参数进行定义的,另外token是额外增加的令牌参数,用于请求头部写入信息的。
这个接口的定义其实和我们常规的Web API接口定义没有太多的不同,如下是一个内部客户信息接口定义。
/// <summary> ///客户信息的服务接口/// </summary> [ServiceContract]public interface ICustomerService : IBaseService<CustomerInfo>{/// <summary> ///根据客户名称获取客户列表/// </summary> /// <param name="name">客户名称</param> /// <returns></returns> [OperationContract]
List<CustomerInfo> FindByName(stringname);
}
差别就是它们接口继承类有所不同,外部接口由于不需要和数据库打交道,我们不需要继承IBaseService接口
根据这些接口的定义,我们还需要实现我们具体的Web API 服务,逻辑上它是对外部Web API接口的封装,但是对于客户端来说,并不需要知道是内部还是外部接口,客户端只需要知道如果提交参数或者结果即可。
由于Web API涉及多个参数的数据提交,一般来说这种情况都是以POST方式处理的,数据参数则统一在Web API端通过定义一个JObject对象来传递即可,登录认证的Web API接口定义如下所示。
/// <summary> ///基于RFID的应用接口/// </summary> public classRFIDController : BaseApiController
{/// <summary> ///终端用户统一登录验证/// </summary> /// <param name="param">包含多个属性的对象</param> /// <param name="token">访问令牌</param> [HttpPost]publicCheckinResult CheckIn(JObject param)
{
CheckinResult result= null;dynamic obj =param;if (obj != null)
{//使用POST数据 var postData =param.ToJson();//使用具体的URL var queryUrl = "https://***.***.***/api/v6/rfid/terminal/checkin/post";var helper = newHttpHelper();
helper.ContentType= "application/json";var content = helper.GetHtml(queryUrl, postData, true);
RFIDBaseData<CheckinResult> jsonResult = JsonConvert.DeserializeObject<RFIDBaseData<CheckinResult>>(content);if (jsonResult != null && jsonResult.code == 0)
{
result=jsonResult.data;
}returnresult;
}else{throw new MyApiException("传递参数错误");
}
}
其中输入的参数这里用了JObject param的参数,我们提交给外部Web API接口的时候,我们把这个参数再次序列号为JSON格式的字符串即可:
var postData =param.ToJson();
其中CheckinResult和RFIDBaseData是根据输入参数、输出结果进行的实体类定义,目的是序列化为强类型的实体类,方便数据处理操作。
在客户端,我们只需要对接好和Web API服务端的接口,那么调用起来就非常方便,其中对应的Web API接口客户端封装类 RFIDCaller 如下所示。
/// <summary> ///对RFID控制的接口调用封装/// </summary> public classRFIDCaller : NormalApiService, IRFIDService
{publicRFIDCaller()
{this.ConfigurationPath = ApiConfig.ConfigFileName; //Web API配置文件 this.configurationName =ApiConfig.RFID;
}public CheckinResult CheckIn(string username, string password, string device_uuid, string device_type, string last_app_version, stringapp_id)
{var action =System.Reflection.MethodBase.GetCurrentMethod().Name;string url =GetNormalUrl(action);var postData = new{
username=username,
password=password,
device_uuid=device_uuid,
device_type=device_type,
last_app_version=last_app_version,
app_id=app_id,
}.ToJson();var result = JsonHelper<CheckinResult>.ConvertJson(url, postData);returnresult;
}
有了这些,我们直接在客户端的界面里面,就可以通过调用CallerFactory<T>进行处理操作了,如下是客户端窗体获取验证身份令牌数据的代码
private string token = null;//访问RFID接口的token /// <summary> ///根据终端用户统一登录验证获取相关访问token/// </summary> /// <returns></returns> private stringGetRFIDToken()
{string username = "wuhuacong";string password = "123456";string device_uuid = "aaaaaaa";string device_type = "iphone";string last_app_version = "xxxxxxx";string app_id = "ntdf5543581a2f066e74cf2fe456";var result = CallerFactory<IRFIDService>.Instance.CheckIn(username, password, device_uuid, device_type, last_app_version, app_id);if(result != null)
{
token=result.token;
}returntoken;
}
上面是认证身份的接口,其他类型的接口类似的处理方式,如增加了一个
获取标签发货通知单批量
操作后,对应的客户端封装类如下所示。
/// <summary> ///对RFID控制的接口调用封装/// </summary> public classRFIDCaller : NormalApiService, IRFIDService
{publicRFIDCaller()
{this.ConfigurationPath = ApiConfig.ConfigFileName; //Web API配置文件 this.configurationName =ApiConfig.RFID;
}public CheckinResult CheckIn(string username, string password, string device_uuid, string device_type, string last_app_version, stringapp_id)
{var action =System.Reflection.MethodBase.GetCurrentMethod().Name;string url =GetNormalUrl(action);var postData = new{
username=username,
password=password,
device_uuid=device_uuid,
device_type=device_type,
last_app_version=last_app_version,
app_id=app_id,
}.ToJson();var result = JsonHelper<CheckinResult>.ConvertJson(url, postData);returnresult;
}public TagOrderAsnResult TagOrderAsn(int brand_id, string factcode, string start_time, string end_time, Pager.Entity.PagerInfo pagerInfo, stringtoken)
{var action =System.Reflection.MethodBase.GetCurrentMethod().Name;string url = GetNormalUrl(action) + string.Format("?token={0}", token);var postData = new{
page=pagerInfo.CurrenetPageIndex,
pagesize=pagerInfo.PageSize,
brand_id=brand_id,
factcode=factcode,
start_time=start_time,
end_time=end_time,
}.ToJson();var result = JsonHelper<TagOrderAsnResult>.ConvertJson(url, postData);returnresult;
}
获取标签发货通知单批量
的Web API接口如下代码定义
/// <summary> ///获取标签发货通知单批量/// </summary> /// <param name="param"></param> /// <param name="token"></param> /// <returns></returns> [HttpPost]public TagOrderAsnResult TagOrderAsn(JObject param, stringtoken)
{
TagOrderAsnResult result= null;dynamic obj =param;if (obj != null)
{//使用POST方式 var postData =param.ToJson();var queryUrl = "https://***.***.***/api/v6/rfid/tag/tag_order_asn/get"; var helper = newHttpHelper();
helper.ContentType= "application/json";
helper.Header.Add("token", token);var content = helper.GetHtml(queryUrl, postData, true);
RFIDBaseData<TagOrderAsnResult> jsonResult = JsonConvert.DeserializeObject<RFIDBaseData<TagOrderAsnResult>>(content);if (jsonResult != null && jsonResult.code == 0)
{
result=jsonResult.data;
}returnresult;
}else{throw new MyApiException("传递参数错误");
}
其中表头信息,我们通过下面的代码指定,设置特殊的token表头信息
var helper = newHttpHelper();
helper.ContentType= "application/json";
helper.Header.Add("token", token);
而在客户端的调用窗体里面,我们调用对应的接口就可以获取该接口的数据了。
privateTagOrderAsnResult asnResult;/// <summary> ///根据参数获取标签生产订单批量信息/// </summary> /// <returns></returns> privateTagOrderAsnResult GetResult()
{
PagerInfo pagerInfo= new PagerInfo() { PageSize = 50, CurrenetPageIndex = 1 };//初始化一个分页条件 var brand_id = this.txtbrand_id.Text.ToInt32();var factcode = this.txtfactcode.Text;var start_time = this.txtstart_time.DateTime.ToString("yyyy-MM-dd HH:mm:ss");var end_time = this.txtend_time.DateTime.ToString("yyyy-MM-dd HH:mm:ss");
asnResult= CallerFactory<IRFIDService>.Instance.TagOrderAsn(brand_id, factcode, start_time, end_time, pagerInfo, Token);returnasnResult;
}
通过上面的代码演示,我们了解了在混合框架基础上增加外部Web API接口的方法,通过增加Facade层接口,增加Web API接口,以及对应的客户端封装类,具体处理参数根据Web API接口的输入参数、输出数据等信息进行综合处理即可。
最后我们来看看数据的展示界面。
在GridControl表格控件中实现多层级主从表数据的展示
在一些应用场景中,我们需要实现多层级的数据表格显示,如常规的二级主从表数据展示,甚至也有多个层级展示的需求,那么我们如何通过DevExpress的GridControl控表格件实现这种业务需求呢?本篇随笔基于这个需求,对二级、三级的主从表数据进行展示,从而揭开对多层级数据展示的神秘面纱。
1、二级主从表数据展示
主从表数据,我们知道,一个主表记录里面关联有多条明细从表记录,在数据定义的层次上我们体现如下所示。
先定义一个实体类信息作为载体。
/// <summary> ///记录基础信息/// </summary> public classDetailInfo
{publicDetailInfo()
{this.ID =Guid.NewGuid().ToString();
}/// <summary> ///ID标识/// </summary> public string ID { get; set; }/// <summary> ///名称/// </summary> public string Name { get; set; }/// <summary> ///描述信息/// </summary> public string Description { get; set; }
}
然后主从表的数据实体类就是除了包含这些信息外,再包含一个子列表(列表信息不一定是同一个实体类),如下所示。
/// <summary> ///二级层次的列表/// </summary> public classDetail2Result : DetailInfo
{public List<DetailInfo> Detail2List { get; set; }
}
这个是我们使用继承关系简化了信息的定义,就是这个实体类包含主表信息外,还包含一个列表集合,属于从表数据的。
有了这些数据的定义,我们构建一些测试的数据,如下所示。
//创建测试数据 var result = newDetail2Result()
{
Name= "测试",
Description= "描述内容",
Detail2List= new List<DetailInfo>()
{newDetailInfo()
{
Name= "666666测试",
Description= "666666描述内容"},newDetailInfo()
{
Name= "222测试",
Description= "222描述内容"},newDetailInfo()
{
Name= "333测试",
Description= "333描述内容"}
}
};//构造一个记录的集合 var list = new List<Detail2Result>() { result };
这样我们就构建了一个主表从表记录的数据源,可以用于表格控件的绑定的了。
首先我们在界面上创建一个空白的窗体用于演示,并在窗体上增加一个GridControl控件用于展示主从表的数据,如下界面所示。
然后,我们可以通过代码创建我们需要的视图信息,如创建主表的GridView显示如下所示。
/// <summary> ///创建第一个视图/// </summary> private voidCreateGridView()
{var grv = this.gridView1;//创建从表显示的列 grv.Columns.Clear();
grv.CreateColumn("ID", "ID");//.Visible = false; grv.CreateColumn("Name", "名称");
grv.CreateColumn("Description", "描述内容");
grv.OptionsBehavior.ReadOnly= false;
grv.OptionsBehavior.Editable= true;
}
很简单,我们创建几个列,并指定它的Caption中文显示属性就可以了,然后我们接着还需要创建从表的GridView显示数据,这个是这篇随笔的关键。
具体的代码一次性贴出来,如下所示。
GridView grv2 = null;/// <summary> ///创建第二个视图/// </summary> private voidCreateLevelView()
{var grv = this.gridView1;var gridControl = this.gridControl1;//创建一个从表的GridView对象 grv2 = newGridView();
grv2.ViewCaption= "记录明细";
grv2.Name= "grv2";
grv2.GridControl=gridControl;//构建GridLevelNode并添加到LevelTree集合里面 var node = newGridLevelNode();
node.LevelTemplate=grv2;
node.RelationName= "Detail2List";//这里对应集合的属性名称 gridControl.LevelTree.Nodes.AddRange(newGridLevelNode[]
{
node
});//添加对应的视图集合显示 gridControl.ViewCollection.Clear();
gridControl.ViewCollection.AddRange(newBaseView[] { grv, grv2 });//创建从表显示的列 grv2.Columns.Clear();
grv2.CreateColumn("ID", "ID");
grv2.CreateColumn("Name", "名称");
grv2.CreateColumn("Description", "描述内容");//设置非只读、可编辑 grv2.OptionsBehavior.ReadOnly = false;
grv2.OptionsBehavior.Editable= true;
}
我们这里注意到 GridLevelNode 对象,它是我们主从表节点的关键信息,我们需要了解下面部分的代码
//构建GridLevelNode并添加到LevelTree集合里面 var node = newGridLevelNode();
node.LevelTemplate=grv2;
node.RelationName= "Detail2List";//这里对应集合的属性名称 gridControl.LevelTree.Nodes.AddRange(newGridLevelNode[]
{
node
});
首先是创建一个节点,然后指定它的 LevelTemplate 为我们新建的GridView,并且他的子集合对象名称为 Detail2List ,最后把这个节点的信息加入到 gridControl.LevelTree.Nodes 里面就可以了,其他的代码就和第一步差不多,指定显示的列和中文显示名称即可。
还有就是我们需要把创建的GridView 加入到指定的集合里面。
//添加对应的视图集合显示 gridControl.ViewCollection.Clear();
gridControl.ViewCollection.AddRange(new BaseView[] { grv, grv2 });
到这里基本上就是大功告成了,剩下的就是数据的绑定处理了。前面我们已经介绍了实体类的准备工作和创建测试数据的代码,那么我们这里沿用上面的代码进行数据的绑定就可以了。如下代码所示。
/// <summary> ///绑定数据源/// </summary> private voidBindData()
{//创建测试数据 var result = newDetail2Result()
{
Name= "测试",
Description= "描述内容",
Detail2List= new List<DetailInfo>()
{newDetailInfo()
{
Name= "666666测试",
Description= "666666描述内容"},newDetailInfo()
{
Name= "222测试",
Description= "222描述内容"},newDetailInfo()
{
Name= "333测试",
Description= "333描述内容"}
}
};//构造一个记录的集合 var list = new List<Detail2Result>() { result };//绑定数据源 this.gridControl1.DataSource =list;
}private void FrmTestDetails_Load(objectsender, EventArgs e)
{
BindData();
}
我们来运行下完成的程序界面,可以看到例子的效果界面如下所示。
我们可以看到数据记录是有树形节点的,展开就可以看到明细记录了,这个就是我们这里介绍的二级主从表数据的展示效果。
2、三级主从表数据展示
上面介绍了二级主从表的数据展示,其实GridControl可以用于展示三级以及更多层级的数据展示,只要你的数据设计合理,就可实现多层级的正确展示的。
本小节介绍三级的主从表数据展示,和二级数据展示类似,不过我们进一步实现了多层级的处理而已。
我们在二级层次的数据上定义了一个三级层次的数据实体类,如下所示。
/// <summary> ///二级层次的列表/// </summary> public classDetail2Result : DetailInfo
{public List<DetailInfo> Detail2List { get; set; }
}/// <summary> ///三级层次的列表/// </summary> public classDetail3Result : DetailInfo
{public List<Detail2Result> Detail3List { get; set; }
}
三级层次的测试数据初始化如下所示:
//创建测试数据 var result = newDetail3Result()
{
Name= "测试11",
Description= "描述内容11",//二级列表 Detail3List = new List<Detail2Result>()
{newDetail2Result()
{
Name= "测试22",
Description= "描述内容22",//三级列表 Detail2List = new List<DetailInfo>()
{newDetailInfo()
{
Name= "31测试",
Description= "31描述内容"},newDetailInfo()
{
Name= "32测试",
Description= "32描述内容"},newDetailInfo()
{
Name= "33测试",
Description= "33描述内容"}
}
}
}
};//构造一个记录的集合 var list = new List<Detail3Result>() { result };
和二级层次的处理步骤类似,我们先创建主表的信息展示,如下所示。
/// <summary> ///创建第一个视图/// </summary> private voidCreateGridView()
{var grv = this.gridView1;var gridControl = this.gridControl1;//创建从表显示的列 grv.Columns.Clear();
grv.CreateColumn("ID", "ID");//.Visible = false; grv.CreateColumn("Name", "名称");
grv.CreateColumn("Description", "描述内容");
grv.OptionsBehavior.ReadOnly= false;
grv.OptionsBehavior.Editable= true;
}
然后着手创建二级、三级的列表信息展示,
GridView grv2 = null;
GridView grv3= null;/// <summary> ///创建第二个视图/// </summary> private voidCreateLevelView()
{var grv = this.gridView1;var gridControl = this.gridControl1;//创建一个二级从表的GridView对象 grv2 = newGridView();
grv2.ViewCaption= "记录明细";
grv2.Name= "grv2";
grv2.GridControl=gridControl;//创建一个三级从表的GridView对象 grv3 = newGridView();
grv3.ViewCaption= "记录明细2";
grv3.Name= "grv3";
grv3.GridControl= gridControl;
这样我们相当于创建多两个(总共三个GridView对象)用于展示数据列表。
接着最为关键的是主从关系的节点,我们可以简单的理解他的Node节点和我们树形列表的Node处理方式类似即可。
//构建GridLevelNode var topNode = newGridLevelNode();
topNode.LevelTemplate= grv2; //这里是对应的视图 topNode.RelationName = "Detail3List"; //这里对应集合的属性名称//构建GridLevelNode var secondNode = newGridLevelNode();
secondNode.LevelTemplate= grv3; //这里是对应的视图 secondNode.RelationName = "Detail2List";//这里对应集合的属性名称//需要添加节点的层级关系,类似Tree节点处理 topNode.Nodes.Add(secondNode);//最后添加节点到集合里面 gridControl.LevelTree.Nodes.Add(topNode);
通过定义两个GridLevelNode,然后指定他们的Node关系( topNode.Nodes.Add(secondNode) ),这样我们就可以很清晰的关联起来它们的节点关系了。
最后是把我们创建的几个视图加入到集合里面,并设定一些关系即可。
//添加对应的视图集合显示 gridControl.ViewCollection.Clear();
gridControl.ViewCollection.AddRange(newBaseView[] { grv, grv2, grv3 });//创建从表显示的列 grv2.Columns.Clear();
grv2.CreateColumn("ID", "ID");
grv2.CreateColumn("Name", "名称");
grv2.CreateColumn("Description", "描述内容");//创建从表显示的列 grv3.Columns.Clear();
grv3.CreateColumn("ID", "ID");
grv3.CreateColumn("Name", "名称");
grv3.CreateColumn("Description", "描述内容");//设置非只读、可编辑 grv2.OptionsBehavior.ReadOnly = false;
grv2.OptionsBehavior.Editable= true;//设置非只读、可编辑 grv3.OptionsBehavior.ReadOnly = false;
grv3.OptionsBehavior.Editable= true;
整个部分的代码如下所示。
GridView grv2 = null;
GridView grv3= null;/// <summary> ///创建第二个视图/// </summary> private voidCreateLevelView()
{var grv = this.gridView1;var gridControl = this.gridControl1;//创建一个二级从表的GridView对象 grv2 = newGridView();
grv2.ViewCaption= "记录明细";
grv2.Name= "grv2";
grv2.GridControl=gridControl;//创建一个三级从表的GridView对象 grv3 = newGridView();
grv3.ViewCaption= "记录明细2";
grv3.Name= "grv3";
grv3.GridControl=gridControl;//构建GridLevelNode var topNode = newGridLevelNode();
topNode.LevelTemplate= grv2; //这里是对应的视图 topNode.RelationName = "Detail3List"; //这里对应集合的属性名称//构建GridLevelNode var secondNode = newGridLevelNode();
secondNode.LevelTemplate= grv3; //这里是对应的视图 secondNode.RelationName = "Detail2List";//这里对应集合的属性名称//需要添加节点的层级关系,类似Tree节点处理 topNode.Nodes.Add(secondNode);//最后添加节点到集合里面 gridControl.LevelTree.Nodes.Add(topNode);//添加对应的视图集合显示 gridControl.ViewCollection.Clear();
gridControl.ViewCollection.AddRange(newBaseView[] { grv, grv2, grv3 });//创建从表显示的列 grv2.Columns.Clear();
grv2.CreateColumn("ID", "ID");
grv2.CreateColumn("Name", "名称");
grv2.CreateColumn("Description", "描述内容");//创建从表显示的列 grv3.Columns.Clear();
grv3.CreateColumn("ID", "ID");
grv3.CreateColumn("Name", "名称");
grv3.CreateColumn("Description", "描述内容");//设置非只读、可编辑 grv2.OptionsBehavior.ReadOnly = false;
grv2.OptionsBehavior.Editable= true;//设置非只读、可编辑 grv3.OptionsBehavior.ReadOnly = false;
grv3.OptionsBehavior.Editable= true;
}
也就是我们在窗体初始化的时候,创建它们的视图关系即可,如下代码所示。
/// <summary> ///测试三级主从明细列表/// </summary> public partial classFrmTestDetails2 : BaseForm
{publicFrmTestDetails2()
{
InitializeComponent();
CreateGridView();
CreateLevelView();
}
最后就是数据源的绑定操作了,这个利用前面介绍过的准备数据即可。
private void FrmTestDetails2_Load(objectsender, EventArgs e)
{
BindData();
}/// <summary> ///绑定数据源/// </summary> private voidBindData()
{//创建测试数据 var result = newDetail3Result()
{
Name= "测试11",
Description= "描述内容11",//二级列表 Detail3List = new List<Detail2Result>()
{newDetail2Result()
{
Name= "测试22",
Description= "描述内容22",//三级列表 Detail2List = new List<DetailInfo>()
{newDetailInfo()
{
Name= "31测试",
Description= "31描述内容"},newDetailInfo()
{
Name= "32测试",
Description= "32描述内容"},newDetailInfo()
{
Name= "33测试",
Description= "33描述内容"}
}
}
}
};//构造一个记录的集合 var list = new List<Detail3Result>() { result };//绑定数据源 this.gridControl1.DataSource =list;
}
以上就是三级层次的关系处理,如果我们理解了,其他更多层级的数据展示也是依照这个规则,增加节点和视图即可,原理一样。
案例的效果如下所示。
3、利用分页控件实现数据的展示
上面的两个案例是基于DevExpress的内置表格控件GridControl进行处理的,我们在Winform框架的开发过程中,往往为了效率和分页方便,一般都是使用分页控件来展示数据的,那么利用分页控件实现多层级的数据展示是如何的呢?
其实基本步骤也是差不多的,只是主表视图使用分页控件即可,如下所示。
/// <summary> ///数据指定的主从表展示/// </summary> public partial classFrmDictTypeMasterDetail : BaseDock
{publicFrmDictTypeMasterDetail()
{
InitializeComponent();
InitDictItem();this.winGridViewPager1.OnPageChanged += newEventHandler(winGridViewPager1_OnPageChanged);this.winGridViewPager1.OnStartExport += newEventHandler(winGridViewPager1_OnStartExport); this.winGridViewPager1.OnDeleteSelected += newEventHandler(winGridViewPager1_OnDeleteSelected);this.winGridViewPager1.OnRefresh += newEventHandler(winGridViewPager1_OnRefresh);this.winGridViewPager1.AppendedMenu = this.contextMenuStrip1;this.winGridViewPager1.ShowLineNumber = true;this.winGridViewPager1.BestFitColumnWith = false;//是否设置为自动调整宽度,false为不设置 this.winGridViewPager1.gridView1.DataSourceChanged += newEventHandler(gridView1_DataSourceChanged);this.winGridViewPager1.gridView1.CustomColumnDisplayText += newDevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventHandler(gridView1_CustomColumnDisplayText);this.winGridViewPager1.gridView1.RowCellStyle += newDevExpress.XtraGrid.Views.Grid.RowCellStyleEventHandler(gridView1_RowCellStyle);CreateLevelView();
RegisterEvent();
}
GridView grv2= null;private voidCreateLevelView()
{var grv = this.winGridViewPager1.GridView1;var gridControl = this.winGridViewPager1.gridControl1;//创建一个从表的GridView对象 grv2 = newGridView();
grv2.ViewCaption= "记录明细";
grv2.Name= "grv2";
grv2.GridControl=gridControl;//构建GridLevelNode并添加到LevelTree集合里面 var node = newGridLevelNode();
node.LevelTemplate=grv2;
node.RelationName= "Children";
gridControl.LevelTree.Nodes.AddRange(newGridLevelNode[]
{
node
});
gridControl.ViewCollection.Clear();
gridControl.ViewCollection.AddRange(newBaseView[] { grv, grv2 });//创建从表显示的列 grv2.Columns.Clear();
grv2.CreateColumn("ID", "ID").Visible =false; //标识行的关键字,可用于删除处理 grv2.CreateColumn("DictType_ID", "DictType_ID").Visible = false;//创建一个字段,隐藏的,存储记录 grv2.CreateColumn("Name", "项目名称");
grv2.CreateColumn("Value", "项目值");
grv2.CreateColumn("Seq", "排序");
grv2.CreateColumn("Remark", "备注");
grv2.OptionsBehavior.ReadOnly= false;
grv2.OptionsBehavior.Editable= true;
grv2.DataSourceChanged+=grv2_DataSourceChanged;
}
以上就是基于GridControl实现数据的主从关系的处理,可以实现多层级的展示,希望这些案例能够对你展示数据有所帮助。
使用EasyNetQ组件操作RabbitMQ消息队列服务
RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue)的开源实现,是实现消息队列应用的一个中间件,消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。EasyNetQ则是基于官方.NET组件RabbitMQ.Client 的又一层封装,使用起来更加方便。本篇随笔主要大概介绍下RabbitMQ的基础知识和环境的准备,以及使用EasyNetQ的相关开发调用。
1、RabbitMQ基础知识
AMQP
,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的
中间件
设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
RabbitMQ
是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
RabbitMQ的特点
:
强大的
应用
程序消息传递;
使用方便;
运行在所有主要
操作系统上;支持大量开发人员平台;
开源
和
商业支持。消息队列的模式有两种模式:P2P(Point to Point),P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。Publish/Subscribe(Pub/Sub),包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
EasyNetQ
的目标是提供一个使.NET中的RabbitMQ尽可能简单的库。在EasyNetQ中消息应由.NET类型表示,消息应通过其.NET类型进行路由。EasyNetQ按消息类型进行路由。发布消息时,EasyNetQ会检查其类型,并根据类型名称,命名空间和装配体给出一个路由密钥。在消费方面,用户订阅类型。订阅类型后,该类型的消息将路由到订户。默认情况下,EasyNetQ使用Newtonsoft.Json库将.NET类型序列化为JSON。
这具有消息是人类可读的优点,因此您可以使用RabbitMQ管理应用程序等工具来调试消息问题。
EasyNetQ是在RabbitMQ.Client库之上提供服务的组件集合。这些操作可以像序列化,错误处理,线程编组,连接管理等。它们由mini-IoC容器组成。您可以轻松地用自己的实现替换任何组件。因此,如果您希望XML序列化而不是内置的JSON,只需编写一个ISerializer的实现并将其注册到容器。
以下是官方提供的一个结构图,这个结构图可以很好的解析该组件的结构:
2、RabbitMQ的环境准备
本处主要介绍在Windows系统中安装RabbitMQ。
1. 下载安装erlang
下载地址
http://www.erlang.org/downloads
(根据操作系统选择32还64位)
2. 下载安装rabbitmq-server
下载地址
http://www.rabbitmq.com/install-windows.html
下载后获得两个安装文件,按照顺序安装即可
安装erlang环境后,一般会添加了一个ERLANG_HOME的系统变量,指向erlang的安装目录路径,如下所示(
一般都添加了,确认下
)
安装RabbitMQ后,在程序里面可以看到
我们使用它的命令行来启动RabbitMQ的服务
查看安装是否成功命令 :
rabbitmqctl status
安装成功,在浏览器中输入
http://127.0.0.1:15672/
,可以看到如下界面,使用默认的账号密码均为guest登陆进行管理
guest 账号是管理员账号,可以添加Exchanges,Queues,Admin。但我们一般不使用guest账号,可以选择用命令来添加账号和权限,也可以使用管理界面进行添加相应的内容。
例如我添加相应的用户账号
一般我们还需要添加虚拟机,默认的虚拟机为/,我这里添加了一个虚拟机myvhost。
然后绑定账号到虚拟机上即可。
3、EasyNetQ组件的使用
EasyNetQ组件的使用方式比较简单,跟很多组件都类似,例如:建立连接,进行操作做等等,对于EasyNetQ组件也是如此。
在.NET中使用EasyNetQ,要求至少基于 .NET4.5的框架基础上进行开发,可以直接在VS项目上使用NuGet的程序包进行添加EasyNetQ的引用。
一般添加引用后,至少包含了下面图示的几个引用DLL。
1)创建连接:
使用EasyNetQ连接RabbitMQ,是在应用程序启动时创建一个IBus对象,并且,在应用程序关闭时释放该对象。
RabbitMQ连接是基于IBus接口的,当IBus中的方法被调用,连接才会开启。创建一个IBus对象的方法如下:
var bus = RabbitHutch.CreateBus(“host=myServer;virtualHost=myVirtualHost;username=admin;password=123456”);
与RabbitMQ服务器的延迟连接由IBus接口表示,创建连接的方式连接字符串由格式为key = value的键/值对组成,每一个用分号(;)分隔。
- host,host=localhost 或者host =192.168.1.102或者host=my.rabbitmq.com,如果用到集群配置的话,那么可以用逗号将服务地址隔开,例如host=a.com,b.com,c.com
- virtualHost,虚拟主机,默认为'/'
- username,用户登录名
- password,用户登录密码
- requestedHeartbeat,心跳设置,默认是10秒
- prefetchcount,默认是50
- pubisherConfirms,默认为false
- persistentMessages,消息持久化,默认为true
- product,产品名
- platform,平台
- timeout,默认为10秒
一般我们在代码里面测试的话,简化连接代码如下所示。
//初始化bus对象 bus = RabbitHutch.CreateBus("host=localhost");
2
)
关闭连接:
bus.Dispose();
要关闭连接,只需简单地处理总线,这将关闭EasyNetQ使用的连接,渠道,消费者和所有其他资源。
如果我们在Winform窗体里面初始化一个IBus对象,那么在窗体关闭的时候,关闭这个接口即可。
private void FrmPublisher_FormClosed(objectsender, FormClosedEventArgs e)
{//关闭IBus接口 if(bus != null)
{
bus.Dispose();
}
}
3
)
发布消息:
EasyNetQ支持最简单的消息模式是发布和订阅。发布消息后,任意消费者可以订阅该消息,也可以多个消费者订阅。并且不需要额外配置。首先,如上文中需要先创建一个IBus对象,然后,在创建一个可序列化的.NET对象。调用Publish方法即可。
var message = new MyMessage { Text = "Hello Rabbit"};
bus.Publish(message);
4
)
订阅消息:
EasyNetQ提供了消息订阅,当调用Subscribe方法时候,EasyNetQ会创建一个用于接收消息的队列,不过与消息发布不同的是,消息订阅增加了一个参数,subscribe_id.代码如下:
bus.Subscribe<MyMessage>("my_subscription_id", msg => Console.WriteLine(msg.Text));
第一个参数是订阅id,另外一个是delegate参数,用于处理接收到的消息。这里要注意的是,subscribe_id参数很重要,假如开发者用同一个subscribeid订阅了同一种消息类型两次或者多次,RabbitMQ会以轮训的方式给每个订阅的队列发送消息。接收到之后,其他队列就接收不到该消息。如果用不同的subscribeid订阅同一种消息类型,那么生成的每一个队列都会收到该消息。
需要注意的是,在收到消息处理消息时候,不要占用太多的时间,会影响消息的处理效率,所以,遇到占用长时间的处理方法,最好用异步处理。
为了测试发布和订阅消息,我们可以建立几个不同的项目来进行测试,如发布放在一个Winform项目,订阅放在一个Winform项目,另外一个项目放置共享的消息对象定义,如下所示。
定义消息对象类如下所示。
/// <summary> ///定义的MQ消息类型/// </summary> public classTextMessage
{public string Text { get; set; }
}
然后在发布消息的Winform项目上创建一个处理的窗体,并添加如下代码。
namespaceMyRabbitMQ.Publisher
{/// <summary> ///测试RabbitMQ消息队列的发布/// </summary> public partial classFrmPublisher : DevExpress.XtraEditors.XtraForm
{//构建一个IBus公用接口对象 private IBus bus = null;publicFrmPublisher()
{
InitializeComponent();//初始化bus对象 bus = RabbitHutch.CreateBus("host=localhost");//对指定消息类型进行回应 bus.Respond<MyRequest, MyResponse>(request => new MyResponse { Text = "Responding to:"+request.Text});//收到消息后输出到控制台上显示 bus.Receive("my.queue", x =>x
.Add<MyMessage>(message =>Console.WriteLine(message.ToJson()))
.Add<MyOtherMessage>(message =>Console.WriteLine(message.ToJson())));
}
发布消息的处理代码,如下代码所示。
private void btnSend_Click(objectsender, EventArgs e)
{if (bus != null)
{
bus.Publish(newTextMessage
{
Text= this.txtContent.Text
});
}
}
然后在创建一个类似窗体,用来订阅消息的处理窗体,如下所示代码和窗体。
namespaceMyRabbitMQ.Subcriber
{/// <summary> ///测试RabbitMQ消息队列的订阅/// </summary> public partial classFrmSubcriber : DevExpress.XtraEditors.XtraForm
{//构建一个IBus公用接口对象 private IBus bus = null;publicFrmSubcriber()
{
InitializeComponent();//初始化bus对象 bus = RabbitHutch.CreateBus("host=localhost");if(bus != null)
{//订阅一个消息,并对接收到的消息进行处理,展示在控件上 bus.Subscribe<TextMessage>("test", (msg) =>{
StringBuilder sb= newStringBuilder();
sb.AppendLine(msg.Text+ "," +DateTime.Now.ToString());
sb.AppendLine(this.txtContent.Text);this.txtContent.Invoke(new MethodInvoker(delegate()
{this.txtContent.Text =sb.ToString();
}));
});
}//使用消息发送接口发送消息 bus.Send("my.queue", new MyMessage { Text = "Hello Widgets!"});
bus.Send("my.queue", new MyOtherMessage { Text = "Hello wuhuacong!"});
}
发送请求获取响应的代码如下所示。
private void btnRequest_Click(objectsender, EventArgs e)
{//定义请求消息的对象 var request = newMyRequest()
{
Text= string.Format("请求消息,{0}", DateTime.Now)
};//异步获取请求消息的结果并进行处理,展示应答消息在窗体中的 var task = bus.RequestAsync<MyRequest, MyResponse>(request);
task.ContinueWith(response=>{
StringBuilder sb= newStringBuilder();
sb.AppendLine(response.Result.Text);
sb.AppendLine(this.txtContent.Text);this.txtContent.Invoke(new MethodInvoker(delegate()
{this.txtContent.Text =sb.ToString();
}));
});
}
两个项目联合进行测试如下界面所示。
发布者多次发送消息的情况下,订阅者中,会进行消息的轮训处理,也就是进行均匀分配。
5)消息发送(Send)和接收(Receive)
与Publish/Subscribe略有不同的是,Send/Receive 可以自己定义队列名称。
//发送端代码 bus.Send("my.queue", new MyMessage{ Text = "Hello Widgets!"});//接收端代码 bus.Receive<MyMessage>("my.queue", message => Console.WriteLine("MyMessage: {0}", message.Text));
并且,也可以在同一个队列上发送不同的消息类型,Receive方法可以这么写:
bus.Receive("my.queue", x =>x
.Add<MyMessage>(message => deliveredMyMessage =message)
.Add<MyOtherMessage>(message => deliveredMyOtherMessage = message));
如果消息到达队列,但是没有发现相应消息类型的处理时,EasyNetQ会发送一条消息到error队列,并且,带上一个异常信息:No handler found for message type <message type>。与Subscribe类型,如果在同一个队列,同一个消息类型,多次调用Receive方法时,消息会通过轮询的形式发送给每个Receive端。
6)远程过程调用:
var request = new TestRequestMessage {Text = "Hello from the client! "}; bus.Request<TestRequestMessage, TestResponseMessage>(request, response => Console.WriteLine("Got response: '{0}'", response.Text));
7
)
RPC服务器:
bus.Respond<TestRequestMessage, TestResponseMessage>(request => new TestResponseMessage{ Text = request.Text + " all done!" });
8
)
记录器:
var logger = new MyLogger() ; var bus = RabbitHutch.CreateBus(“my connection string”, x => x.Register<IEasyNetQLogger>(_ => logger));
9
)
路由:
Publish方法,可以加一个topic参数。
bus.Publish(message, "X.A");
消息订阅方可以通过路由来过滤相应的消息。
* 匹配一个字符
#匹配0个或者多个字符
所以 X.A.2 会匹配到 "#", "X.#", "*.A.*" 但不会匹配 "X.B.*" 或者 "A". 当消息订阅需要用到topic时候,需要调用Subscribe的重载方法
bus.Subscribe("my_id", handlerOfXDotStar, x => x.WithTopic("X.*"));
bus.Subscribe("my_id", handlerOfStarDotB, x => x.WithTopic("*.B"));
上述这种方式,会将消息轮询发送给两个订阅者,如果只需要一个订阅者的话,可以这么调用:
bus.Subscribe("my_id", handler, x => x.WithTopic("X.*").WithTopic("*.B"));
RabbitMQ具有非常好的功能,
基于主题的路由
,允许订阅者基于多个标准过滤消息。*(星号)匹配一个字。#(哈希)匹配为零个或多个单词。
RabbitMQ的应用场景,一般在快速处理订单,以及异步的多任务处理中可以得到很好的体现,下面是几个应用场景。
邮件和短消息的处理
订单的解耦处理
RabbitMQ的服务器架构
3、RabbitMQ查询状态出现错误的处理
安装成功之后使用rabbitmqctl status命令之后出现如下错误。
Status of node rabbit@WUHUACONG ...Error: unable to perform an operationon node 'rabbit@WUHUACONG'. Please see diagnostics information and suggestions below.Most common reasonsforthis are:
* Target node is unreachable(e.g. due to hostname resolution, TCP connection or firewall issues)* CLI tool fails to authenticate with the server(e.g. due to CLI tool's Erlang cookie not matching that of the server)* Target node isnotrunning
In addition to the diagnostics info below:
* See the CLI, clustering and networking guides on http://rabbitmq.com/documentation.html to learn more
* Consult server logsonnode rabbit@WUHUACONG
DIAGNOSTICS===========attempted to contact: [rabbit@WUHUACONG]
rabbit@WUHUACONG:
* connected to epmd(port 4369) onWUHUACONG
* epmd reports node 'rabbit' uses port25672 forinter-node and CLI tool traffic
* TCP connection succeeded but Erlang distribution failed
* Authentication failed(rejected by the remote node),please check the Erlang cookie
Current node details:
* node name: rabbitmqcli100@WUHUACONG
* effective user's home directory: C:\Users\Administrator
* Erlang cookie hash: RgaUM2coc+rxIhJrfLS7Jw==
这个问题出现比较常见,主要原因是两个目录的.erlang.cookie文件内容不一样。
要确保.erlang.cookie文件的一致性,不知道什么原因导致了C:\Users\{UserName}\.erlang.cookie和默认情况下C:\WINDOWS\System32\config\systemprofile\.erlang.cookie不一致了,将Windows目录下的拷贝到用户目录下就可以了。
反正无论如何,两个地址的Cookie内容一致就可以了,然后重启下RabbitMQ服务器即可正常运行,并可以正常获取它的状态信息。