第二步:
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