6

我正在尝试实现一个服务合同,其中包含一个采用通用接口的方法,并且该通用接口本身被赋予了一个接口参数。我用 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。

4

4 回答 4

12

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

显然,使用接口作为泛型类型参数对于 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 接口可能比它的价值更麻烦。

于 2010-07-26T19:19:44.110 回答
2

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

不幸的是,XML 模式既不知道接口也不知道泛型,所以不——你不能一般地序列化那些——你需要使用具体类型。

于 2010-07-26T17:25:58.550 回答
1

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

于 2010-07-26T18:22:20.043 回答
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" };
   }
}

对上述服务设置服务引用后,可以运行此代码:

ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();

ServiceReference1.FooOfstring temp = client.GetData();

MessageBox.Show(temp.Value);

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

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

[System.Runtime.Serialization.DataMemberAttribute()]
public string Value 
{ 
   get {...} 
   set {...} 
}
于 2010-07-27T13:50:20.343 回答