WCF: 从初学者的角度来看和教程 - 续集






4.77/5 (8投票s)
本文将介绍如何在ASP.NET网站中消费WCF服务,以及如何创建支持AJAX的WCF服务。
引言
本文是我之前关于WCF文章的延续。大家可以通过访问以下URL来查看之前的文章:https://codeproject.org.cn/Articles/566691/WCF-From-a-Beginners-perspective-a-Tutorial。
在这篇文章中,我想解释并展示如何在ASP.NET Web应用程序中消费WCF服务,以及如何通过代码和大量插图编写支持ASP.NET AJAX的WCF服务。
对于不了解WCF是什么、它做什么以及其他相关知识的读者,请访问上面列出的URL,它提供了对该主题的初步理解,包括如何开始、如何着手编写WCF服务等。让我们立即开始吧。敬请关注。
背景
没有特别的背景知识要求,但既然这是对上述文章(请访问URL)的续篇,我建议您先浏览一下,不会有坏处。是的,拥有Visual Studio 2008是先决条件。
使用代码
让我们从代码部分开始。
- 打开您的Visual Studio 2008客户端,然后单击“新建”>“网站”。
- 这将打开一个对话框,如下所示。从模板中选择“WCF服务”,指定要存储WCF服务的目录,然后单击“确定”。
- Visual Studio将打开一个新的解决方案。该解决方案将包含一个WCF服务项目。在App_Code文件夹下,您可以找到两个类文件,名为*Service.cs*和*IService.cs*。另外,Service.svc文件也将显现。类文件中会有一些默认的实现。请删除它们,因为现在我们将编写自己的功能。
- 我将说明我们将在该服务中做什么。我非常喜欢电视剧,因此这个WCF服务将帮助我们输入喜欢的电视剧的详细信息,并在输入后帮助我们查看它们。在*IService.cs*的**ServiceContract**下,我有两个操作:**InsertSeriesDetails()**和**GetSeriesDetails()**。在我的**DataContract**中,我有三个属性作为**DataMembers**,因为它们在WCF服务被消费时会被序列化。电视连续剧详情的插入和检索将通过数据库进行。因此,让我们在SQL Server 2008的数据库中创建一个表。
- 打开您的SQL Management Studio,连接到您的服务器,然后在您想要的数据库中创建一个名为**dbo.TVSeries**的表。目前,我在表中包含3列:**TVSeriesName**, **FavMaleCharacter**, **FavFemaleCharacter**。您可以根据需要添加更多列。复制下面的SQL脚本并在您的SQL Management Studio中执行它。
- 让我们回到WCF服务的创建。在*IService.cs*文件中开始编码,如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.Data; [ServiceContract] public interface IService { [OperationContract] string InsertSeriesDetails(TVSeries tvSeriesObj); [OperationContract] List<TVSeries> GetSeriesDetails(); } [DataContract] public class TVSeries { string seriesName = ""; string favMaleChar = ""; string favFemaleChar = ""; [DataMember] public string TVSeriesName { get {return seriesName ;} set { seriesName=value;} } [DataMember] public string FavMaleCharacter { get {return favMaleChar ;} set {favMaleChar=value ;} } [DataMember] public string FavFemaleCharacter { get {return favFemaleChar ;} set { favFemaleChar=value;} } }
我想解释一下我在*IService.cs*文件中编写的代码。这里我有一个接口作为服务契约,它包含两个操作契约,也就是说,用通俗的话来说,它下面定义了两个方法。接下来,我们有一个名为
TVSeries
的用户定义类,其中包含三个DataMembers,这些DataMembers向客户端解释了它包含什么样的数据。在编写完*IService.cs*文件后,我们必须在Service.cs文件中编写方法的实现。让我们继续。 - 将下面代码复制到您的*Service.cs*文件中:
- 您可以创建一个新的解决方案,或在上述解决方案中添加另一个项目,随您喜欢。接下来,单击“新建”>“网站”。
- 将打开一个对话框,其中包含许多模板,单击“ASP.NET网站”,指定存储位置,然后按“确定”。
- 将添加一个空的网站到您的解决方案。请参考附带的压缩代码来设计页面。基本上,我有**3个文本框**,用户将在其中输入电视连续剧名称、他最喜欢的男性和女性角色的名字,**2个按钮**,分别称为“插入”和“点击查看详情”,以及**1个网格视图**。它看起来有点像这样:
- 现在到了关键部分,添加服务引用到网站。右键单击网站项目,然后单击“**添加服务引用**”。它将打开一个对话框,您需要在其中输入您刚刚创建的服务URL,然后按“转到”。ASP.NET引擎会列出WCF服务公开的所有操作,您需要提供一个命名空间并按“确定”。请参阅下图以获取进一步参考。
- 现在,打开*Default.aspx.cs*文件并输入如下代码:
- 调试解决方案,它不应该抛出任何错误,并且功能应该按预期工作。请看下面:
- 我想在这里强调一个重要方面,在消费WCF服务时,您可能会遇到如下异常:
CREATE TABLE dbo.TVSeries(
[TVSeriesName] VARCHAR(max) NOT NULL,
[FavMaleChar] VARCHAR(max) NOT NULL,
[FavFemaleChar] VARCHAR(max) NOT NULL)
执行脚本后,您将在您想要的数据库的“表”类别下找到如下内容:
我们现在不会向表中插入行,而是通过ASP.NET网站来完成。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;
public class Service : IService
{
SqlConnection xconn = new SqlConnection(
ConfigurationManager.ConnectionStrings["connectionStrings"].ToString());
public string InsertSeriesDetails(TVSeries tvSeriesObj)
{
string msg = string.Empty;
SqlCommand xcmd = new SqlCommand("Insert into Development.dbo.TVSeries(
TVSeriesName,FavMaleChar,FavFemaleChar) values(@seriesName,@favMale,@favFemale)", xconn);
xcmd.Parameters.AddWithValue("@seriesName", tvSeriesObj.TVSeriesName);
xcmd.Parameters.AddWithValue("@favMale", tvSeriesObj.FavMaleCharacter);
xcmd.Parameters.AddWithValue("@favFemale", tvSeriesObj.FavFemaleCharacter);
try
{
xconn.Open();
int ret = xcmd.ExecuteNonQuery();
if (ret == 1)
{
msg = "Hurray...You have now entered your favorite TV Series Details..";
}
else
{
msg = "Some error occurred..Please try again";
}
}
finally
{
xconn.Close();
}
return msg;
}
public List<TVSeries> GetSeriesDetails()
{
List<TVSeries> TVlist = new List<TVSeries>();
SqlDataAdapter da = new SqlDataAdapter("select * from Development.dbo.TVSeries", xconn);
DataTable dt = new DataTable();
try
{
xconn.Open();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
TVSeries obj = new TVSeries();
obj.TVSeriesName = dt.Rows[i][0].ToString();
obj.FavMaleCharacter = dt.Rows[i][1].ToString();
obj.FavFemaleCharacter = dt.Rows[i][2].ToString();
TVlist.Add(obj);
}
}
}
finally
{
xconn.Close();
}
return TVlist;
}
}
在上面的代码中,我们尝试与包含我们表的SQL数据库建立连接,然后实现了两个操作契约,即InsertSeriesDetails()
和GetSeriesDetails()
。InsertSeriesDetails()
将TVSeries
类作为其参数,然后我们定义一个SQLCommand
对象,提供适当的Insert查询,然后调用ExecuteNonQuery()
方法,将一行插入到数据库中。GetSeriesDetails()
涉及一个SQLDataAdapter
,它使用SQL表中的记录填充一个DataTable
,然后我们将结果绑定到一个List。
我想在此指出,在使用WCF服务时,我们不能直接使用DataTable来绑定数据。这一点稍后将在文章中讨论。请留意。.
我个人喜欢将连接字符串分离到配置文件中,而不是直接在代码隐藏文件中使用完整的连接字符串。您可以如下定义连接字符串:
<connectionStrings>
<add name="connectionStrings" connectionString="Data Source=DEVELOPER-PC;Initial Catalog=Development;
Integrated Security=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
此外,配置文件的一个重要部分将是终结点配置。目前我还没有在这里修改终结点,已经将它们留为默认值。我在配置文件中有wsHttpBinding,如果您愿意,可以更改它。
<endpoint address="" binding="wsHttpBinding" contract="IService">
现在,WCF服务已准备就绪,右键单击Service.svc文件,然后选择并单击“在浏览器中查看”,ASP.NET开发服务器将打开您的浏览器,您可以在那里看到您的WCF服务,类似如下:
好了,您的服务已启动并正在运行。现在,让我们创建一个ASP.NET网站,它将作为客户端来消费此WCF服务。
我使用了一些CSS技巧和窍门对页面进行了一点设计。您可以在*StyleSheet.css*页面中找到这些内容。没关系,我们继续前进...
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using TV;
using System.Collections.Generic;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
lblResult.Visible = false;
}
protected void Button1_Click(object sender, EventArgs e)
{
if (!(string.IsNullOrEmpty(txtSeriesName.Text) &&
string.IsNullOrEmpty(txtMale.Text) && string.IsNullOrEmpty(txtFemale.Text)))
{
ServiceClient obj = new ServiceClient();
TVSeries obj2 = new TVSeries();
obj2.TVSeriesName = txtSeriesName.Text;
obj2.FavMaleCharacter = txtMale.Text;
obj2.FavFemaleCharacter = txtFemale.Text;
string message = obj.InsertSeriesDetails(obj2);
lblResult.Visible = true;
lblResult.Text = message;
}
else
{
lblResult.Visible = true;
lblResult.Text = "Please enter the TV series details..Then hit Insert button...";
txtSeriesName.Focus();
}
}
protected void Button2_Click(object sender, EventArgs e)
{
lblResult.Visible = false;
ServiceClient obj3 = new ServiceClient();
IList<TVSeries> listTV = obj3.GetSeriesDetails();
grdView.DataSource = listTV;
grdView.DataBind();
}
}
上面的代码没有什么特别之处,我们只是创建了代理类ServiceClient
的一个对象,并使用该对象在两个按钮点击事件中调用WCF服务公开的两个操作。构建代码,它应该可以无误地构建。
另外,请检查网站的配置文件中的客户端终结点详细信息,应该与如下内容相似:
<client>
<endpoint address="https://:51861/NewService/Service.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService"
contract="TV.IService" name="WSHttpBinding_IService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
{"The server was unable to process the request due to an internal error.
For more information about the error, either turn on IncludeExceptionDetailInFaults
(either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior)
on the server in order to send the exception information back to the client, or turn on tracing
as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs."}
这表明您的WCF服务中存在问题,有一个未处理的异常。我们需要找到代码或其他问题根源。为此,我们在WCF服务的配置文件中包含**includeExceptionDetailInFaults=true**属性,以便获得关于发送回客户端的未处理异常或错误的充分信息。请看下面:
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
在我的例子中,WCF服务代码和我的数据库中的列名不匹配,因此我也收到了上面巨大的异常。我包含了上述属性,并能够找出我的错误并最终解决它。请参阅下图以获取进一步参考。
希望读者对如何在ASP.NET Web应用程序中消费WCF服务有了初步了解。接下来,让我们专注于如何编写**支持AJAX的WCF服务**。敬请关注。
以下是MSDN的一段摘录:
ASP.NET AJAX由客户端脚本库和服务器组件组成,这些组件集成在一起,提供了一个强大的开发框架。要从ASP.NET页面访问服务:一旦将服务URL添加到页面上的ASP.NET Script Manager控件,就可以使用看起来就像普通JavaScript函数调用的JavaScript代码来调用服务操作。
大多数Windows Communication Foundation (WCF) 服务可以通过添加适当的ASP.NET AJAX终结点来公开为与ASP.NET AJAX兼容的服务。
那么,我们从上面的文本中理解了什么?让我通过一个简单的例子来解释。您有一个数学网站,需要在网站上的几个标签中显示一个简单的Pascal系列,并对其进行一些效果处理,使网站看起来很酷。那么您现在该怎么办?
创建一个支持AJAX的WCF服务,并包含一个操作契约来处理您的Pascal系列的编码部分,然后使用JavaScript代码将该服务调用到您的标签上,无需进行任何服务器端编码,从而避免网站出现性能问题和延迟。
总结是您可以直接从客户端JavaScript代码与WCF服务通信。
让我们继续看代码。
- 打开您的VS 2008,然后单击“新建”>“网站”。
- 选择“ASP.NET网站”模板,并指定您想要的存储位置。
- 右键单击AJAX项目,然后单击“添加新项”。从列表中选择“AJAX启用的WCF服务”,并为其指定一个名称。
- 添加AJAX启用的WCF服务后,将打开一个类文件,其中包含一些默认代码。这里有一个
[ServiceContract(Namespace="")]
属性。将命名空间指定为您在本项目中给出的名称,在本例中为**AJAX**。在.cs文件中编写一些操作契约(方法),这些方法最终将被公开。请参考下面的代码以供进一步参考。 - 打开*Default.aspx*页面,并从工具箱的AJAX部分拖放一个ScriptManager。然后,我们需要配置ScriptManager以接受WCF服务,如下所示:
- 在您的*Default.aspx*页面上拖放两个标签和两个HTML类型的文本框(基本上是Input (text)),以及一个HTML类型的按钮,看起来像这样:
- 现在到了关键部分,编写JavaScript代码,请在此处稍加注意。我们在按钮“Click Me”的
onclick
事件中定义了一个名为“Multiply()
”的函数,在该函数本身中,我们将配置如何调用WCF服务的操作契约。从下面的代码开始,直接在*Default.aspx*页面中编写: - 构建解决方案,检查是否有任何错误,然后开始调试项目。它应该像这样:
using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
[ServiceContract(Namespace = "AJAX")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MathService
{
// Add [WebGet] attribute to use HTTP GET
[OperationContract]
public int Multiplication(int a, int b)
{
return a * b;
}
}
我们有一个操作契约,它只是将两个整数相乘并将结果返回。现在,让我们配置客户端来访问服务。
<asp:ScriptManager ID="ScriptManager1" runat="server">
<Services>
<asp:ServiceReference Path="MathService.svc" />
</Services>
</asp:ScriptManager>
您也可以通过“属性”>“服务”(添加集合)来完成此操作。现在脚本管理器已配置,我们需要在页面上进行一些设计。
<asp:Label ID="Label1" runat="server" Text="Enter one number:"></asp:Label>
<input id="txtOne" type="text" />
<br />
<br />
<asp:Label ID="Label2" runat="server"
Text="Enter another number:"></asp:Label>
<input id="txtTwo" type="text" /><br />
<br />
<input id="btnClick" type="button" value="Click Me" onclick="return Multiply()" /><br />
<div id="new"></div>
我们还有一个名为“new”的div,我们将在其中显示结果。Default.aspx页面将如下所示:
<script type="text/javascript" language="javascript">
function Multiply() {
var service = new AJAX.MathService();
service.Multiplication($get("txtOne").value,
$get("txtTwo").value, onSuccess, onFailure);
}
function onSuccess(result) {
$get("new").innerHTML = "The multiplication is:"+result;
}
function onFailure(result) {
window.alert(result.get_message());
}
</script>
现在,让我解释上面的JavaScript代码。在Multiply()函数中,我们创建了一个名为“service”的新变量,它引用了前面创建的MathService服务。然后,我们使用“service”变量来访问WCF服务的操作契约。我们将4个参数传递给函数,其中2个是用户将在前端输入的数值,另外2个是进一步的JavaScript函数,它们定义了在操作成功或失败时会发生什么。因此,函数被称为onSuccess和onFailure。您可以给出任何您想要的名称,但请记住实现它们。
在成功实现操作契约后,我尝试在“new”div中编码一条消息,而在onFailure时,如果实现失败,则会发出警报消息。代码本身几乎不言自明。
因此,我们已经看到了如何创建支持AJAX的WCF服务以及如何通过JavaScript从ASP.NET客户端调用它。
好了,文章也该结束了。在这篇文章中,我们学习了:
- 如何通过ASP.NET网站消费WCF服务。
- 如何编写支持AJAX的WCF服务并在ASP.NET网站中消费它。
关注点
我在这篇文章的写作过程中学到了很多。
我想与读者分享的是,我们不能直接在WCF服务中使用DataTable来绑定数据。这实际上是一种不良的做法。
如果我们从WCF服务返回DataTable,我们实际上是在破坏面向服务的体系结构。契约没有定义DataTable包含哪些列。因此,如果将来列发生更改,客户端将遇到问题,因为列已更改,但契约没有。
尽管这不是一种好的做法,但通过提供TableName,我们仍然可以在WCF服务中使用DataTable。
DataTable dt=new DataTable();
dt.TableName="newTestTable";
//some codes over here
return dt;
我已将代码附加到压缩文件夹中。它基本上包含三个项目:NewService、SampleWeb和AJAX。
NewService是WCF服务项目,SampleWeb是消费NewService的ASP.NET网站项目。AJAX实现了支持AJAX的WCF服务及其通过Web应用程序的消费。充分利用这些代码,享受它吧。
历史
- 2013年4月2日:版本 1.0