【问题标题】:WCF service creating multiple internal objects?WCF服务创建多个内部对象?
【发布时间】:2019-02-19 16:11:28
【问题描述】:

我正在自托管 WCF 服务,并希望我的服务对象是单例的。我的客户使用通道来访问服务。主机和客户端是控制台应用程序,如下所列。

我的服务有一个方法可以返回一个内部对象的接口。该内部对象包含获取/设置数字的方法。我的服务有替代方法来获取/设置该内部对象的编号。

我的问题是通过服务返回的接口设置内部对象的编号并使用服务方法(SetInternalNumber)设置它似乎没有设置相同的对象。当我检索这些值时,它们是不同的。

我的客户端应用程序显示内部对象的初始编号,更改该编号,获取对象的另一个接口及其初始值,通过服务设置值,以及不同的列表价值观。

我认为创建了我的内部对象的多个实例,但事实并非如此。我知道这一点,因为我在对象构造函数中打印了一行,而我只看到该行一次。

发生了什么事?我认为可能必须将 Internal 类标记为单例 (InstanceContextMde.Single),但这不起作用。

服务接口

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    [ServiceKnownType(typeof(Internal))]
    IInternal GetMyInterface();

    [OperationContract]
    int GetInternalNumber();

    [OperationContract]
    void SetInternalNumber(int number);
}

服务实现

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MyService : IMyService
{
    private IInternal _internal;

    public MyService()
    {
        Random rng = new Random();
        _internal = new Internal(rng.Next());
    }

    public int GetInternalNumber()
    {
        return _internal.GetNumber();
    }

    public IInternal GetMyInterface()
    {
        return _internal;
    }

    public void SetInternalNumber(int number)
    {
        _internal.SetNumber(number);
    }
}

内部对象接口

[ServiceContract]
public interface IInternal
{
    [OperationContract]
    int GetNumber();

    [OperationContract]
    void SetNumber(int number);
}

内部对象实现

[DataContract]
public class Internal : IInternal
{
    [DataMember]
    private int _number;

    public Internal(int number)
    {
        _number = number;
        Console.WriteLine($"creating Internal - {_number}");
    }

    public int GetNumber()
    {
        return _number;
    }

    public void SetNumber(int number)
    {
        _number = number;
    }
}

自托管应用

    static void Main(string[] args)
    {
        Uri baseAddress = new Uri(@"http://localhost:12345/GettingStarted");            
        MyService singleton = new MyService();
        ServiceHost selfHost = new ServiceHost(singleton, baseAddress);

        try
        {
            selfHost.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "MyService");
            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            smb.HttpGetEnabled = true;
            selfHost.Description.Behaviors.Add(smb);

            selfHost.Open();

            Console.WriteLine("host is open");
            Console.ReadLine();

            selfHost.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"exception: {ex.Message}");
            Console.ReadLine();

            selfHost.Abort();
        }
    }

客户端应用

    static void Main(string[] args)
    {
        var myBinding = new BasicHttpBinding();
        var myEndpoint = new EndpointAddress(@"http://localhost:12345/GettingStarted/MyService");

        using (var myChannelFactory = new ChannelFactory<IMyService>(myBinding, myEndpoint))
        {
            IMyService service = null;

            try
            {
                service = myChannelFactory.CreateChannel();

                Console.WriteLine("channel created");

                // get interface to concrete object 
                IInternal myInternal = service.GetMyInterface();
                Console.WriteLine($"initial number = {myInternal.GetNumber()}");

                Console.ReadLine();

                // set a new # with Internal interface
                Console.WriteLine("set new number");
                myInternal.SetNumber(123);
                Console.WriteLine($"new number = {myInternal.GetNumber()}");

                Console.ReadLine();

                // get another interface, don't set its number
                Console.WriteLine("get another interface, initial value");
                IInternal anotherInternal = service.GetMyInterface();
                Console.WriteLine($"number(another) = {anotherInternal.GetNumber()}");

                Console.ReadLine();

                // set # with service
                Console.WriteLine("set # through server");
                service.SetInternalNumber(789);                    

                // what are the #s?
                Console.WriteLine();
                Console.WriteLine($"from Internal: {myInternal.GetNumber()}");                    
                Console.WriteLine($"from another Internal = {anotherInternal.GetNumber()}");
                Console.WriteLine($"from service = {service.GetInternalNumber()}");

                Console.ReadLine();

                ((ICommunicationObject)service).Close();
                myChannelFactory.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();

                (service as ICommunicationObject)?.Abort();
            }
        }
    }

【问题讨论】:

    标签: wcf service


    【解决方案1】:

    Wcf 是一个发送消息和接收消息的通信框架。

    您的方法 GetMyInterface 返回内部将首先被序列化为 xml,然后当客户端收到消息时,它将反序列化消息以重建对象。

    这意味着如果你的方法返回一个对象,它总是不是同一个对象,并且与你的服务内部无关,因为客户端从消息中重建它。 您可以进行如下测试

     Console.WriteLine($"are the two internal the same?" + (myInternal == anotherInternal ? "the same" : "not the same"));
    

    我的结果。

    由于client中的两个internal和你的service中的internal不一样,所以你的SetInternalNumber方法只会设置数字来分隔内部对象,互不影响。

    客户端内部只有服务内部的初始值,与它无关,彼此无关。这就是为什么您的两个内部客户端具有相同的初始编号。

    【讨论】:

    • 一方面这是有道理的,但另一方面却没有。没有意义的是 WCF 如何在客户端上创建一个有效的 Internal 对象。除了方法签名之外,它不应该知道有关 Internal 类的任何信息。客户端如何知道 Internal 是如何实现的?尽管我有问题,但我相信我的意图很明确:我想要调用服务器对象上的方法。我该如何做到这一点?我需要重新设计我的服务接口和/或类吗?
    • 虽然你返回了一个 IInternal 的接口,但客户端仍然会重建一个 Internal 类型的对象,你可以通过你的属性 [ServiceKnownType(typeof(Internal))] 找到它。一旦您返回您的 Internal ,它们与服务器端无关,并且将有自己的编号。您可以尝试代码 Console.WriteLine(myInternal is Internal ? "yes" : "no");看到结果。如果要保持状态,请不要返回接口,让服务端改变状态并返回状态
    • 我开始了解 WCF 正在做什么,但我仍然不明白它如何在客户端组合一个工作对象(内部)。它如何知道 Internal 的实现?似乎不应该。如果这是其他人的服务器,我(和 WCF)只需要使用一个接口即可。
    • 当您调用客户端代理的方法时,它会向服务器端发送消息,服务器端将反序列化消息以构建您需要传递给方法的参数对象(当然它做了很多其他事情。)当服务器端需要返回结果对象时,它也会返回消息,客户端将反序列化消息以构建返回的对象。
    • 代理部分是我所缺少的。我想我之所以错过它,部分原因是自托管和我的客户创建了自己的频道。我切换到使用配置、添加服务引用等,现在可以看到更多正在发生的事情。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多