【问题标题】:WCF: Is serialization of a generic interfaces possible?WCF:通用接口的序列化可能吗?
【发布时间】:2011-03-21 05:15:26
【问题描述】:

我正在尝试实现一个服务合同,其中包含一个采用通用接口的方法,并且该通用接口本身被赋予了一个接口参数。我用 ServiceKnownType 修饰了服务接口,我用常规 KnownType 修饰了服务实现,我用常规 KnownType 修饰了 datacontract 实现:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallbacks))]
[ServiceKnownType(typeof(Batch<object>))]
[ServiceKnownType(typeof(Command))]
public interface IActions
{
    [OperationContract]
    IResponse TakeAction(IBatch<ICommand> commands);
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
[KnownType(typeof(Batch<object>))]
[KnownType(typeof(Command))]
internal class Actions : IActions
{
}

[DataContract]
[KnownType(typeof(Command))]
public class Batch<T> : IBatch<T>
{
}

为了记录,我在那里有 Batch,因为您似乎只能为泛型类型表达 knowntype 一次——它似乎发出 BatchOfanyType,但我不确定如何处理。

我得到的例外是“将任何静态未知的类型添加到已知类型列表中 - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给 DataContractSerializer 的已知类型列表中。”

有什么明显的我做错了吗?只是不支持接口的通用接口吗?作为记录,我为这个项目使用 C# 2.0 和 .NET 3.0。

【问题讨论】:

    标签: c# wcf generics serialization serviceknowntype


    【解决方案1】:

    泛型可以序列化,但有一定的限制。例如,给定数据合约:

    [DataContract]
    public class Foo<T>
    {
         [DataMember]
         public T Value { get; set; }
    }
    

    以及服务合同:

    [ServiceContract]
    public interface IService1
    {
         [OperationContract]
         Foo<String> GetData();
    }
    

    以及服务实现:

    public class Service1 : IService1
    {
       public Foo<string> GetData()
       {
           return new Foo<string>() { Value = "My test string" };
       }
    }
    

    对上述服务设置Service Reference后,就可以运行这段代码了:

    ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
    
    ServiceReference1.FooOfstring temp = client.GetData();
    
    MessageBox.Show(temp.Value);
    

    并显示带有“我的测试字符串”的消息框。

    请注意,服务本身不是通用的,但使用的数据契约是通用的。此外,在客户端生成的数据契约不是通用的,而是具有字符串类型属性值的“扁平化”类:

    [System.Runtime.Serialization.DataMemberAttribute()]
    public string Value 
    { 
       get {...} 
       set {...} 
    }
    

    【讨论】:

      【解决方案2】:

      如果你真的想的话,你可以在服务契约定义中使用接口,只要你在做的时候包括了已知的类型(稍作调整,见下文)。

      显然,使用接口作为泛型类型参数对于 C# 3.0 来说太过分了。我将已知类型属性更改为

      [ServiceKnownType(typeof(Batch<Command>))]
      public interface IActions
      {
      }
      

      在一定程度上,这使它起作用。序列化和反序列化本身可以工作,但是你会遇到这个异常:

      无法转换类型为“Batch`1[Command]”的对象 输入“IBatch`1[ICommand]”。

      要使该转换起作用,您需要对泛型类型协变的语言支持,这是在 C# 4.0 中引入的。不过,要让它在 C# 4.0 中工作,您需要添加一个方差修饰符:

      public interface IBatch<out T>
      {
      }
      

      然后它完美地工作......不幸的是你没有使用 C# 4.0。

      关于在服务合同中使用接口的最后一件事:如果您从它们生成服务引用,它将所有接口参数键入为object,因为原始接口类型不是元数据的一部分。您可以通过程序集引用共享契约,或者手动重构生成的代理来修复它,但总而言之,使用 WCF 接口可能比它的价值更麻烦。

      【讨论】:

      • 是的,当我想到 C# 4.0 中的协方差时,我在使用的平台上进行了编辑。哦,升级。
      【解决方案3】:

      您不能序列化接口。接口只定义合约,而不是对象。我猜这个的一个例外是 ISerializable 接口。

      【讨论】:

      • 当你“序列化ISerializable”时,你不是,真的。 ISerializable 是用于序列化其他东西的接口。
      • @约翰。你是对的。您仍然没有真正序列化接口,只是同意用于序列化实现 ISerializable 的对象的合同。为了完整起见,我只是添加了这个。
      【解决方案4】:

      WCF 是一个基于 SOA 消息的系统 - 它可以通过网络以可在 XML 模式中表示的序列化 XML 格式发送任何内容。

      不幸的是,XML 模式既不知道接口也不知道泛型,所以不知道——你不能对它们进行泛型序列化——你需要使用具体类型。

      【讨论】:

      • WCF 确实理解泛型足以序列化它们(我已经做到了)。您对接口的观点是正确的。
      • @RQDQ 你有地方展示吗?博客、CodeProject 文章或其他内容?喜欢看!
      • @RQDQ:实际上,这并不完全正确,imo。虽然接口本身无法序列化,但使用的具体类型可以。 “棘手”的部分是反序列化,因为它需要知道要实例化哪种具体类型。但是,序列化数据中包含足够的信息来做到这一点。
      • 我刚刚发布了一些通过电线序列化通用玻璃的代码。
      • @Thorarin - 你是对的。我刚刚设置了一个 repro(在框架 4.0 中授予),它以两种方式正确序列化接口。当然,VS 生成的客户端将操作公开为 Object 类型,而不是接口或基本类型。
      猜你喜欢
      • 1970-01-01
      • 2013-07-17
      • 1970-01-01
      • 1970-01-01
      • 2011-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-18
      相关资源
      最近更新 更多