转眼到了毕业年级,恐怕自己能力有限,错失就业机会,在大四课程完成后自行联系了几家公司,笔试、面试,最后进入了目前的.NET开发岗位。学习已接近两个月,最近几天按照前辈的要求开发一个练手项目,进行信息管理和订单管理。其中信息管理模块主要使用C#及Windows窗体应用开发基础知识,订单管理模块主要通过调用接口,进行订单提交,同时本地存入Mysql数据库进行管理。
写此笔记,主要是用于记录自己开发过程中遇到的问题和解决方式,加深记忆,方便日后遇到类似问题快速找出解决方式。激励自己的同时与社区其它IT行业新人共勉。
一、基础知识补充
1.时间戳(timestamp)
时间戳(timestamp)是一种标准的时间表示方式,通常是一个字符序列,用以唯一的标识某一时刻的时间,Unix时间戳定义为从格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至指定时间的秒数,不考虑闰秒。
C#中十位时间戳的计算:
(DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000
相对的,十三位的时间戳计算为:
(DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000
计算出的结果为对应位数的整形数字,一般存放于long类型的变量中
2.MD5加密
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种不可逆的加密算法,只有明文相同的情况下,才能得到相同的密文。百度上查到的信息说MD5算法不可能反解密出明文,具体能否解密我并不清楚,但我的提交需要用到MD5加密,于是我将具体加密过程封装于MD5加密方法中。
/// <summary>
/// MD5加密
/// </summary>
/// <param name="str">待加密字符串</param>
/// <returns>加密并转大写后的字符串</returns>
public static string Md5Encrypt(string str)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] byteArray = md5.ComputeHash(System.Text.Encoding.Default.GetBytes(str));
string result = "";
foreach (byte a in byteArray)
{
if (a < 16)
result += "0" + a.ToString("X");
else
result += a.ToString("X");
}
return result.ToUpper();
}
3.处理JSON
我调用的接口返回信息使用Json格式,需要对其进行一定的操作。
我使用了Newtonsoft.Json,简洁高效,具体举个例子:
var postRequestResult = JsonConvert.DeserializeObject<dynamic>(url,data));
二、Http请求
Http请求在C#中有很多种实现方式,我个人大体总结为三种:
HttpClient、HttpWeb,第三方类库
前两种由微软官方提供,其中HttpWeb虽然你现在可以在微软官方文档中找到,但是官方也会提示你,这是一种过时的方式,推荐你使用HttpClient。第三方类库不必说,我们开发者中的大牛自己写出的方法,往往更简洁高效。
这里列出使用HttpClient进行的GET和POST请求,加深记忆,方便取用。
GET
/// <summary>
/// 使用HttpClient进行异步Get请求
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="data">提交数据</param>
/// <returns></returns>
public static string HttpClientGet(string url, string data)
{
string strResult = "";
url = url + "?" + data;
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = httpClient.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
strResult = response.Content.ReadAsStringAsync().Result;
}
return strResult;
}
POST:
/// <summary>
/// 使用HttpClient 进行Post请求
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="data">提交信息</param>
/// <returns></returns>
public static string HttpClientPost(string url, string data)
{
//HttpClient推荐使用单一实例共享使用,发送请求的方法推荐使用异步的方式
//声明返回值
string strResult = "";
//可转换提交数据为json
//var str = JsonConvert.SerializeObject(data);
var httpContent = new StringContent(data, Encoding.UTF8, "application/json");
HttpClient httpClient = new HttpClient();
HttpResponseMessage responseMessage =
httpClient.PostAsync(url, httpContent).Result;
if (responseMessage.IsSuccessStatusCode)
{
strResult = responseMessage.Content.ReadAsStringAsync().Result;
}
return strResult;
}
三、关于日志的处理
日志处理这里遇到了两个问题:
1.读取日志乱码
录入以及打开本地文件都没有发现问题,在程序中读取时发现乱码。
解决方式:设置执行ReadAllLines的编码格式为UTF8(其实编写时这里我不是没有设置,而是设置了GB2312,但一直没有起作用)
array = File.ReadAllLines(path, Encoding.UTF8 );//path即要读取的日志的路径
2.读取日志不换行
即本地日志文件没有问题,读取并展示在windows窗体应用的textbox控件中时,应当换行的地方没有换行。这个问题真的是不看不知道,一看恍然大悟。具体原因放在下面:
//"\n"只是一个换行符(Unicode U + 000A).这通常是Unix行分隔符.
// "\r\n"是回车符(Unicode U + 000D),后跟换行符(Unicode U + 000A).这通常是Windows行分隔符.
//textbox中的换行\n是在window上执行的,而window上执行换行的时候需要\r\n,所以就导致了\n无法换行
//但是在Linux系统和其他平台上却是\n换行或者其他的,这样就不能保证软件在每个平台上都正确换行
四、关于订单提交的问题
最主要的问题就是在我写完订单提交相关功能后前辈给我提的一个问题
在写提交订单的模块的时候,要求是多线程提交订单。我的想法是用户每输入一条订单信息,暂存,等待用户点击提交,开启多线程进行提交。在提交返回请求通过信息后进行入库操作。
所以我被问的问题就是:如果提交过程中程序意外关闭,如何保证数据的准确性?
我第一遍听以为是说提交开启的线程还没结束,程序就关闭的情况,这个我是写明了等待所有提交线程结束的。但前辈的意思是整个程序意外完全关闭。这个问题困扰了我一上午,于是我跟另一位前辈请教了一下,跟他说明了我现在是如何做的。对话大概是:
前辈:“你是怎么提交订单操作数据的?”
我:“我是先调用接口,提交订单信息,返回通过后入库。”
前辈:“问题就出在这儿啊。”
我:“这个……我不明白。”
前辈:“因为提交过程可能会出问题,所以你要先入库!”
我:“那入库了再提交如何保证订单信息的正确呢?”
前辈:“这个没关系,之后进行订单查询,完全可以获取订单的真实状态,我们一般都会做一个自动更新来获取订单实时状态。”
…………
于是我恍然大悟,我的想法过于简单,考虑得太不全面,或者说还只是个学生思想,并不是一个合格的开发者。在学习过程中,我一度认为核对信息后入库是理所应当的,才是合乎逻辑的,这主要是因为学习时的项目基本基于本地,操作较为随意,我甚至可以随便修改自己本地的数据库。真正的程序开发中,数据的准确性、安全性太过重要,因此,上面所说的先入库再提交、再查询检测这种看似更麻烦的方式反而才是正解。
最后的解决方案:用户点击提交时,订单信息先入库,增加订单处理状态“SUBMIT”,指示订单处理提交中或未提交状态,调用接口发送请求进行提交,若请求通过,修改该订单的数据库数据,将返回数据更新到数据库(包括订单状态)。
而订单管理界面的载入方法,执行一次订单提交方法,检索出数据库中处于“SUBMIT”状态的订单,进行提交操作,这是为了自动提交用户点击提交,入库后因意外情况未进行提交的订单信息,只执行一次。另外加入一个Timer,每十秒调用一次查询接口,查询SUBMIT和RECHARGE(充值中)状态的订单,返回其新状态更新到数据库。