第六节:SignalR完结篇之依赖注入和分布式部署
一. SignalR中DI思想的应用
分析解决思路:
1. 新建Repository类和IRepository接口,里面声明SaveMsg方法,用来存储信息 (PS:便于测试,这里将信息保存到txt文本文档中)
代码如下:
1 public interface IRepository 2 { 3 void SaveMsg(string connectionId, string msg); 4 } 5 public class Repository : IRepository 6 { 7 /// <summary> 8 /// 模拟数据库插入操作 9 /// 这里以日志代替 10 /// </summary> 11 /// <param name="connectionId"></param> 12 /// <param name="msg"></param> 13 public void SaveMsg(string connectionId, string msg) 14 { 15 //此处执行插入数据库操作 16 FileOperateHelp.WriteFile("/Logs/msg.txt", $"用户【{connectionId}】发来消息:{msg},时间为:{DateTime.Now.ToLongDateString()}"); 17 } 18 }
分享一个文件相关操作的工具类FileOperateHelp:
1 public class FileOperateHelp 2 { 3 #region 01.写文件(.txt-覆盖) 4 /// <summary> 5 /// 写文件(覆盖源文件内容) 6 /// 文件不存在的话自动创建 7 /// </summary> 8 /// <param name="FileName">文件路径(web里相对路径,控制台在根目录下写)</param> 9 /// <param name="Content">文件内容</param> 10 public static string Write_Txt(string FileName, string Content) 11 { 12 try 13 { 14 Encoding code = Encoding.GetEncoding("gb2312"); 15 string htmlfilename = FileOperateHelp.PathConvert(FileName); 16 //string htmlfilename = HttpContext.Current.Server.MapPath(FileName + ".txt"); //保存文件的路径 17 string str = Content; 18 StreamWriter sw = null; 19 { 20 try 21 { 22 sw = new StreamWriter(htmlfilename, false, code); 23 sw.Write(str); 24 sw.Flush(); 25 } 26 catch { } 27 } 28 sw.Close(); 29 sw.Dispose(); 30 return "ok"; 31 } 32 catch (Exception ex) 33 { 34 35 return ex.Message; 36 } 37 38 } 39 #endregion 40 41 #region 02.读文件(.txt) 42 /// <summary> 43 /// 读文件 44 /// </summary> 45 /// <param name="filename">文件路径(web里相对路径,控制台在根目录下写)</param> 46 /// <returns></returns> 47 public static string Read_Txt(string filename) 48 { 49 50 try 51 { 52 Encoding code = Encoding.GetEncoding("gb2312"); 53 string temp = FileOperateHelp.PathConvert(filename); 54 // string temp = HttpContext.Current.Server.MapPath(filename + ".txt"); 55 string str = ""; 56 if (File.Exists(temp)) 57 { 58 StreamReader sr = null; 59 try 60 { 61 sr = new StreamReader(temp, code); 62 str = sr.ReadToEnd(); // 读取文件 63 } 64 catch { } 65 sr.Close(); 66 sr.Dispose(); 67 } 68 else 69 { 70 str = ""; 71 } 72 return str; 73 } 74 catch (Exception ex) 75 { 76 77 return ex.Message; 78 } 79 } 80 #endregion 81 82 #region 03.写文件(.txt-添加) 83 /// <summary> 84 /// 写文件 85 /// </summary> 86 /// <param name="FileName">文件路径(web里相对路径,控制台在根目录下写)</param> 87 /// <param name="Strings">文件内容</param> 88 public static string WriteFile(string FileName, string Strings) 89 { 90 try 91 { 92 string Path = FileOperateHelp.PathConvert(FileName); 93 94 if (!System.IO.File.Exists(Path)) 95 { 96 System.IO.FileStream f = System.IO.File.Create(Path); 97 f.Close(); 98 f.Dispose(); 99 } 100 System.IO.StreamWriter f2 = new System.IO.StreamWriter(Path, true, System.Text.Encoding.UTF8); 101 f2.WriteLine(Strings); 102 f2.Close(); 103 f2.Dispose(); 104 return "ok"; 105 } 106 catch (Exception ex) 107 { 108 109 return ex.Message; 110 } 111 } 112 #endregion 113 114 #region 04.读文件(.txt) 115 /// <summary> 116 /// 读文件 117 /// </summary> 118 /// <param name="FileName">文件路径(web里相对路径,控制台在根目录下写)</param> 119 /// <returns></returns> 120 public static string ReadFile(string FileName) 121 { 122 try 123 { 124 string Path = FileOperateHelp.PathConvert(FileName); 125 string s = ""; 126 if (!System.IO.File.Exists(Path)) 127 s = "不存在相应的目录"; 128 else 129 { 130 StreamReader f2 = new StreamReader(Path, System.Text.Encoding.GetEncoding("gb2312")); 131 s = f2.ReadToEnd(); 132 f2.Close(); 133 f2.Dispose(); 134 } 135 return s; 136 } 137 catch (Exception ex) 138 { 139 return ex.Message; 140 } 141 } 142 #endregion 143 144 #region 05.删除文件 145 /// <summary> 146 /// 删除文件 147 /// </summary> 148 /// <param name="Path">文件路径(web里相对路径,控制台在根目录下写)</param> 149 public static string FileDel(string Path) 150 { 151 try 152 { 153 string temp = FileOperateHelp.PathConvert(Path); 154 File.Delete(temp); 155 return "ok"; 156 } 157 catch (Exception ex) 158 { 159 return ex.Message; 160 } 161 } 162 #endregion 163 164 #region 06.移动文件 165 /// <summary> 166 /// 移动文件 167 /// </summary> 168 /// <param name="OrignFile">原始路径(web里相对路径,控制台在根目录下写)</param> 169 /// <param name="NewFile">新路径,需要写上路径下的文件名,不能单写路径(web里相对路径,控制台在根目录下写)</param> 170 public static string FileMove(string OrignFile, string NewFile) 171 { 172 try 173 { 174 OrignFile = FileOperateHelp.PathConvert(OrignFile); 175 NewFile = FileOperateHelp.PathConvert(NewFile); 176 File.Move(OrignFile, NewFile); 177 return "ok"; 178 } 179 catch (Exception ex) 180 { 181 return ex.Message; 182 } 183 } 184 #endregion 185 186 #region 07.复制文件 187 /// <summary> 188 /// 复制文件 189 /// </summary> 190 /// <param name="OrignFile">原始文件(web里相对路径,控制台在根目录下写)</param> 191 /// <param name="NewFile">新文件路径(web里相对路径,控制台在根目录下写)</param> 192 public static string FileCopy(string OrignFile, string NewFile) 193 { 194 try 195 { 196 OrignFile = FileOperateHelp.PathConvert(OrignFile); 197 NewFile = FileOperateHelp.PathConvert(NewFile); 198 File.Copy(OrignFile, NewFile, true); 199 return "ok"; 200 } 201 catch (Exception ex) 202 { 203 return ex.Message; 204 } 205 } 206 #endregion 207 208 #region 08.创建文件夹 209 /// <summary> 210 /// 创建文件夹 211 /// </summary> 212 /// <param name="Path">相对路径(web里相对路径,控制台在根目录下写)</param> 213 public static string FolderCreate(string Path) 214 { 215 try 216 { 217 Path = FileOperateHelp.PathConvert(Path); 218 // 判断目标目录是否存在如果不存在则新建之 219 if (!Directory.Exists(Path)) 220 { 221 Directory.CreateDirectory(Path); 222 } 223 return "ok"; 224 } 225 catch (Exception ex) 226 { 227 return ex.Message; 228 } 229 } 230 #endregion 231 232 #region 09.递归删除文件夹目录及文件 233 /// <summary> 234 /// 递归删除文件夹目录及文件 235 /// </summary> 236 /// <param name="dir">相对路径(web里相对路径,控制台在根目录下写) 截止到哪删除到哪,eg:/a/ 连a也删除</param> 237 /// <returns></returns> 238 public static string DeleteFolder(string dir) 239 { 240 241 try 242 { 243 string adir = FileOperateHelp.PathConvert(dir); 244 if (Directory.Exists(adir)) //如果存在这个文件夹删除之 245 { 246 foreach (string d in Directory.GetFileSystemEntries(adir)) 247 { 248 if (File.Exists(d)) 249 File.Delete(d); //直接删除其中的文件 250 else 251 DeleteFolder(d); //递归删除子文件夹 252 } 253 Directory.Delete(adir, true); //删除已空文件夹 254 } 255 return "ok"; 256 } 257 catch (Exception ex) 258 { 259 return ex.Message; 260 } 261 } 262 263 #endregion 264 265 #region 10.将相对路径转换成绝对路径 266 /// <summary> 267 /// 10.将相对路径转换成绝对路径 268 /// </summary> 269 /// <param name="strPath">相对路径</param> 270 public static string PathConvert(string strPath) 271 { 272 //web程序使用 273 if (HttpContext.Current != null) 274 { 275 return HttpContext.Current.Server.MapPath(strPath); 276 } 277 else //非web程序引用 278 { 279 strPath = strPath.Replace("/", "\\"); 280 if (strPath.StartsWith("\\")) 281 { 282 strPath = strPath.TrimStart('\\'); 283 } 284 return System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, strPath); 285 } 286 } 287 #endregion 288 289 }
2. 采用构造函数注入的方式,在MySpecHub1这个Hub类中进行配置。
代码如下图:
3. 配置注入代码,在Startup类中的Configuration方法中,进行依赖注入代码的配置。
代码如下:
每当需要创建MySpecHub1实例,SignalR 将调用此匿名函数。
GlobalHost.DependencyResolver.Register(typeof(MySpecHub1), () => new MySpecHub1(new Repository()));
4. 在群发接口中进行SaveMsg方法的调用进行测试。
5. 测试结果:
二. 基于SQLServer或Redis进行部署
下面以SQLServer为例简单的配置一下。
1. 通过Nuget下载程序集:Microsoft.AspNet.SignalR.SqlServer
2. 在SQLServer中新建一个数据库,比如 SignalRDB,不需要创建任何表,因为程序运行时,会自动生成所需表
3. 在Startup中配置映射数据库,代码如下:
1 public class Startup 2 { 3 public void Configuration(IAppBuilder app) 4 { 5 app.UseCors(CorsOptions.AllowAll).MapSignalR(); 6 //四. 性能优化 7 // 1. SQLServer版本(跨服务器通信代码配置) 8 string sqlConnectionString = "data source=localhost;initial catalog=SignalRDB;persist security info=True;user id=sa;password=123456;"; 9 GlobalHost.DependencyResolver.UseSqlServer(sqlConnectionString); 10 11 } 12 }
以上3步,已经实现了不同地址间SignalR间的通讯,配置非常简单,内部复杂实现微软已经给实现好了,那么下面我们简单的部署一下,分别部署在1001 和 1002 端口下,进行通讯。
PS:补充Redis的配置
1. 通过Nuget下载程序集:Microsoft.AspNet.SignalR.Redis
2. 代码配置:GlobalHost.DependencyResolver.UseRedis("127.0.0.1", 6379, "123456", "mykey");
截止到此处Signalr系列入门已经全部更新完成,再深入的需要小伙伴们自行研究了,原计划的项目案例由于剥离代码实在是太耗时间了,暂时搁置,后面有时间在补充,下一步会给该系列做一个目录就彻底告一段落。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。