Wcf项目代码结构如下:
Book.Common:公共类库,提供公共方法。类库
Book.Models:提供实体类。类库
Book.WcfService:Wcf接口以及实现。类库
Book.WinForm:显示获取信息。winForm窗体
Hosting:服务宿主,将Wcf服务承载到一个进程中。控制台程序
Book.WcfService定义IBookService和BookService
[ServiceContract] public interface IBookService { [OperationContract] string GetBook(string bookId); }
需要引用System.ServiceModel。Wcf是基于契约的,这里将接口类声明为服务契约,方法生命为操作契约
public class BookService : IBookService { public string GetBook(string bookId) { Books book = GetBookById(bookId); string xml = XmlHelper.ToXml<Books>(book); return xml; } public Books GetBookById(string id) { Books book = new Books(); book.Name = "C#从入门到放弃"; book.Price = 54; book.AuthorId = 1; return book; } }
以上是接口的实现。
上面需要引用Books类
在Book.Models类库里面声明
[DataContract] public class Books { [DataMember] public string Name { get; set; } [DataMember] public int Price { get; set; } [DataMember] public int AuthorId { get; set; } }
公共方法类XmlHelper包含在Book.Common里
public class XmlHelper { /// <summary> /// 反序列化成对象 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="filename">XML文件路径</param> /// <returns></returns> public static T ParseXml<T>(string filename) { T obj = default(T); XmlSerializer serializer = new XmlSerializer(typeof(T)); FileStream fs = new FileStream(filename, FileMode.Open); try { obj = (T)serializer.Deserialize(fs); } catch (System.Exception ex) { string s = ex.Message; throw ex; } finally { fs.Close(); } return obj; } /// <summary> /// 反序列化成对象 /// </summary> /// <param name="filename">XML文件路径</param> /// <param name="type">对象类型</param> /// <returns></returns> public static object ToObject(string filename, Type type) { object obj; XmlSerializer serializer = new XmlSerializer(type); FileStream fs = new FileStream(filename, FileMode.Open); try { obj = serializer.Deserialize(fs); } catch (System.Exception ex) { string s = ex.Message; throw ex; } finally { fs.Close(); } return obj; } /// <summary> /// 反序列化成对象 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="data">XML数据对象字符串</param> /// <returns></returns> public static T DeSerializer<T>(string data) { T obj = default(T); XmlSerializer serializer = new XmlSerializer(typeof(T)); try { using (StringReader sr = new StringReader(data)) { XmlSerializer xz = new XmlSerializer(typeof(T)); obj = (T)serializer.Deserialize(sr); } } catch (System.Exception ex) { string s = ex.Message; throw ex; } return obj; } /// <summary> /// 创建XML文件 /// </summary> /// <param name="fullFileName">XML文件名</param> /// <param name="data">XML字符串</param> public static void CreateXml(string fullFileName, string data) { using (StreamWriter sw = new StreamWriter(fullFileName, false, Encoding.UTF8)) { sw.Write(data); } } /// <summary> /// 把对象转换成字符串 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="t">对象实体</param> /// <returns></returns> public static string ToXml<T>(T t) { using (StringWriter sw = new StringWriter()) { XmlSerializer xz = new XmlSerializer(t.GetType()); xz.Serialize(sw, t); return sw.ToString(); } } }
上述服务需要一个宿主来承载,他不会自主运行。Wcf给予终结点(EndPoint)的手段进行通信
在Hosting的配置文件App.config中的configuration中添加声明
<system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="metadataBehavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8888/BookService/metadata" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="metadataBehavior" name="Book.WcfService.BookService"> <endpoint address="http://127.0.0.1:8888/BookService" binding="wsHttpBinding" bindingConfiguration="" contract="Book.WcfService.IBookService" /> </service> </services> </system.serviceModel>
上面可以看到终结点Endpoint是由地址Address、绑定Binding和契约contract组成,也就是EndPoint=ABC
WCF服务的描述通过元数据(Metadata)的形式发布出来。
上面的配置我们声明了元数据的获取地址,
在Hosting的Program.cs中声明
using (ServiceHost host = new ServiceHost(typeof(BookService)))
{
//服务打开时发生
host.Opened += delegate
{
Console.WriteLine("BookService 按任意键结束");
};
host.Open();
Console.ReadKey();
}
此时到文件夹中运行Hosting.exe 注意使用管理员身份运行。不然会报错
那么浏览器输入http://127.0.0.1:8888/BookService/metadata 就可以看到元数据了
运行Hosting的情况下,在Book.WinForm的引用 右键 添加服务引用
如上添加服务
在Book.WinForm的点击事件上添加
private void button1_Click(object sender, EventArgs e) { Books book = new Books(); BookServiceRef.BookServiceClient client = new BookServiceRef.BookServiceClient(); msgBox.Text = client.GetBook("1"); book=XmlHelper.DeSerializer<Books>(msgBox.Text); textBox1.Text = book.Name; textBox2.Text = book.Price.ToString(); textBox3.Text = book.AuthorId.ToString(); }
结果如图
上述方法是采用继承与ClientBase<T>的对象进行服务调用,也就是这里的BookServiceRef。换一种方式呢?
关键在于我们要找到获取书籍信息的方法,也就是GetBook,显然Winform窗体程序不能直接引用Book.WcfService,这样所有的内容都暴露了,没有意义
那么可以新建一个类库,用来放接口IBookService
新建Book.Contracts类库
将IBookService粘贴到里面,让Book.WcfService和Book.WinForm引用它
将原来Book.WinForm的服务引用删除
将点击事件换成
using(ChannelFactory<IBookService> channelFactory=new ChannelFactory<IBookService>("ws_IBookService")) { Books book = new Books(); IBookService proxy = channelFactory.CreateChannel(); using(proxy as IDisposable) { msgBox.Text = proxy.GetBook("1"); book = XmlHelper.DeSerializer<Books>(msgBox.Text); textBox1.Text = book.Name; textBox2.Text = book.Price.ToString(); textBox3.Text = book.AuthorId.ToString(); } }
在Book.WinForm的配置里添加
<system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding_IBookService" /> </wsHttpBinding> </bindings> <client> <endpoint address="http://127.0.0.1:8888/BookService" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IBookService" contract="Book.Contracts.IBookService" name="ws_IBookService"> <identity> <userPrincipalName value="DEVELOPERAdministrator" /> </identity> </endpoint> </client> </system.serviceModel>
到这里就结束了