第二步:
WCF宿主程序用哪种类型比较好? 主要的托管宿主包括,IIS,控制台程序,WPF,Windows Forms,NT Service,Windows服务,COM+作为宿主。IIS部署简单,可以向发布Web Service一样部署,但是仅仅支持HTML协议,而且宿主进程在客户请求时候启动。控制台作为宿主托管简单,一般做简单WCF 编程时候使用。Windows Forms宿主可以提供管理宿主状态的UI,操作更加直观。一般企业WCF宿主会采用Windows服务或者COM+,Windows服务可以长期运行在后台,支持所有的绑定协议,但是安装部署相对复杂。COM+,作为宿主,我们可以利用COM+组建的一些特性,例如对象池等。 一般企业开发选择IIS,Windows服务,性能要求的时候可以考虑COM+,作为宿主。 个人学习IIS和控制台程序相对开发简单,配置便捷。
在这里我们实践一把WCF宿主方式:IIS中的WAS(net.tcp),自己写的wpf程序(http或net.tcp),windows服务(net.pipe)
IIS中的WAS(net.tcp)
1、确定Windows Process Activation Service在windows组件中已经安装。如图:
2、打开IIS对于已经在第一步发布到IIS中的ProductsService服务,点击“高级设置”在“已启用的协议”设置“http,net.tcp”。如图:
3、ProductsClient项目app.config.添加一个endpoint节点
<client>
<endpoint address="http://localhost/ProductsService/ProductsService.svc"
binding="basicHttpBinding"bindingConfiguration="BasicHttpBinding_IProductsService"
contract="ProductsService.IProductsService"name="BasicHttpBinding_IProductsService" />
<endpointaddress="net.tcp://localhost/ProductsService/ProductsService.svc"
binding="netTcpBinding"contract="ProductsService.IProductsService"
name="NetTcpBinding_IProductsService" />
</client>
IIS中WAS(net.tcp)中宿寄wcf服务就大功告成
wpf程序(http或net.tcp)
1、新建类库ProductsServiceLibrary,引入NBuilder.dll。分别添加tblProduct.cs,tblProductInventory.cs,IProductsService.cs和ProductsService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ProductsServiceLibrary
{
/// <summary>
///tblProduct 的摘要说明
/// </summary>
public class tblProduct
{
public tblProduct()
{
//
//TODO: 在此处添加构造函数逻辑
//
}
public int ProductID;
public string Name;
public string ProductNumber;
/// <summary>
/// 0 = 产品是购买的。1 = 产品是内部生产的。
/// </summary>
public bool MakeFlag;
/// <summary>
//0 = 产品不可销售。1 = 产品可销售。
/// </summary>
public bool FinishedGoodsFlag;
public string Color;
////最小库存量。
public int SafetyStockLevel;
/// <summary>
/// 触发采购订单或工作订单的库存水平。
/// </summary>
public int ReorderPoint;
/// <summary>
/// 产品的标准成本。
/// </summary>
public decimal StandardCost;
/// <summary>
/// 销售价格。
/// </summary>
public decimal ListPrice;
/// <summary>
/// 产品的规格。
/// </summary>
public string Size;
/// <summary>
/// Size 列的测量单位。
/// </summary>
public string SizeUnitMeasureCode;
/// <summary>
/// Weight 列的测量单位。
/// </summary>
public string WeightUnitMeasureCode;
/// <summary>
/// 产品的重量。
/// </summary>
public decimal Weight;
/// <summary>
/// 生产产品所需的天数。
/// </summary>
public int DaysToManufacture;
/// <summary>
/// R = 平地 M = 山地 T = 旅行 S = 标准
/// </summary>
public string ProductLine;
/// <summary>
/// H = 高 M = 中 L = 低
/// </summary>
public string Class;
/// <summary>
/// W = 女式 M = 男式 U = 通用
/// </summary>
public string Style;
/// <summary>
/// 产品所属的产品子类别。指向 ProductSubCategory.ProductSubCategoryID 的外键。
/// </summary>
public int ProductSubcategoryID;
/// <summary>
/// 产品所属的产品型号。指向 ProductModel.ProductModelID 的外键。
/// </summary>
public int ProductModelID;
/// <summary>
/// 产品开始销售的日期。
/// </summary>
public DateTime SellStartDate;
/// <summary>
/// 产品停止销售的日期。
/// </summary>
public DateTime SellEndDate;
/// <summary>
/// 产品停产的日期。
/// </summary>
public DateTime DiscontinuedDate;
/// <summary>
/// 行的上次更新日期和时间。
/// </summary>
public DateTime ModifiedDate;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ProductsServiceLibrary
{
/// <summary>
///ProductInventory 的摘要说明
/// </summary>
public class tblProductInventory
{
public tblProductInventory()
{
//
//TODO: 在此处添加构造函数逻辑
//
}
/// <summary>
/// 产品标识号。指向 Product.ProductID 的外键。
/// </summary>
public int ProductID;
/// <summary>
/// 库存位置标识号。指向 Location.LocationID 的外键。
/// </summary>
public int LocationID;
/// <summary>
/// 库存位置中的存储间。
/// </summary>
public string Shelf;
/// <summary>
/// 库存位置中架子上的存储容器。
/// </summary>
public int Bin;
/// <summary>
/// 库存位置中的产品数量。
/// </summary>
public int Quantity;
/// <summary>
/// 行的上次更新日期和时间。
/// </summary>
public DateTime ModifiedDate;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace ProductsServiceLibrary
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService”。
[ServiceContract]
public interface IProductsService
{
// Get the product number of every product
[OperationContract]
List<string> ListProducts();
// Get the details of a single product
[OperationContract]
ProductData GetProduct(string productNumber);
// Get the current stock level for a product
[OperationContract]
int CurrentStockLevel(string productNumber);
// Change the stock level for a product
[OperationContract]
bool ChangeStockLevel(string productNumber, short newStockLevel, string shelf, int bin);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using FizzWare.NBuilder;
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、服务和配置文件中的类名“Service”。
namespace ProductsServiceLibrary
{
[DataContract]
public class ProductData
{
[DataMember]
public string Name;
[DataMember]
public string ProductNumber;
[DataMember]
public string Color;
[DataMember]
public decimal ListPrice;
}
public class ProductsServiceImpl : IProductsService
{
static IList<tblProduct> ps = Builder<tblProduct>.CreateListOfSize(50).Build();
static IList<tblProductInventory> pis = Builder<tblProductInventory>.CreateListOfSize(50).Build();
public List<string> ListProducts()
{
var p = ps.Select(item => item.ProductNumber).ToList();
return p;
}
public ProductData GetProduct(string productNumber)
{
ProductData productData = null;
tblProduct matchingProduct = ps.First(item => string.Compare(item.ProductNumber, productNumber) == 0);
productData = new ProductData()
{
Name = matchingProduct.Name,
ProductNumber = matchingProduct.ProductNumber,
Color = matchingProduct.Color,
ListPrice = matchingProduct.ListPrice
};
return productData;
}
public int CurrentStockLevel(string productNumber)
{
int stockLevel = 0;
// Calculate the sum of all quantities for the specified product
stockLevel = (from pi in pis
join p in ps
on pi.ProductID equals p.ProductID
where String.Compare(p.ProductNumber, productNumber) == 0
select (int)pi.Quantity).Sum();
return stockLevel;
}
public bool ChangeStockLevel(string productNumber, short newStockLevel, string shelf, int bin)
{
//var pr = Builder<tblProduct>.CreateListOfSize(50).Build();
//var pri = Builder<tblProductInventory>.CreateListOfSize(50).Build();
int productID = (from p in ps
where String.Compare(p.ProductNumber, productNumber) == 0
select p.ProductID).First();
// Find the ProductInventory object that matches the parameters passed
// in to the operation
tblProductInventory productInventory = pis.First(
pi => String.Compare(pi.Shelf, shelf) == 0 &&
pi.Bin == bin && pi.ProductID == productID);
// Update the stock level for the ProductInventory object
productInventory.Quantity += newStockLevel;
// Save the change back to the database
return true;
}
}
}
2、新建WPF应用程序项目ProductsServiceHost,添加对ProductsServiceLibrary类库引用
MainWindow前台:
<Grid>
<Button Height="23" HorizontalAlignment="Left" Margin="51,60,0,0" Name="start" VerticalAlignment="Top" Width="75" Click="start_Click">Start</Button>
<Button Height="23" HorizontalAlignment="Right" Margin="0,60,56,0" Name="stop" VerticalAlignment="Top" Width="75" IsEnabled="False" Click="stop_Click">Stop</Button>
<Label Height="23" HorizontalAlignment="Left" Margin="43,0,0,111" Name="label1" VerticalAlignment="Bottom" Width="88">Service Status:</Label>
<TextBox IsReadOnly="True" Margin="133,0,59,107" Name="status" Text="Service Stopped" Height="26" VerticalAlignment="Bottom"></TextBox>
</Grid>
MainWindow后台:
ServiceHost productsServiceHost = null;
private void start_Click(object sender, RoutedEventArgs e)
{
try
{
productsServiceHost = new ServiceHost(typeof(ProductsServiceImpl));
productsServiceHost.Open();
stop.IsEnabled = true;
start.IsEnabled = false;
status.Text = "Service Running";
}
catch (Exception ex)
{
handleException(ex);
}
}
private void handleException(Exception ex)
{
MessageBox.Show(ex.Message);
}
private void stop_Click(object sender, RoutedEventArgs e)
{
try
{
productsServiceHost.Close();
stop.IsEnabled = false;
start.IsEnabled = true;
status.Text = "Service Stopped";
}
catch (Exception ex)
{
handleException(ex);
}
}
3、app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="ProductsServiceLibrary.ProductsServiceImpl">
<endpoint address="net.tcp://localhost:8080/TcpService"
binding="netTcpBinding"
bindingConfiguration="" name="NetTcpBinding_IProductsService"
contract="ProductsServiceLibrary.IProductsService" />
<endpoint address="http://localhost:8000/ProductsService/Service.svc"
binding="basicHttpBinding" name="BasicHttpBinding_IProductsService"
contract="ProductsServiceLibrary.IProductsService" />
</service>
</services>
<behaviors>
</behaviors>
</system.serviceModel>
</configuration>
注意这里面设置了两个endpoint,系统会对这两个地址启动wcf服务。客户端选择任何一个地址引入service都可以
<client>
<endpoint address="net.tcp://localhost:8080/TcpService"
binding="netTcpBinding" contract="ProductsService.IProductsService"
name="NetTcpBinding_IProductsService" />
<endpoint address="http://localhost:8000/ProductsService/Service.svc"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IProductsService"
contract="ProductsService.IProductsService"
name="BasicHttpBinding_IProductsService" />
</client>
至于windows服务作为宿主
1、新建windows服务项目WindowsProductsService,添加ProductsServiceLibrary类库引用,将Service1.cs重命名ServiceHostController.cs
public partial class ServiceHostController : ServiceBase
{
private ServiceHost productsServiceHost;
public ServiceHostController()
{
InitializeComponent();
// The name of the service that appears in the Registry
this.ServiceName = "ProductsService";
// Allow an administrator to stop (and restart) the service
this.CanStop = true;
// Report Start and Stop events to the Windows event log
this.AutoLog = true;
}
protected override void OnStart(string[] args)
{
productsServiceHost = new ServiceHost(typeof(ProductsServiceImpl));
NetNamedPipeBinding binding = new NetNamedPipeBinding();
productsServiceHost.AddServiceEndpoint(typeof(IProductsService),
binding, "net.pipe://localhost/ProductsServicePipe");
productsServiceHost.Open();
}
protected override void OnStop()
{
productsServiceHost.Close();
}
2、在ServiceHostController.cs[设计],右键添加安装。点击serviceInstaller1,修改其属性ServiceName为ServiceHostController;点击serviceProcessInstaller1,修改Account
为LocalSystem。生成项目。
3、用管理员权限打开Visual Studio 命令提示。切到WindowsProductsService项目Debug文件夹下,运行installutil WindowsProductsService.exe。windows服务则安装完成
4、在前面的ProductsClient客户端配置文件中添加一个endpoint
<endpoint address="net.pipe://localhost/ProductsServicePipe"
binding="netNamedPipeBinding"
contract="ProductsService.IProductsService"
name="NetNamedPipeBinding_IProductsService" />
ok,这样就大功告成了。欣慰一下吧。
小提示:如果你想卸载windows服务,可以运行installutil /u WindowsProductsService.exe