玩转C科技.NET

从学会做人开始认识这个世界!http://volnet.github.io

导航

[WCF]继承

在过去中,我们已经习惯了C#继承的各个特性,我们可以按如下的方式定义我们的继承关系:

[ServiceContract]
public interface ISimpleCalculator
{
    //Other Members
    [OperationContract]
    int Add(int arg1, int arg2);
}

[ServiceContract]
public interface IScientificCalculator : ISimpleCalculator
{
    [OperationContract]
    int Multiply(int arg1, int arg2);
}

Ok,不要担心,在服务端这样的特性依然稳健地存在着:

public class ScientificCalculatorService : IScientificCalculator
{
    //Other Members

    #region IScientificCalculator Members

    public int Multiply(int arg1, int arg2)
    {
        return arg1 * arg2;
    }

    #endregion

    #region ISimpleCalculator Members

    public int Add(int arg1, int arg2)
    {
        return arg1 + arg2;
    }

    #endregion
}

但是紧接着,Client端呢?

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference.IScientificCalculator")]
public interface IScientificCalculator {
    //Other Members

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ISimpleCalculator/Add", ReplyAction="http://tempuri.org/ISimpleCalculator/AddResponse")]
    int Add(int arg1, int arg2);
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IScientificCalculator/Multiply", ReplyAction="http://tempuri.org/IScientificCalculator/MultiplyResponse")]
    int Multiply(int arg1, int arg2);
}

在Reference.cs文件内,我们只能看到IScientificCalculator 接口的身影,却找不到ISimpleCalculator的踪迹。而事实上我们在服务端对这两个接口都定义了ServiceContract的Attribute,也许这对你来说并不重要,或者你不太关心这些继承特性所带来的优势,但是正也是因为这些继承特性所能带来的优势(包括多态等经典的OO特性)我们需要改造这个Reference.cs以使其适应我们“真正的需要”。类似以下的应用将会失败:

static void Main(string[] args)
{
    ScientificCalculatorClient calculator = new ScientificCalculatorClient();

    UseScientificCalculator(calculator);
    calculator.Close();
}

//Will not be supported now
static void UseSimpleCalculator(ISimpleCalculator calculator)
{
    Console.WriteLine("Calculator Add : {0}", calculator.Add(5, 4));
}

static void UseScientificCalculator(IScientificCalculator calculator)
{
    Console.WriteLine("Calculator Add : {0}", calculator.Add(5, 4));
    Console.WriteLine("Calculator Multiply : {0}", calculator.Multiply(5, 4));
}

当前的问题就是:

ISimpleCalculator接口在客户端是不被识别的。要解除这样的矛盾,就是要让客户端也拥有该接口。

首先我们考虑到我们与Service之间的通信是依赖ServiceContract来描述的,ServiceContract就类似OO中的Interface,一经发布就不可以修改了(尽量!)。我们能做的最好就是能在Client端将这些内容重新搭建起来,包括之间的继承关系。

在Add ServiceReference之后系统为我们自动生成了很多内容,找到Reference.cs,这将是我们大刀阔斧的地方……

我们可以看到它里面只实现了一个IScientificCalculator接口,这是我们先前就提到过的,我们的系统调用服务,都是通过从这里获取它们想要的“服务端”的一些类去构造本地实例来完成一系列操作的。那么我们现在只需要在这里引入相应的接口继承结构即可……

将原来实现的唯一接口注释掉,并添加以下代码:

//[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
//[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "ServiceReference.IScientificCalculator")]
[ServiceContract]
public interface ISimpleCalculator
{
    //Other Members

    // TODO: Add your service operations here
    [OperationContract]
    int Add(int arg1, int arg2);
}
//[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
//[System.ServiceModel.ServiceContractAttribute(ConfigurationName = "ServiceReference.IScientificCalculator")]
[ServiceContract(ConfigurationName="ServiceReference.IScientificCalculatorVolnet")]
public interface IScientificCalculator : ISimpleCalculator
{       
    [OperationContract]
    int Multiply(int arg1, int arg2);
}

我们需要using System.ServiceModel之后才可使用以上代码,该代码片断其实没有什么很特别的地方,它与服务端的接口继承没有什么大的出入,唯一需要关注的则是我黑体标注的“ConfigurationName="ServiceReference.IScientificCalculatorVolnet"”,注意,我这里不是在为自己的昵称做广告噢,而是以示区别。(刚刚本人就为这个问题折腾了我许久,终于搞出来了……汗)

为什么要添加ConfigurationName呢?这就要先看以下一段代码(app.config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <system.serviceModel>
        <!--……-->
        <client>
            <endpoint address="http://localhost:8080/ScientificCalculatorService"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IScientificCalculator"
                contract="ServiceReference.IScientificCalculatorVolnet" name="WSHttpBinding_IScientificCalculator">
                <identity>
                    <userPrincipalName value="GONGCEN-NB\GongCen" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

注意到在这里我又再次以自己的昵称结尾,理由在于将它(Client Endpoint)与服务端(Service Endpoint)区分开来。事实上这个contract只对Client有效,当然要保证应用程序的正确,这两边必须一致就对了,那么IScientificCalculator 需要在ServiceContract上添加这个属性,而ISimpleCalculator却不要。

MSDN:[ConfigurationName]

The name used to locate the service element in an application configuration file. The default is the name of the service implementation class.

由于这个ConfigurationName的默认值将会是这个服务的实现类的类名,因此如果去掉此项,将会得到以下错误:

Unhandled Exception: System.InvalidOperationException: Could not find default endpoint element that references contract 'ConsoleApplicationClient.ServiceReference.IScientificCalculator' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.

您一定疑惑在app.config里面您确实可以找到它的踪迹,可是它去哪里了呢?让我们看下面一个替换或许您能体会更多。

让我们将配置向前回退一段。

1、将[ServiceContract(ConfigurationName="ServiceReference.IScientificCalculatorVolnet")]替换为[ServiceContract]

2、将app.config中的contract="ServiceReference.IScientificCalculatorVolnet" 替换为contract="ConsoleApplicationClient.ServiceReference.IScientificCalculator"

再次运行程序,是不是OK了?假设我们不去深究这个ConsoleApplicationClient的位置究竟是什么内容,但我们知道它一定不是ConsoleApplicationClient,而是由另外一个默认值将其“全局化”定义为了其他,使整个Endpoint不在我们的可用范围内。因此将会报错。

 

当然如果您要运行以上步骤,您还得接着下面的步骤:

接口的实现:

移除旧有的ScientificCalculatorClient

添加以下代码:

public partial class SimpleCalculatorClient : System.ServiceModel.ClientBase<ConsoleApplicationClient.ServiceReference.IScientificCalculator>, ISimpleCalculator
{

    #region ISimpleCalculator Members

    //Other Members

    public int Add(int arg1, int arg2)
    {
        return base.Channel.Add(arg1, arg2);
    }

    #endregion
}

public partial class ScientificCalculatorClient : SimpleCalculatorClient, ConsoleApplicationClient.ServiceReference.IScientificCalculator
{
    #region IScientificCalculator Members

    public int Multiply(int arg1, int arg2)
    {
        return base.Channel.Multiply(arg1, arg2);
    }

    #endregion
}

值得一提的是利用SimpleCalculatorClient实现ClientBase<inteface i>,然后ScientificCalculatorClient通过继承SimpleCalculatorClient来完成既有功能的继承,以保证意思表达的正确性,也避免了重复定义所带来的破坏旧有继承体系结构的错误。(如果两个都继承ClientBase<inteface i>,则两个类都需要实现其内部方法,而我们原有的逻辑则是让ScientificCalculator继承自SimpleCalculator,并且这时候你得重复编写更多的代码。)

这样,我们的Client就构筑完了,下面的代码将帮助我们测试我们的劳动成果:

class Program
{
    static void Main(string[] args)
    {
        ScientificCalculatorClient calculator = new ScientificCalculatorClient();
        SimpleCalculatorClient simCalculator = new SimpleCalculatorClient();

        UseSimpleCalculator(simCalculator);
        UseSimpleCalculator(calculator);
        UseScientificCalculator(calculator);

        simCalculator.Close();
        calculator.Close();
    }

    static void UseSimpleCalculator(ISimpleCalculator calculator)
    {
        Console.WriteLine("SimpleCalculator Add : {0}", calculator.Add(1, 2));
    }

    static void UseScientificCalculator(IScientificCalculator calculator)
    {
        Console.WriteLine("ScientificCalculator Add : {0}", calculator.Add(3, 4));
        Console.WriteLine("ScientificCalculator Multiply : {0}", calculator.Multiply(5, 6));
    }
}
//SimpleCalculator Add : 3
//SimpleCalculator Add : 3
//ScientificCalculator Add : 7
//ScientificCalculator Multiply : 30


程序代码下载

posted on 2007-11-12 03:33  volnet(可以叫我大V)  阅读(1215)  评论(0编辑  收藏  举报

使用Live Messenger联系我
关闭