前言

自从几年前抛弃wcf,使用web api 来做服务器端开发之后,就不再迷惑了。但是因为本来从事传统行业管理软件开发,一般都以分布式应用开发为主。纯BS还是比较少,于是比较喜欢用windows service来宿主web api。发现这种场景网上文章还是比较少。这次就结合最近的技术尝试(DI、IOC),整体介绍一下这方面的实践。

名词解释

依赖注入

依赖倒置原则 
A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。
B.抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
 
DI—Dependency Injection,即“依赖注入”组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

  理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  ●谁依赖于谁:当然是应用程序依赖于IoC容器

  ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源

  ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象

  ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

 
控制反转:
控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
 
我绑架了一个人质,对围观的警察说:我要一辆红色法拉利,才能释放人质。但其实我只是希望要一辆车而已。要法拉利很容易被拒绝,还可能引起很严重的后果。如果我说要一辆车,那么警察估计更容易给我一辆普通车...
 
在软件开发里面的就是尽量使用接口对象,而不使用具体明确的对象(依赖外部注入的接口对象),以此达到解除耦合的目的。哎我自己也理解得不深刻,其实我要说的是:
owin+web api+autofac.上面的解释是我抄的,理解不了就算了吧。后面这些总该知道什么东西吧。
 

开发工具和包

IDE: VS2015

Package:

<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="4.4.0" targetFramework="net45" />
<package id="Autofac.Owin" version="4.0.0" targetFramework="net45" />
<package id="Autofac.WebApi2" version="4.0.1" targetFramework="net45" />
<package id="Autofac.WebApi2.Owin" version="4.0.0" targetFramework="net45" />
<package id="EntityFramework" version="6.1.3" targetFramework="net45" />
<package id="EntityFramework.zh-Hans" version="6.1.3" targetFramework="net45" />
<package id="LitJson" version="0.7.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.OwinSelfHost" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Hosting" version="3.0.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="System.Data.SQLite" version="1.0.104.0" targetFramework="net45" />
<package id="System.Data.SQLite.Core" version="1.0.104.0" targetFramework="net45" />
<package id="System.Data.SQLite.EF6" version="1.0.104.0" targetFramework="net45" />
<package id="System.Data.SQLite.Linq" version="1.0.104.0" targetFramework="net45" />
</packages>

targetFramework:net45,注意一下运行时是4.5以上,也是说服务程序必须在win7 sp1以上的操作系统才能运行。

编码细节和要点

1、windows服务宿主web api

//protected override
public new void OnStart(string[] args)
{
try
{
string middleware_url = string.Join("", new string[] { "http://", MiddlewareIP, ":", MiddlewarePort });
hostObject = WebApp.Start<Startup>(middleware_url);
if (hostObject != null)
Com.DataCool.DotNetExpand.LogHelper.Info("中间件宿主WebApi成功,URL:" + middleware_url);
else
Com.DataCool.DotNetExpand.LogHelper.Error("中间件宿主WebApi错误!");
string result = HttpAPIRequest();
if (!string.IsNullOrEmpty(result))
Com.DataCool.DotNetExpand.LogHelper.Info(result);
}
catch (Exception ex)
{
Com.DataCool.DotNetExpand.LogHelper.Error(ex);
}
IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
EndPoint epSender = (EndPoint)ipeSender;
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 首次探测时间5 秒, 间隔侦测时间2 秒
byte[] inValue = new byte[] { 1, 0, 0, 0, 0x88, 0x13, 0, 0, 0xd0, 0x07, 0, 0 };
serverSocket.IOControl(IOControlCode.KeepAliveValues, inValue, null);
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(MiddlewareIP), 5880);
try
{
serverSocket.Bind(ipEndPoint);
serverSocket.Listen(1024);
socketThread = new Thread(ListenClientConnect);
socketThread.Start();
}
catch (Exception ex)
{
Com.DataCool.DotNetExpand.LogHelper.Error("服务启动失败,原因:" + ex.Message);
}
}

  其实就一句话:hostObject = WebApp.Start<Startup>(middleware_url);这个Startup是用来配置web api的路由规则和实现autofac初始流程的。

 public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
//自定义路由
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//规范api格式仅仅支持XML
var xmlFormatter = new XmlMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new XmlContentNegotiator(xmlFormatter)); var builder = new ContainerBuilder();
//注册本程序集内的ApiControllers
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
//内置日志服务注册
builder.Register(c => new ServiceLog()).As<IServiceLog>().InstancePerRequest(); var iServices = Assembly.Load("Van.Interface");
var services = Assembly.Load("Van.Service");
//根据名称约定(服务层的接口和实现均以Service结尾),实现服务接口和服务实现的依赖
builder.RegisterAssemblyTypes(iServices, services)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces(); var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container); appBuilder.UseAutofacMiddleware(container);
appBuilder.UseAutofacWebApi(config);
appBuilder.UseWebApi(config);
}
}

  

请看一下注释,友情提示本篇文章的“高潮“部分就在这里,任何解释都是苍白的。哈哈哈...

2、使用注入的接口对象

using Newtonsoft.Json;
using System.Web.Http;
using Van.Interface; namespace MiddlewareService.controller
{
/// <summary>
/// 合理用药控制器
/// </summary>
public class VanController : CoolBaseController
{
private readonly IServiceLog _logger;
private readonly IPersonService _PersonService;
public VanController(IServiceLog logService, IPersonService pService)
{
_logger = logService;
_PersonService = pService;
} [HttpPost]
[HttpGet]
public ApiActionResult ClientAnalyzerCheck(string prescriptionInfo)
{
var ds = MiddlewareServiceSvr.Instance.GetMedDictData();
Com.DataCool.DotNetExpand.LogHelper.Info(ds.GetXml());
var result = new ApiActionResult
{
Success = false,
Message = "操作失败!" + "请求参数:" + prescriptionInfo,
Result = null
};
Com.DataCool.DotNetExpand.LogHelper.Info(JsonConvert.SerializeObject(result));
return result;
} /// <summary>
/// 客户端测试用
/// 返回控制器版本号
/// </summary>
/// <returns>ApiActionResult 提示成功和正确返回版本号则表示api是可用状态</returns>
[HttpPost]
[HttpGet]
public ApiActionResult GetVersion()
{
var result = new ApiActionResult
{
Success = true,
Message = "请求客户端IP:" + RequestClientIP + ";操作成功!"+ _PersonService.Get("乔峰").Name,
Result = "1.0.20170222"
};
_logger.Info("客户端发起请求控制器版本号;服务器回复:" + JsonConvert.SerializeObject(result));
return result;
}
}
}

 控制器里面的接口对象是不需要new的,直接在构造函数里面会被IOC容器自动注入进来。这里提一下依赖关系 控制器应用接口对象所在的程序集和接口所在的程序集,还有实体类所在的程序集,看起来是下图的样子。就是注入的接口对象是在另一个程序集里面。宿主服务和控制器所在的程序是依赖外部注入的接口对象的。

3、windows服务咋调试呢

 static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
static void Main(string[] args)
{
#region 初始化日志组件配置信息
string assemblyFilePath = Assembly.GetExecutingAssembly().Location;
string configFilePath = assemblyFilePath + ".config";
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(configFilePath));
#endregion
#region 带参数运行 -i安装服务 -u卸载服务
if (args.Length > 0)
{
AssemblyInstaller myAssemblyInstaller;
myAssemblyInstaller = new AssemblyInstaller();
myAssemblyInstaller.UseNewContext = true;
myAssemblyInstaller.Path = System.AppDomain.CurrentDomain.BaseDirectory + "\\" + System.AppDomain.CurrentDomain.FriendlyName;
System.Collections.Hashtable mySavedState = new System.Collections.Hashtable();
switch (args[0].ToLower())
{
case "-i":
myAssemblyInstaller.Install(mySavedState);
myAssemblyInstaller.Commit(mySavedState);
myAssemblyInstaller.Dispose();
return;
case "-u":
myAssemblyInstaller.CommandLine = new string[1] { "/u" };
myAssemblyInstaller.Uninstall(null);
myAssemblyInstaller.Dispose();
return;
default:
System.Console.WriteLine("------参数说明------");
System.Console.WriteLine("- i 安装服务!");
System.Console.WriteLine("- u 卸载服务!");
System.Console.ReadKey();
return;
}
}
#endregion
//ServiceBase[] ServicesToRun;
//ServicesToRun = new ServiceBase[]
//{
// new MiddlewareServiceSvr()
//};
//ServiceBase.Run(ServicesToRun);
new MiddlewareServiceSvr().OnStart(null);
}
}

安装卸载服务可以用自带参数的办法。那么启动服务,停止服务,删除服务呢,用操作系统提供的命令就行了,比如net start ???,net stop ???。这里???是你的服务名。删除: sc delete ???。 调试服务呢?把服务程序集在生成里面设置成“控制台应用程序”,这样可以在运行的时候用Console.WriteLine()...之类的方法来在控制台查看打印的变量或者调试信息了。 把上面的服务标准运行方式改为直接调服务类的OnStart方法来启动服务。

4、截图展示:

 

 

windows service宿主web api使用"依赖注入"和“控制反转”的技术实践的更多相关文章

  1. .net mvc web api Autofac依赖注入框架-戈多编程

    今天自己搭了一套基于三层的依赖注入mvc web api 的依赖注入框架,在此总结下相关配置 1.设置应用程序的.net Framework版本为 4.5 2.通过Nuget 安装autofac包 I ...

  2. .NET Core的依赖注入[1]: 控制反转

    写在前面:我之前写过一系列关于.NET Core依赖注入的文章,由于.NET Core依赖注入框架的实现原理发生了很大的改变,加上我对包括IoC和DI这些理论层面的东西又有了一些新的理解,所以我在此基 ...

  3. 浅谈(IOC)依赖注入与控制反转(DI)

    前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ...

  4. Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用

    一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...

  5. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->Spring Framework的依赖注入和控制反转

    Dependency Injection and Inversion of Control 1.概述: 1.1相关概念 bean:由IoC容器所管理的对象,也即各个类实例化所得对象都叫做bean 控制 ...

  6. 简单解析依赖注入(控制反转)在Spring中的应用

    IoC——Inversion of Control  控制反转DI——Dependency Injection   依赖注入 大家都知道,依赖注入是Spring中非常重要的一种设计模式.可能很多初学者 ...

  7. 【AutoFac】依赖注入和控制反转的使用

    在开始之前首先解释一下我认为的依赖注入和控制反转的意思.(新手理解,哪里说得不正确还请指正和见谅) 控制反转:我们向IOC容器发出获取一个对象实例的一个请求,IOC容器便把这个对象实例“注入”到我们的 ...

  8. PHP 依赖注入和控制反转再谈(二)

    今天有个朋友看到yii2中介绍的依赖注入一头雾水,之前我写过类似的文章发给他看了,可能还没深入理解吧,这里我再通俗点描述下依赖注入的原理吧,尽可能滴说通俗易懂一点吧:先还是扯下概念性滴问题(概念问题我 ...

  9. 谈谈php依赖注入和控制反转

    要想理解php依赖注入和控制反转两个概念,就必须搞清楚如下的问题: DI--Dependency Injection   依赖注入 IoC--Inversion of Control  控制反转 1. ...

随机推荐

  1. win7(windows 7)系统下安装SQL2005(SQL Server 2005)图文教程——转载

    操作系统:Microsoft Windows 7 旗舰版(32位) 数据库版本:SQL Server 2005 简体中文开发板 数据库下载链接:http://pan.baidu.com/share/l ...

  2. Hibernate懒加载的三种解决方案

    Hibernate懒加载的两种解决方案: 1.Hibernate.initialize(代理对象) 2.在*.hbm.xml映射文件中添加lazy="false"属性 3.使用op ...

  3. C标准I/O建立一个文件仓库

    实现了 增删改查 创建了一个结构体 保存 num name cout price 代码 #include<stdio.h>#include<string.h>#include& ...

  4. 强行在MFC窗体中渲染Cocos2d-x 3.6

    [前言] 把Cocos2dx渲染到另一个应用程序框架中的方法,在2.x时代有很多大神已经实现了,而3.x的做法网上几乎找不着.这两天抽空强行折腾了一下,不敢独享,贴出来供大家参考. [已知存在的问题] ...

  5. HDU-1102-Constructing Roads(并查集)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1102 这题大意就不讲了, 这题很容易,不过我做的很不爽,一个下午,一直WA,后来才发现数组开小了 只开 ...

  6. 使用AIR进行移动APP开发常见功能和问题(下)

    1.  Air如何判断android.ios 平台网络连接状态? Android,使用as3原生api: if(NetworkInfo.isSupported)//只有android支持 Networ ...

  7. overflow:hidden 你所不知道的事

    overflow:hidden 你所不知道的事 overflow:hidden这个CSS样式是大家常用到的CSS样式,但是大多数人对这个样式的理解仅仅局限于隐藏溢出,而对于清除浮动这个含义不是很了解. ...

  8. 实际开发中,实用的辅助iOS开发的工具

    就目前所知,开发iOS绝大部分都是用Xcode,除此工具之外,还有几个好用的可以辅助实际开发中遇到的问题,拥有这种辅助开发技能,在工作中,甚至是以后的面试中,都可能会有不小的帮助. 下面推荐三个实用的 ...

  9. PHP文本处理之中文汉字字符串转换为数组

    在PHP中我们可以通过str_split 将字符串转换为数组,但是却对中文无效,下面记录一下个人将中文字符串转换为数组的方法. 用到的PHP函数 mb_strlen - 获取字符串的长度 mb_sub ...

  10. SQL2012还原数据库操作在本地服务器上操作和用别的电脑远程连接到服务器进行操作的文件路径差异

    在数据库服务器上想还原一个数据库到某个备份文件时期的,服务器的数据库文件本身是保存在 D:\DEVDB目录 通过开发电脑上的MS manager来连接数据库服务器操作还原 虽发现文件卡项上,原始文件名 ...