开发者

my first WCF server - works with "string" but doesn't work with custom interface?

I've implemented my first WCF application. I need one method IConsoleData GetData(); I receive CommunicationException "There was an error reading from the pipe: The channel was closed. (109, 0x6d)." in client.

When I replaced IConsoleData GetData(); to string GetData(); application become functionable.

How should I fix the code to use IConsoleData GetData()?

Server:

//public interface IConsoleData
//{
//    double GetCurrentIndicator();
//}

//public class IConsoleDataImpl : IConsoleData
//{
//    public double GetCurrentIndicator()
//    {
//        return 22;
//    }
//}

[ServiceContract]
public interface IMBClientConsole
{
    [OperationContract]
    //IConsoleData GetData();
    string GetData();
}

public class MBClientConsole : IMBClientConsole
{
    //public IConsoleData GetData()
    //{
    //    return new IConsoleDataImpl();
    //}
    public string GetData()
    {
        //return new IConsoleDataImpl();
        return "hello";
    }
}

class Log
{

    private ServiceHost _host;

    public void initialize()
    {
        _host = new ServiceHost(typeof (MBClientConsole),
                                new Uri[]
                                    {
                                        new Uri("net.pipe://localhost")
                                    });
            _host.A开发者_运维技巧ddServiceEndpoint(typeof(IMBClientConsole),
              new NetNamedPipeBinding(),
              "PipeReverse");

            _host.Open();
            System.Threading.Thread.Sleep(1000000);
            // TODO: host.Close();
    }
}

Client:

//public interface IConsoleData
//{
//    double GetCurrentIndicator();
//}

[ServiceContract]
public interface IMBClientConsole
{
    [OperationContract]
    //IConsoleData GetData();
    string GetData();
}

class Program
{
    static void Main(string[] args)
    {
        ChannelFactory<IMBClientConsole> pipeFactory =
            new ChannelFactory<IMBClientConsole>(
                new NetNamedPipeBinding(),
                new EndpointAddress(
                    "net.pipe://localhost/PipeReverse"));

        IMBClientConsole pipeProxy =
          pipeFactory.CreateChannel();

        while (true)
        {
            string str = Console.ReadLine();
            Console.WriteLine("pipe: " +
              //pipeProxy.GetData().GetCurrentIndicator());
              pipeProxy.GetData());
        }
    }
}


if you use an Interface you have to do two things:

  1. the implementations needs to be serializable (use the DataContract - Attribute)
  2. you have to tell the service the known types if you use the interface (here it's IConsoleDataImpl)

Make your live easier and decorate the implementation with DataContract and the members of it with DataMember Attributes and use the implementation instead of the interface.

You can find a lot about this here

(and don't begin the implementations name with "I")

here is a rework without the known-type stuff and I think this will help you for your first steps - no need to dig in that deep (Interfaces + KnownTypes) yet.

[DataContract]
public class ConsoleData
{
    [DataMember]
    public double CurrentIndicator
    {
        get { return 22; }
        set { /* whatever */ }
    }
}

[ServiceContract]
public interface IMBClientConsole
{
    [OperationContract]
    ConsoleData GetData();
}


You have a couple problems there. First, your interface should be marked as a [DataContract]

[DataContract]
public interface IConsoleData
{
    double GetCurrentIndicator();
}

Now, when WCF sends an IConsoleData over the wire, it is going to serialize the data in the class, send it, and deserialize it on the client. The problem with your implementation is that it contains nothing that can be serialized.

public class IConsoleDataImpl : IConsoleData
{
    public double GetCurrentIndicator()
    {
        return 22;
    }
}

If you were to build a client using svcutil.exe from the above, it would create the IConsoleDataImpl class, but the GetCurrentIndicator method wouldn't do anything. Important distinction here: WCF will transmit DATA, not IMPLEMENTATION.

What you probably want here is something more like:

[DataContract]
public interface IConsoleData
{
    [DataMember]
    double CurrentIndicator { get; set; }
}

public class ConsoleDataImpl : IConsoleData
{
    public double CurrentIndicator { get; set; }
}


[ServiceContract]
[KnownType(typeof(ConsoleDataImpl))]
public interface IMBClientConsole
{
    [OperationContract]
    IConsoleData GetData();
}

public class MBClientConsole : IMBClientConsole
{
    public IConsoleData GetData()
    {
        return new IConsoleDataImpl() { CurrentIndicator = 22 };
    }
}

Although at this point the IConsoleData interface isn't really needed, and I would just remove it.

But basically the thing to remember is in general, you want your WCF services to contain methods, and your data contracts to contain properties (or fields). The implementation inside a method in a DataContract will not be in the client if you generate a client from he WSDL, that would only work if you were to copy over a shared dll with the DataContracts to the client.

Hope that makes sense...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜