采用异步来实现重新连接服务器或者重新启动服务

开启异步监听,不会导致主线程的堵塞,在服务异常断开后一直检测重新连接服务,成功连接服务后通知各个注册的客户端!

#region 检测断线并重连OPC服务 可以强制启动OPC服务程序
/// <summary>
/// 提供外部使用
/// 重新连接事件
/// </summary>
public event LinkStateChangedEvent OnLinkStateChanged;

/// <summary>
/// 当前连接状态
/// </summary>
volatile ELinkState curLinkState = ELinkState.Unkown;
/// <summary>
/// 获取或设置当前连接状态
/// </summary>
ELinkState CurLinkState
{
get
{
lock (this)
{
return curLinkState;
}
}
set
{
lock (this)
{
curLinkState = value;
}
}
}
/// <summary>
/// 检测间隔毫秒数
/// </summary>
const int _checkInterval = 3000;
/// <summary>
/// 空闲心跳时间
/// </summary>
AutoResetEvent _waitCheckAndTryReLinkEvent = new AutoResetEvent(false);
/// <summary>
/// 是否正在检测并尝试重连
/// </summary>
volatile bool _isCheckingAndTryReLink = false;
/// <summary>
/// 获取或设置是否正在检测并尝试重连
/// </summary>
bool IsCheckingAndTryReLink
{
get
{
lock (this)
{
return _isCheckingAndTryReLink;
}
}
set
{
lock (this)
{
_isCheckingAndTryReLink = value;
}
}
}

/// <summary>
/// 启动检测并尝试重连
/// </summary>
void StartCheckAndTryReLink()
{
IsCheckingAndTryReLink = true;
BeginWaitCheckAndTryReLink();
}
/// <summary>
/// 开始等待检测并尝试重连
/// </summary>
void BeginWaitCheckAndTryReLink()
{
//开始异步等待
ThreadPool.RegisterWaitForSingleObject(_waitCheckAndTryReLinkEvent, WaitCheckAndTryReLinkCallBack, null, _checkInterval, true);
}
/// <summary>
/// 等待检测并尝试重连回调函数
/// </summary>
/// <param name="state"></param>
/// <param name="timedOut"></param>
void WaitCheckAndTryReLinkCallBack(Object state, Boolean timedOut)
{
//检测断线并重连
try
{
OPCAutomation.OPCServer service = GetOPCServer();
if (service != null)
{//连接正常
if (CurLinkState != ELinkState.Connected)
{
ELinkState oldState = CurLinkState;
CurLinkState = ELinkState.Connected;
OutLinkStateChanged(oldState, CurLinkState);
}
}
else
{//连接已断开
if (CurLinkState != ELinkState.Disconnected)
{
ELinkState oldState = CurLinkState;
CurLinkState = ELinkState.Disconnected;
OutLinkStateChanged(oldState, CurLinkState);
}
#region 尝试重连
//先注销
//try
//{
// service.Disconnect();
// // remotingClient.UnregisterChannel();
//}
//catch(Exception ex)
//{

//}
//等待一下
System.Threading.Thread.Sleep(3000);
//再重新注册
try
{
// remotingClient.RegisterChannel();
Server = new OPCAutomation.OPCServer();//重新初始化服务
Server.Connect(ServerName, IP);
//Server = service;
OPCIsConnected = true;
Task.Factory.StartNew(CreateGroupsInfos);
}
catch
{
OPCIsConnected = false;
}
#endregion
}
}
catch
{
}
//进入下一轮
if (IsCheckingAndTryReLink)
{
BeginWaitCheckAndTryReLink();
}
}
/// <summary>
/// 停止检测并尝试重连
/// </summary>
void StopCheckAndTryReLink()
{
IsCheckingAndTryReLink = false;
_waitCheckAndTryReLinkEvent.Set();
}

/// <summary>
/// 委托:连接状态发生变化
/// </summary>
/// <param name="oldState">旧状态</param>
/// <param name="newState">新状态</param>
void OutLinkStateChanged(ELinkState oldState, ELinkState newState)
{
if (OnLinkStateChanged != null)
{
//广播
LinkStateChangedEvent tempEvent = null;
foreach (Delegate del in OnLinkStateChanged.GetInvocationList())
{
try
{
tempEvent = (LinkStateChangedEvent)del;
tempEvent.BeginInvoke(oldState, newState, null, null);
}
catch
{
}
}
}
}

#endregion

public enum ELinkState
{
/// <summary>
/// 未知状态
/// </summary>
[Description("未知状态")]
Unkown = -1,
/// <summary>
/// 断开
/// </summary>
[Description("已断开")]
Disconnected = 0,
/// <summary>
/// 已连接
/// </summary>
[Description("已连接")]
Connected = 1
}

C#中类的属性的获取

/// <summary>
/// 将多个实体转换成一个DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public static DataTable EntityToDataTable<T>(List<T> list)
{
DataTable dt = new DataTable();
string res = typeof(T).ToString();
int start = res.IndexOf('.') + 1;

//初始化DataTable的值 包括表名、列
dt.TableName = res.Substring(start, res.Length - start);
Dictionary<string, object> dic = Utils.GetAllAttributes<T>(list[0]);
if (dic==null||dic.Count==0)
{
throw new Exception("无法转换数据!");
}
foreach (string key in dic.Keys)
{
// Type type = dic[key].GetType();
dt.Columns.Add(key, dic[key].GetType());
}
foreach (T t in list)
{
dic = new Dictionary<string, object>();
dic = Utils.GetAllAttributes<T>(t);
List<object> lists = new List<object>();
foreach (object item in dic.Values)
{
lists.Add(item);
}
DataRow dr = dt.NewRow();
dr.ItemArray = lists.ToArray();
dt.Rows.Add(dr);
}

return dt;
}

/// <summary>
/// 获取实体的所有属性
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static Dictionary<string, object> GetAllAttributes<T>(T t)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
if (t == null)
{
return dic;
}
PropertyInfo[] infos = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (infos == null || infos.Length <= 0)
{
return dic;
}
foreach (PropertyInfo info in infos)
{
string Name = info.Name;
object value = info.GetValue(t, null);
if (info.PropertyType.IsValueType || info.PropertyType.Name.StartsWith("String")||info.PropertyType.FullName.Contains("System"))
{
if (dic.ContainsKey(Name))
{
continue;
}
dic.Add(Name, value);
}
//else
//{
// GetAllAttributes(value);
//}
}
return dic;
}

SignalR2简易数据看板演示

 

软件环境:

1、vs2015、windows7、.net4.5

演示说明:

当点击按钮的时候,柱状图数值加1并实时变化

1、首先打开vs2015创建一个mvc项目,并安装SignalR2,具体操作可参见:http://net-yuan.com/Article/Detail/e407a472-338a-458d-9d7a-4a9e43fd2da5

2、右键项目,新建文件夹,命名为Hubs,添加命名为ChartHub的SignalR集线器类

3、右键项目,新建文件夹,命名为Services,添加命名为ChartService的新类

4、ChartService类中创建一个方法NotifyUpdates,代码如下:

1
2
3
4
5
6
7
8
9
public async Task NotifyUpdates()
        {
            var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChartHub>();
            if (hubContext != null)
            {
                var stats = await this.<br>GetCount();
                hubContext.Clients.All.updateChart(stats);
            }
        }

5、再创建两个方法,分别模拟获取计数和增长计数的过程,实际中,可以从数据库查询和数据写入数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
    /// 模拟数据类
    /// </summary>
    public static class D
    {
        public static int Count { getset; }
    }
 
    public async Task<int> GetCount()
        {
            return D.Count;
        }
 
        public async Task<int> IncreaceCount()
        {
            D.Count = D.Count + 1;
            return D.Count;
        }

6、创建ApiController,命名为DataController,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DataController : ApiController
    {
        private ChartService chartService;
 
        public DataController()
        {
            this.chartService = new ChartService();
        }
 
        // POST api/<controller>
        public async Task<HttpResponseMessage> Post()
        {
            var isCorrect = await this.chartService.IncreaceCount();
 
            await this.chartService.NotifyUpdates();
 
            return Request.CreateResponse(HttpStatusCode.Created, isCorrect);
        }
    }

7、视图Home/Index,代码如下:

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
<br />
<p><button class="btn btn-info" onclick="clkTest();">数据模拟</button></p>
 
<p>1、本示例只是一个最小场景的演示</p>
<p>2、当点击按钮的时候,柱状图数值加1并实时变化</p>
<p>3、拆开来看,将点击操作看作服务器对数据的变化的发布;将柱状图数据变化看作是对服务器数据的订阅。这就是典型的数据推送,数据看板就是其中的一项应用。</p>
<p>4、可以有好多变化:如果将点击按钮的动作替换为每n(s)自动触发一次;如果能感知数据库某个表的数据变化来触发图表变化</p>
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
 
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/echarts.simple.min.js"></script>
@section Scripts {
    <script src="@Url.Content("~/Scripts/jquery.signalR-2.4.0.min.js")"></script>
    <script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>
    <script>
        // 基于准备好的dom,初始化echarts实例
        var myChart = echarts.init(document.getElementById('main'));
 
        // 指定图表的配置项和数据
        option = {
            color: ['#3398DB'],
            tooltip: {
                trigger: 'axis',
                axisPointer: {            // 坐标轴指示器,坐标轴触发有效
                    type: 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
                }
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            xAxis: [
                {
                    type: 'category',
                    data: ['SignalR'],
                    axisTick: {
                        alignWithLabel: true
                    }
                }
            ],
            yAxis: [
                {
                    type: 'value'
                }
            ],
            series: [
                {
                    name: '直接访问',
                    type: 'bar',
                    label: {
                        normal: {
                            show: true,
                            position: 'inside'
                        }
                    },
                    barWidth: '30%',
                    data: [0]
                }
            ]
        };
 
        var connection = $.hubConnection();
        var hub = connection.createHubProxy("ChartHub");
        hub.on("updateChart", function (chart) {
            statisticsData = chart;
            console.log(statisticsData);
            option.series[0].data[0] = statisticsData;
            // 使用刚指定的配置项和数据显示图表。
            myChart.setOption(option);
            //$("#spCount").text(statisticsData);
        });
 
        connection.start();
    </script>
}
 
<script>
    $(function () {
        myChart.setOption(option);
    })
 
    var clkTest = function () {
        $.post("/api/Data", null, function (res) {
            console.log(res);
            //$("#spClicks").text(res);
        }, "json")
    }
</script>

8、演示如下,打开两个客户端,可以看到,当点击按钮的时候,两个客户端的柱状图都会变化

 

GitHub:https://github.com/net-yuan/SignalR-RealTime

C#动态调用泛型类、泛型方法

 

在制作一个批量序列化工具时遇到了如下问题,在此记录一下,仅供参考。

主程序加载另一个程序集,将其中的所有类取出,然后对这些类分别调用泛型类或泛型方法。控制台程序解决方案如下:

  • Main工程:提供Worker类进行数据操作,XMLTool<T>泛型类将数据集序列化为.xml文档,RootCollection<T>类封装数据集

    • Worker类

           提供成员方法void DoWork<T>()、List<T> GetList<T>()、静态成员方法StaticDoWork<T>(),代码如下:

     1 public class Worker 2     { 3         public Worker() 4         { 5         } 6  7         public void DoWork<T>() 8         { 9             Type t = typeof(T);10             Console.WriteLine("Get Class: {0}", t.Name);11             PropertyInfo[] properties = t.GetProperties();12             foreach (PropertyInfo property in properties)13             {14                 Console.WriteLine("\tproperty.Name: " + property.Name + "\tproperty.MemberType: " + property.PropertyType);15             }16         }17 18         public static void StaticDoWork<T>()19         {20             Type t = typeof(T);21             Console.WriteLine("Get Class: {0}", t.Name);22             PropertyInfo[] properties = t.GetProperties();23             foreach (PropertyInfo property in properties)24             {25                 Console.WriteLine("\tproperty.Name: " + property.Name + "\tproperty.MemberType: " + property.PropertyType);26             }27         }28 29         public List<T> GetList<T>()30         {31             Console.WriteLine("Generate List for [{0}]", typeof(T).Name);32             return new List<T>() 33             { 34                 Activator.CreateInstance<T>(), 35                 Activator.CreateInstance<T>() 36             };37         }38     }
    • XMLTool<T>类

       1publicclass XMLTool<T> 2     { 3publicstaticvoid XmlSerialize_Save(List<T> needSerializedList, string xmlDirPath, string xmlFileName) 4         { 5             RootCollection<T> collection = new RootCollection<T>(); 6             collection.ItemList = needSerializedList; 7if (!Directory.Exists(xmlDirPath)) 8                 Directory.CreateDirectory(xmlDirPath); 9using (System.IO.FileStream stream = new System.IO.FileStream(xmlFileName, System.IO.FileMode.Create))10             {11                 System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(collection.GetType());12                 serializer.Serialize(stream, collection);13             }14         }15     }
    • RootCollection<T>类:
       1     [Serializable] 2     public class RootCollection<T> 3     { 4         public RootCollection() 5         { 6             itemList = new List<T>(); 7         } 8  9         private List<T> itemList;10 11         public List<T> ItemList12         {13             get { return itemList; }14             set { itemList = value; }15         }16     }
  • MockClassLib工程:提供BaseEntityAppleCatPerson
    • BaseEntity类:抽象类,负责初始化类成员

       1     public abstract class BaseEntity 2     { 3         public BaseEntity() 4         { 5             InitiaWithNull(); 6         } 7 8         private void InitiaWithNull() 9         {10             Type type = this.GetType();11             PropertyInfo[] properties = type.GetProperties();12             string[] PropNames = new string[properties.Length];13             Dictionary<string, PropertyInfo> PropNameToInfo = new Dictionary<string, PropertyInfo>();14             for (int i = 0; i < properties.Length; i++)15             {16                 PropNames[i] = properties[i].Name;17                 PropNameToInfo.Add(PropNames[i], properties[i]);18             }1920             foreach (string propname in PropNames)21             {22                 string proptype = PropNameToInfo[propname].PropertyType.Name;2324                 object value = null;25                 if (NullValue.Keys.Contains(proptype))26                     value = NullValue[proptype];2728                 type.GetProperty(propname).SetValue(this, value, null);29             }30         }3132         private static readonly Dictionary<string, object> NullValue = new Dictionary<string, object>() 33             { 34                 { "String", String.Empty }, 35                 { "DateTime", DateTime.MinValue},36                 { "Decimal", Decimal.MinValue}37             };38     }
    • AppleCatPerson类:测试类,继承于BaseEntity
       1     public class Apple : BaseEntity 2     { 3        public string Color { get; set; } 4     } 5 6     public class Cat : BaseEntity 7     { 8        public string Type { get; set; } 9     }1011     public class Person : BaseEntity12     {13        public int ID { get; set; }14        public string Name { get; set; }15     }

Main工程的Program的Main方法中,一般情况下,调用Worker的泛型方法来处理测试类的话,可以写为:

Worker worker = new Worker();

worker.DoWork<Apple>();

worker.DoWork<Cat>();

worker.DoWork<Person>();

但是,如果MockClassLib中需要处理的类型非常多时,这样显示调用必然是不灵活的,应当怎样向泛型方法DoWork<T>()的尖括号中动态传入类型呢?

考虑代码:

            //Load assembly            Assembly mockAssembly = Assembly.LoadFrom("MockClassLibrary.dll");            Type[] typeArray = mockAssembly.GetTypes();

            //Create instance of Worker                      Worker worker = new Worker();            foreach(Type curType in typeArray)            {                  worker.DoWork<curType>();   //Error            }

可以看到,Type类型的实例是无法直接传入泛型方法的尖括号中的,T要求显式指明类型名。

下面通过反射方式来获取泛型方法,并创建特定类型的泛型方法。

  • 对于非静态方法:public void DoWork<T>()

对于非静态方法,调用MethodInfo.Invoke(object, object[])时,第一个参数需要指明泛型方法的所有者(即这里创建的worker对象),第二个参数为泛

型方法的参数列表,DoWork<T>()没有输入参数,所以设为null

//Create an instance of WorkerWorker worker = new Worker();       

//Get type of WorkerType workerType = typeof(Worker);

 //Get Generic MethodMethodInfo doWorkMethod = workerType.GetMethod("DoWork");

//Invoke DoWork<T> with different Typeforeach (Type curType in typeArray){    if (curType.IsClass && !curType.IsAbstract)//Filter BaseEntity    {        MethodInfo curMethod = doWorkMethod.MakeGenericMethod(curType);        curMethod.Invoke(worker, null);//Member method,use instance    }}
  • 对于静态方法:public static void StaticDoWork<T>()

不同于非静态方法,这里直接反射的类静态方法,所以Invoke()的第一个参数设为null

//Get type of WorkerWorker worker = new Worker();

//Get Generic MethodMethodInfo staticDoWorkMethod = workerType.GetMethod("StaticDoWork");

//Invoke StaticDoWork<T>foreach (Type curType in typeArray){    if (curType.IsClass && !curType.IsAbstract)    {        MethodInfo curMethod = staticDoWorkMethod.MakeGenericMethod(curType);        curMethod.Invoke(null, null);//Static method    }}
  • 对于有返回值的非静态方法:public List<T> GetList()

如同动态调用DoWork<T>()方法一样,只是在处理返回值时,可以使用下面的方法

1 IList tempList = (IList)curMethod.Invoke(worker, null);2 //Or3 IEnumerable tempList = (IEnumerable)curMethod.Invoke(worker, null);
  • 对于泛型类:XMLTool<T>

下面要使用泛型类XMLTool<T>的静态方法public static void XmlSerialize_Save(List<T> list, string dirPath, string fileName)方法。

首先应通过反射构造出指定类型的泛型类XMLTool<T>,再反射出其中的XmlSerialize_Save方法并使用。

 1 //Use Generic Class 2 Type xmlToolType = typeof(XMLTool<>).MakeGenericType(curType); 3  4 //Get method 5 MethodInfo saveMethod = xmlToolType.GetMethod("XmlSerialize_Save"); 6  7 //Invoke 8 saveMethod.Invoke 9 (10     null, //Static method11     new object[] { resultList, @"c:\", @"c:\Test_" + curType.Name + ".xml" }

12 );

Program-->Main()方法的全部代码:

 1 namespace RetrieveUnknownClass 2 { 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             //Load assembly 8             Assembly mockAssembly = Assembly.LoadFrom("MockClassLibrary.dll"); 9             Type[] typeArray = mockAssembly.GetTypes();10 11             //Create instance of Worker          12             Type workerType = typeof(Worker);13             Worker worker = new Worker();14 15             #region Member method16 17             Console.WriteLine(">>>>>>>>>Use Generic Method:");18             MethodInfo doWorkMethod = workerType.GetMethod("DoWork");19 20             //Invoke DoWork<T>21             foreach (Type curType in typeArray)22             {23                 if (curType.IsClass && !curType.IsAbstract)24                 {25                     MethodInfo curMethod = doWorkMethod.MakeGenericMethod(curType);26                     curMethod.Invoke(worker, null);//Member method,use instance27                 }28             }29 30             #endregion31 32             #region Static method33 34             Console.WriteLine("\r\n>>>>>>>>>Use Static Generic Method:");35             MethodInfo staticDoWorkMethod = workerType.GetMethod("StaticDoWork");36 37             //Invoke StaticDoWork<T>38             foreach (Type curType in typeArray)39             {40                 if (curType.IsClass && !curType.IsAbstract)41                 {42                     MethodInfo curMethod = staticDoWorkMethod.MakeGenericMethod(curType);43                     curMethod.Invoke(null, null);//Static method44                 }45             }46 47             #endregion48 49             #region Get A List & Serialize It to Xml File With Generic 50 51             Console.WriteLine("\r\n>>>>>>>>>Get List By Generic Method:");52             MethodInfo getListMethod = workerType.GetMethod("GetList");53 54             foreach (Type curType in typeArray)55             {56                 if (curType.IsClass && !curType.IsAbstract)57                 {58                     MethodInfo curMethod = getListMethod.MakeGenericMethod(curType);59                     //Generate List60                     IList resultList = (IList)curMethod.Invoke(worker, null);61                     //Show List62                     ShowList(resultList);63                     //Use Generic Class64                     Type xmlToolType = typeof(XMLTool<>).MakeGenericType(curType);65                     MethodInfo saveMethod = xmlToolType.GetMethod("XmlSerialize_Save");66 67                     saveMethod.Invoke68                         (69                             null, //Static method70                             new object[] { resultList, @"c:\", @"c:\Test_" + curType.Name + ".xml" }71                         );72                 }73             }74             75             Console.WriteLine("Serialization Completed...\r\n");76             #endregion77         }78 79         public static void ShowList(IList list)80         {81             Console.WriteLine("Type of list: {0}\r\nCount of current list: {1}\r\nType of item in list: {2}\r\n", 82                 list.GetType(), 83                 list.Count, 84                 list[0].GetType());85         }86     }87 }
 
 

asp .net core Get raw request.

 

  小弟初来乍到,分享一些工作学习中遇到的问题和解决方式,如有不准确或是有错误的地方,希望不吝赐教,谢过了。  --Dogtwo

背景:
一个代理服务器BK,接收前端A发送的请求,记录log,并转发给另外的服务器B。
请求中有类似这样的模块:
Person:
{
  name:abc,
  age: 20,
  address:
  {
    home: xxx,
    company: yyy
  }
}
其中home,company以及address为可选字段,服务器B要求如果为空则不传递该字段。
对于前端A来说,如果客户没有填入某字段则json中将不包含该字段。
例如若不包含home,则前端A的request中内容为:
Person:
{
  name:abc,
  age: 20,
  address:
  {
    company: yyy
}
}
前文所述,A的请求会经过BK再转发给B,BK使用ASP.NETCORE,读取A发送来的请求方式如下
public Task Test([FromBody] Model model)
框架本身已经封装很好,会将request.body中内容自动转化为model对象。对于可选字段home来说,如果前端未发送,
则model.person.address.home的值为""。若请求中address整体都未发送,则model.address为null.

一般来说,这样的处理不会有什么问题。但这次的坑中,我们需要把model再当作内容转发给B,原来使用的方法为
(RestRequest) request.addJsonBody(model);

此时会使A发送的请求与B接收的请求出现差异!
此时会使A发送的请求与B接收的请求出现差异!
此时会使A发送的请求与B接收的请求出现差异!(重要的事情说三遍)

例如
A发送的:
Person:
{
  name:abc,
  age: 20
}
B接收的:
Person:
{
  name:abc,
  age: 20,
  address:null
}

对于基本类型,若某字段可选我们可以这样处理
public int? a { get; set; }
对于内嵌的address来说,经由BK处理后无法取消掉address。(或者是我没找到方法,有办法的话请不吝赐教)

解决办法:
既然框架转化的model不能满足要求,第一思路是直接去取原生的request来获取request.body,转发给B.
但此时在方法为取到的request.body中内容居然为""。明明可以通过[FromBody]来获取model,直接取原生内容居然为空,很费解.
网络求助后发现
ASP NET Core不允许我们仅仅通过方法参数以任何有意义的方式捕获“原始”数据。因此我们需要通过处理Request.Body来获取原始数据,然后反序列化它。

我们可以捕获原始的Request.Body并从原始缓冲区中读取参数。最简而有效的方法是接受不带参数的POST或PUT数据,然后从Request.Body读取原始数据:
例:

 1 [HttpPost("api/blog/jsonstring")]
 2 public async Task Index()
 3 {
 4     var result = string.Empty;
 5
 6     using (var reader = new StreamReader(Request.Body,Encoding.UTF8))
 7     {
 8         result = await reader.ReadToEndAsync();
 9     }
10
11     return result;
12 }

但在该项目中,使用上述代码取得的result仍为空,笔者代码类似于:

 1 [HttpPost("api/blog/jsonstring")]
 2 public async Task Index([FromBody] Model model)
 3 {
 4   var result = string.Empty;
 5
 6   using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
 7   {
 8     result = await reader.ReadToEndAsync();
 9   }
10
11   return result;
12 }

区别在于多了这个[FromBody] Model model,将其去掉之后result可以成功取得对应值。
继续求助网络发现:
ASP.NET Core 中的 Request.Body 虽然是一个 Stream ,但它是一个与众不同的 Stream —— 不允许 Request.Body.Position=0 ,这就意味着只能读取一次。
解决办法为
引用命名空间 Microsoft.AspNetCore.Http.Internal ,调用方法 Request.EnableRewind() (但尝试时该方法无效,需要进一步研究)

另外,成功取得request.body值以后要用这个方法将其加入到新的request中:
request.AddParameter("application/json", result, ParameterType.RequestBody);

参考:
1. ASP.NET Core Web API获取原始请求内容
2. ASP.NET Core 中读取 Request.Body 的正确姿势
3.Returning only useful fields from the API && Consuming an API that accepts a comma-separated list of fields.

从壹开始前后端分离[.NetCore 不定期更新] 38 ║自动初始化数据库

 

系列教程一目录:.netcore+vue 前后端分离

系列教程二目录:DDD领域驱动设计

正文

缘起

哈喽大家好呀,我们又见面啦,这里先祝大家圣诞节快乐哟,昨天的红包不知道有没有小伙伴抢到呢。今天的这篇内容灰常简单,只是对我们的系统的数据库进行CodeFirst,然后就是数据处理,因为这几个月来,还是有小伙伴陆陆续续的向我索要数据,本来想着都是很简单的数据,就不给了,然后仅仅是提供了一个Sql的表结构,但是想想一个完整的项目,怎么能没有一个初始化的功能呢(不仅仅是表结构,还是要一些简单的数据)?所以就想着写今天这篇文章了,这篇文章虽然可能看起来很简单,不过也是给大家提供了一个思路,就是自己以后在写项目的时候,如何添加一个初始化的Seed Data,我也是参考其他小伙伴的,这里重点表扬下QQ群里,@初久童鞋,没有他的博客园地址,就没办法放他的首页了。

投稿作者:初久,个人地址:null,实现项目启动的时候,自动初始化数据,其中会涉及到上下文、SqlSugar、依赖注入等知识。

好啦,话不多说,直接开始动手。

一、对Mode实体类进行配置

因为要使用到了CodeFirst了,所以我们必须要对我们的实体类 Model 进行配置,当然也有很多的小伙伴,使用的是EFCore,当然是可以的,EFCore的我就不多少了,很简单,如果有不会的小伙伴,可以看我的第二个系列的《让你明白DDD的小故事 & EFCore初探》和《剪不断理还乱的 值对象和Dto》这两篇文章都有对EFCore的配置有提到,有需要的可以看看。那咱们就配置下我们的SqlSugar吧。

这里只用 Advertisement.cs 来举例吧,其他的,大家可以自行去查看我的Github上的code:

    public class Advertisement : RootEntity
    {

        /// <summary>
        /// 广告图片
        /// </summary>
        [SugarColumn(Length = 512, IsNullable = true)]
        public string ImgUrl { get; set; }

        /// <summary>
        /// 广告标题
        /// </summary>
        [SugarColumn(Length = 64, IsNullable = true)]
        public string Title { get; set; }

        /// <summary>
        /// 广告链接
        /// </summary>
        [SugarColumn(Length = 256, IsNullable = true)]
        public string Url { get; set; }

        /// <summary>
        /// 备注
        /// </summary>
        [SugarColumn(Length = int.MaxValue, IsNullable = true)]
        public string Remark { get; set; }

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime Createdate { get; set; } = DateTime.Now;
    }

    public class RootEntity
    {
        /// <summary>
        /// ID
        /// </summary>
        [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
    }

这个就很简单的了,主要就是属性 SugarColumn() ,里边有一些属性,可以自行配置,这里给大家简单注释一下:

 public class SugarColumn : Attribute
 {
     public SugarColumn();

     public string ColumnName { get; set; }//列名
     public bool IsIgnore { get; set; }//是否忽略
     public bool IsPrimaryKey { get; set; }//是否是主键
     public bool IsIdentity { get; set; }//是否自增
     public string MappingKeys { get; set; }//映射key
     public string ColumnDescription { get; set; }//列描述
     public int Length { get; set; }//长度
     public bool IsNullable { get; set; }//是否为空
     public string OldColumnName { get; set; }//旧的列名
     public string ColumnDataType { get; set; }//列类型,自定义
     public int DecimalDigits { get; set; }//dicimal精度
     public string OracleSequenceName { get; set; }//Oracle序列名
     public bool IsOnlyIgnoreInsert { get; set; }//是否仅对添加忽略
     public bool IsEnableUpdateVersionValidation { get; set; }
 }

这里我已经配置完成了,而且是尽量的仿照着我的数据库来的,可能会有细微的差别,如果你要想使用的话,可以用一个测试的数据库来实验。

二、配置上下文与初始数据

大家是否还记得之前在仓储Repository中,我们创建了一个上下文,这里可以直接拿来用,不过因为我们的 API 层已经和 Repository 层解耦分割了,所以我就在 Mode 层来实现这个功能吧。如果你不解耦,可以直接使用仓储层的上下文即可。

1、建立 SqlSugar 上下文

在 Blog.Core.Model 层新建一个 Seed 文件夹,然后把仓储层中的 context 拷贝过去,我重命名为 MyContext.cs:

重点就是构造函数,要实现实例化 SqlSugarClient 的作用:

 public MyContext()
 {
     if (string.IsNullOrEmpty(_connectionString))
         throw new ArgumentNullException("数据库连接字符串为空");
     _db = new SqlSugarClient(new ConnectionConfig()
     {
         ConnectionString = _connectionString,//数据库字符串
         DbType = DbType.SqlServer,//数据库类型
         IsAutoCloseConnection = true,//自动关闭数据库
         IsShardSameThread = false,//启用异步多线程
         InitKeyType = InitKeyType.Attribute,//mark
         ConfigureExternalServices = new ConfigureExternalServices()
         {
             //DataInfoCacheService = new HttpRuntimeCache()
         },
         MoreSettings = new ConnMoreSettings()
         {
             //IsWithNoLockQuery = true,
             IsAutoRemoveDataCache = true
         }
     });
 }

2、实现初始化种子数据的功能

上边咱们创建了好上下文,那接下来咱们就应该实现 CodeFirst 功能了,

还是再 Seed 文件夹,新建 DBSeed.cs 类:

    public class DBSeed
    {
        /// <summary>
        /// 异步添加种子数据
        /// </summary>
        /// <param name="myContext"></param>
        /// <returns></returns>
        public static async Task SeedAsync(MyContext myContext)
        {
            try
            {                // 注意!一定要先手动创建一个空的数据库
                // 会覆盖,可以设置为true,来备份数据
                // 如果生成过了,第二次,就不用再执行一遍了,注释掉该方法即可
                myContext.CreateTableByEntity(false, typeof(Advertisement), typeof(BlogArticle), typeof(Guestbook), typeof(Module), typeof(ModulePermission), typeof(OperateLog), typeof(PasswordLib), typeof(Permission), typeof(Role), typeof(RoleModulePermission), typeof(sysUserInfo), typeof(Topic), typeof(TopicDetail), typeof(UserRole));

                // 下边的就是种子数据
                #region Advertisement
                if (!await myContext.Db.Queryable<Advertisement>().AnyAsync())
                {
                    myContext.GetEntityDB<Advertisement>().Insert(
                       new Advertisement()
                       {
                           Createdate = DateTime.Now,
                           Remark = "mark",
                           Title = "good"
                       });
                }
                #endregion

                #region BlogArticle Guestbook
                if (!await myContext.Db.Queryable<BlogArticle>().AnyAsync())
                {
                    int bid = myContext.GetEntityDB<BlogArticle>().InsertReturnIdentity(
                         new BlogArticle()
                         {
                             bsubmitter = "admins",
                             btitle = "老张的哲学",
                             bcategory = "技术博文",
                             bcontent = "<p>1。。。。。。",
                             btraffic = 1,
                             bcommentNum = 0,
                             bUpdateTime = DateTime.Now,
                             bCreateTime = DateTime.Now
                         });

                    if (bid > 0)
                    {

                        if (!await myContext.Db.Queryable<Guestbook>().AnyAsync())
                        {
                            myContext.GetEntityDB<Guestbook>().Insert(
                               new Guestbook()
                               {
                                   blogId = bid,
                                   createdate = DateTime.Now,
                                   username = "user",
                                   phone = "110",
                                   QQ = "100",
                                   body = "很不错",
                                   ip = "127.0.0.1",
                                   isshow = true,
                               });
                        }
                    }
                }
                #endregion

                #region Module
                int mid = 0;
                if (!await myContext.Db.Queryable<Module>().AnyAsync())
                {
                    mid = myContext.GetEntityDB<Module>().InsertReturnIdentity(
                        new Module()
                        {
                            IsDeleted = false,
                            Name = "values的接口信息",
                            LinkUrl = "/api/values",
                            OrderSort = 1,
                            IsMenu = false,
                            Enabled = true,
                        });

                }
                #endregion

                #region Role
                int rid = 0;
                if (!await myContext.Db.Queryable<Role>().AnyAsync())
                {
                    rid = myContext.GetEntityDB<Role>().InsertReturnIdentity(
                        new Role()
                        {
                            IsDeleted = false,
                            Name = "Admin",
                            Description = "我是一个admin管理员",
                            OrderSort = 1,
                            CreateTime = DateTime.Now,
                            Enabled = true,
                            ModifyTime = DateTime.Now
                        });

                }
                #endregion

                #region RoleModulePermission
                if (mid > 0 && rid > 0)
                {
                    if (!await myContext.Db.Queryable<RoleModulePermission>().AnyAsync())
                    {
                        myContext.GetEntityDB<RoleModulePermission>().Insert(
                            new RoleModulePermission()
                            {
                                IsDeleted = false,
                                RoleId = rid,
                                ModuleId = mid,
                                CreateTime = DateTime.Now,
                                ModifyTime = DateTime.Now
                            });
                    }
                }
                #endregion

                #region sysUserInfo
                int uid = 0;
                if (!await myContext.Db.Queryable<sysUserInfo>().AnyAsync())
                {
                    uid = myContext.GetEntityDB<sysUserInfo>().InsertReturnIdentity(
                        new sysUserInfo()
                        {
                            uLoginName = "admins",
                            uLoginPWD = "admins",
                            uRealName = "admins",
                            uStatus = 0,
                            uCreateTime = DateTime.Now,
                            uUpdateTime = DateTime.Now,
                            uLastErrTime = DateTime.Now,
                            uErrorCount = 0

                        });

                }
                #endregion

                #region UserRole
                if (uid > 0 && rid > 0)
                {
                    if (!await myContext.Db.Queryable<UserRole>().AnyAsync())
                    {
                        myContext.GetEntityDB<UserRole>().Insert(
                            new UserRole()
                            {
                                IsDeleted = false,
                                UserId = uid,
                                RoleId = rid,
                                CreateTime = DateTime.Now,
                                ModifyTime = DateTime.Now
                            });
                    }
                }
                #endregion

                #region Topic TopicDetail
                if (!await myContext.Db.Queryable<Topic>().AnyAsync())
                {
                    int tid = myContext.GetEntityDB<Topic>().InsertReturnIdentity(
                         new Topic()
                         {
                             tLogo = "/Upload/20180626/95445c8e288e47e3af7a180b8a4cc0c7.jpg",
                             tName = "《罗马人的故事》",
                             tDetail = "这是一个荡气回肠的故事",
                             tIsDelete = false,
                             tRead = 0,
                             tCommend = 0,
                             tGood = 0,
                             tCreatetime = DateTime.Now,
                             tUpdatetime = DateTime.Now,
                             tAuthor = "laozhang"
                         });

                    if (tid > 0)
                    {

                        if (!await myContext.Db.Queryable<TopicDetail>().AnyAsync())
                        {
                            myContext.GetEntityDB<TopicDetail>().Insert(
                               new TopicDetail()
                               {
                                   TopicId = tid,
                                   tdLogo = "/Upload/20180627/7548de20944c45d48a055111b5a6c1b9.jpg",
                                   tdName = "第一章 罗马的诞生 第一节 传说的年代",
                                   tdContent = "<p>第一节 传说的年代</时代走出,近入了历史时代。</p><p><br></p>",
                                   tdDetail = "第一回",
                                   tdIsDelete = false,
                                   tdRead = 1,
                                   tdCommend = 0,
                                   tdGood = 0,
                                   tdCreatetime = DateTime.Now,
                                   tdUpdatetime = DateTime.Now,
                                   tdTop = 0,
                               });
                        }
                    }
                }
                #endregion

            }
            catch (Exception ex)
            {

            }
        }
    }

是不是很简单,上边的 CreateTableByEntity 是用来创建数据库的表结构的,第一次执行完成后,剩下的就可以不用执行了。下边的是添加种子数据,我增加了判断,其他的大家可以自定义处理。

这个时候我们已经把初始化表结构,和添加种子数据完成了,那我们应该怎么用呢,别慌,请往下看。

三、在项目启动的时候,执行初始化

1、将上边的类注入服务

这个很简单,相信大家都能看懂,我就直接注入到服务,然后服务会自动注入到Autofac:

2、在主程序 Main 中启动初始化

相信大家都应该知道,其实 .net core 本身是一个控制台程序,所以项目启动是在 Program.cs 中的 Main主程序方法中的,我们做一下修改:

    public class Program
    {
        public static void Main(string[] args)
        {
            // 生成承载 web 应用程序的 Microsoft.AspNetCore.Hosting.IWebHost。Build是WebHostBuilder最终的目的,将返回一个构造的WebHost。
            var host = CreateWebHostBuilder(args).Build();

            // 创建可用于解析作用域服务的新 Microsoft.Extensions.DependencyInjection.IServiceScope。
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                var loggerFactory = services.GetRequiredService<ILoggerFactory>();

                try
                {
                    // 从 system.IServicec提供程序获取 T 类型的服务。
                    var myContext = services.GetRequiredService<MyContext>();
                    DBSeed.SeedAsync(myContext).Wait();
                }
                catch (Exception e)
                {
                    var logger = loggerFactory.CreateLogger<Program>();
                    logger.LogError(e, "Error occured seeding the Database.");
                }
            }

            // 运行 web 应用程序并阻止调用线程, 直到主机关闭。
            // 创建完 WebHost 之后,便调用它的 Run 方法,而 Run 方法会去调用 WebHost 的 StartAsync 方法
            // 将Initialize方法创建的Application管道传入以供处理消息
            // 执行HostedServiceExecutor.StartAsync方法
            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            //使用预配置的默认值初始化 Microsoft.AspNetCore.Hosting.WebHostBuilder 类的新实例。
            WebHost.CreateDefaultBuilder(args)
                //指定要由 web 主机使用的启动类型。相当于注册了一个IStartup服务。
                .UseStartup<Startup>();
    }

执行流程就是,我们项目启动,首先会创建一个初始化WebHostBuilder 实例,然后使用启动默认的 Startup 服务,当然你也可以自定义这个启动服务,比如 StatupDevelopment 。

这样写  .UseStartup(typeof(StartupDevelopment).GetTypeInfo().Assembly.FullName)

接下来,就是 Build 我们的刚刚实例化的 webhostbuilder ,生成一个 WebHost 宿主主机。

中间我们就可以对宿主下的服务进行配置,

最后就是执行 Run() 方法,启动应用程序,直到主机关闭。

四、测试结果

1、用动图来演示效果

经过配置,我这里先建立了一个空的数据库 DBInitTest ,然后看看效果:

这里要注意下:根据数据库大小的不同,中间可能经历的时间不一样,我们已经成功的生成了数据库,并初始化出来了数据。

好啦,今天的这个小技巧就说到这里了,你也可以根据自己的情况,根据自己的ORM来设计哟,特别适用于一个给别人展示的Demo项目,和自己的小项目。

2、如果用EFCore是更简单

 try
 {
     // TODO: Only run this if using a real database
     myContext.Database.Migrate();

     if (!myContext.Posts.Any())
     {
         myContext.Posts.AddRange(
             new List<Post>{
         new Post{
             Title = "Post Title 1",
             Body = "Post Body 1",
             Author = "Dave",
             LastModified = DateTime.Now
         }
             }
         );
         await myContext.SaveChangesAsync();
     }
 }

采用异步来实现重新连接服务器或者重新启动服务 C#中类的属性的获取 SignalR2简易数据看板演示 C#动态调用泛型类、泛型方法 asp .net core Get raw request. 从壹开始前后端分离[.NetCore 不定期更新] 38 ║自动初始化数据库的更多相关文章

  1. 从壹开始前后端分离 [.netCore 不定期更新 ] 三十五║ 完美实现全局异常日志记录

    缘起 哈喽我是不定期更新的日常,昨天群里小伙伴问到了记录日志,当然,以前我也挖过这个坑,后来一直没有来得及填上,也想着 swagger 一直又有错误信息展示的功能,就迟迟没有添加这个功能,不过昨天夜里 ...

  2. 从壹开始前后端分离[.netCore 不定期 ] 36 ║解决JWT自定义中间件授权过期问题

    缘起 哈喽,老张的不定期更新的日常又开始了,在咱们的前后端分离的.net core 框架中,虽然已经实现了权限验证<框架之五 || Swagger的使用 3.3 JWT权限验证[修改]>, ...

  3. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  4. 从壹开始前后端分离[.NetCore] 37 ║JWT完美实现权限与接口的动态分配

    缘起 本文已经有了对应的管理后台,地址:https://github.com/anjoy8/Blog.Admin 哈喽大家好呀!又过去一周啦,这些天小伙伴们有没有学习呀,已经有一周没有更新文章了,不过 ...

  5. k.tt 研究下生成的逻辑代码:从壹开始前后端分离 [.netCore 填坑 ] 三十二║ 四种方法快速实现项目的半自动化搭建

    更新 1.更新小伙伴 @大龄Giser 提出好点子:试试VS的插件扩展:VSIX.ItemProject等,将T4模板给制作插件,这里先记下,有懂的小伙伴可以自己先试试,我会在以后更新. 2.感谢小伙 ...

  6. 从壹开始前后端分离 [.netCore 填坑 ] 三十三║ ⅖ 种方法实现完美跨域

    缘起 哈喽大家周四好,趁着大家在团建的时候花一个下午学点儿东西,也是督促大家学习哟,希望大家看到老张的文章,可以有一丢丢的学习动力.不过话说过来,该吃的团建还是要去的,不能学我呀 [ /(ㄒoㄒ)/~ ...

  7. ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

    一.前言 这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自 ...

  8. 从壹开始前后端分离[.NetCore ] 38 ║自动初始化数据库(不定期更新)

    缘起 哈喽大家好呀,我们又见面啦,这里先祝大家圣诞节快乐哟,昨天的红包不知道有没有小伙伴抢到呢.今天的这篇内容灰常简单,只是对我们的系统的数据库进行CodeFirst,然后就是数据处理,因为这几个月来 ...

  9. 从壹开始前后端分离 [.netCore 填坑 ] 三十四║Swagger:API多版本控制,带来的思考

    前言 大家周二好呀,.net core + Vue 这一系列基本就到这里差不多了,今天我又把整个系列的文章下边的全部评论看了一下(我是不是很负责哈哈),提到的问题基本都解决了,还有一些问题,已经在QQ ...

随机推荐

  1. TCP三次握手四次挥手相关问题探讨

    TCP的握手挥手和状态转换是很多网络问题的基础.在此进行相关问题的讨论及记录. 首先,这幅图大致介绍了TCP连接和断开的过程: 注意其中的几个状态: LISTEN, SYN-SEND, SYN-RCV ...

  2. 第四章 第一个rabbitmq程序

    rabbitmq消息发送模型 要素: 生产者 消费者 交换器:生产者将消息发送到交换器 队列:交换器通过某种路由规则绑定到指定队列,将消息加入队列,消费者从队列消费消息 前提: 引入rabbitmq的 ...

  3. 内存泄漏 Memory Leaks 内存优化 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. MySql的入侵测试以及防范

    在做了之前的SQL SERVER之后,便很想尝试一下MYSQL的入侵测试已经防范,与大家一起分享. 总的来说,我一直在用的是MYSQL,对MYSQL比较熟悉,相比较而言,感觉MYSQL更安全,这只是我 ...

  5. URAL 1748

    题目大意:找出T组不大于ni(i=1,2,3,...,T)的因子数最多的数mi(i=1,2,3,...,T),有多个数时输出最小的. KB     64bit IO Format:%I64d & ...

  6. Declaration Merging with TypeScript

    原文:https://blog.oio.de/2014/03/21/declaration-merging-typescript/ Why might you need this? There can ...

  7. rapidxml修改节点的值

    1.rapidxml修改节点的value,修改之后,序列化还是原来的值,具体原因是什么,要看rapidxml是怎么实现的.如下: void TestRapidXml() { ]; sprintf(xm ...

  8. shell中使用if判断时用到的一些参数

    shell 编程中使用到得if语句内判断参数 –b 当file存在并且是块文件时返回真 -c 当file存在并且是字符文件时返回真 -d 当pathname存在并且是一个目录时返回真 -e 当path ...

  9. 过滤器(web基础学习笔记二十一)

    一.过滤器简介 二.在Eclipse中创建过滤器 三.使用过滤器设置全部web字符编码 public void doFilter(ServletRequest request, ServletResp ...

  10. Mysql自己主动备份

    Mysql自己主动备份 批处理命令: set"Ymd=%date:~,4%%date:~5,2%%date:~8,2%" set"hMs=%time:~,2%%time: ...