【问题标题】:How do I return an interface from a WCF Service?如何从 WCF 服务返回接口?
【发布时间】:2013-12-05 19:42:49
【问题描述】:

假设我有一些接口:

public interface IFoo {
  IBar DoesStuff();
}
public interface IBar {
  string Thingo { get; }
}

我在整个代码库中都在使用此代码。 IFoo 进程需要移动到不同的系统上(x64 与 x32 的区别),这就是我们使用 WFC 的原因。我的 WCF 服务实现了这个接口。当我创建“服务引用”时,会创建代理存根,但会更改接口。

public interface IFoo {
   object DoesStuff();
}   

我尝试将 IBar/Bar 定义为 DataService 和 DataContract,没有区别。有没有办法使用我的界面生成代理代码?

我在想如果模拟对象可以生成我的接口对象进行测试,那么我不应该能够让服务也兑现它吗?还是做了一些愚蠢和错误的事情?

【问题讨论】:

    标签: wcf wcf-client


    【解决方案1】:

    IBar 必须是具体的和 DataContract。 WCF 不是关于分布式对象,而是一种传输数据并让服务处理该数据的方法。您不能在 WCF 中返回具有行为的对象。

    【讨论】:

    • 如果我很清楚有一个支持具体的 Bar 对象,它被定义为实现 IBar 的 DataContract,那么我不是你的第一个陈述。我在返回的对象中没有行为。它只包含属性获取器。我知道吸气剂是方法。你所描述的最终是我所追求的;在其他地方处理并返回包含数据的对象。数据是定义为接口。
    • 将 WCF 视为促进消息交换,而不是使对象或接口跨网络可用。 WCF 中的]DataContract] 内容允许您在类的属性和字段之间映射 到消息,反之亦然。您实际上并没有通过网络发送类(或实例),只是它映射到的消息。
    【解决方案2】:

    我不知道您是否还需要解决方案。下面是我要做的。这里的技巧不是像许多博客建议的那样使用标准的“添加服务引用”,而是使用 Channel Factory 编写自己的客户端代理。在这种情况下,您可以选择重用接口,但根据需要重新定义具体类。如果您需要,很乐意进一步详细说明。

    // Service Contract
    [ServiceContract(name="a", namespace="b")]
    public interface IFoo {
        Bar DoesStuff();
    }
    
    // Interface to share
    public interface IBar {
        string Thingo { get; }
    }
    
    // Server Implementation
    public class Bar : IBar
    {
        string Thingo { get; set; }
    }
    
    // Client Proxy reference IBar interface only but redefine concrete class Bar.
    public class Bar : IBar 
    {
        public string Thingo
        {
            get { return _thingo; }
            set { _thingo = value; }
        }
        string _thingo;
    }
    
    
    /// Sample channel factory implementation
    using System;
    using System.Configuration;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    
    public abstract partial class ServiceProxyBase<TServiceContract> : IServiceProxy
        where TServiceContract : class 
    {
        protected ServiceProxyBase()
            : this(null, null)
        {
        }
    
        protected ServiceProxyBase(string url, Binding binding)
        {
            var contractName = typeof(TServiceContract).Name;
            var urlConfiguration = string.Format("{0}_Url", contractName);
            var serviceUrl = url ?? ConfigurationManager.AppSettings.ValueOrDefault          (urlConfiguration, string.Empty, true);
            if (serviceUrl.IsNullOrEmptỵ̣())
            {
                throw new Exception(string.Format("Unable to read configuration '{0}'", urlConfiguration));
            }
    
            var serviceBinding = binding ?? new BasicHttpBinding();
            Factory = new ChannelFactory<TServiceContract>(serviceBinding);
    
            var serviceUri = new Uri(serviceUrl);
    
            var endPoint = new EndpointAddress(serviceUri);
    
            Channel = Factory.CreateChannel(endPoint);
        }
    
        public virtual void Abort()
        {
            isAborted = true;
        }
    
        public virtual void Close()
        {
            if (Channel != null)
            {
                ((IClientChannel)Channel).Close();
            }
    
            if (Factory != null)
            {
                Factory.Close();
            }
        }
    
        private ChannelFactory<TServiceContract> Factory { get; set; }
    
        protected TServiceContract Channel { get; set; }
    
        private bool isAborted = false;
    }
    
    
    public class FooServiceProxy : ServiceProxyBase<IFooServiceProxy>, IFooServiceProxy
    {
        public Task<Bar> DoesStuffAsync()
        {
            return Channel.DoesStuffAsync();
        }
    }
    
    [ServiceContract(name="a", namespace="b")] // The trick when redefine service contract
    public interface IFooServiceProxy
    {
        [OperationContract]
        Task<Bar> DoesStuffAsync();
    }
    

    【讨论】:

      【解决方案3】:

      尝试将 Web 服务视为跨平台。如果您返回接口,Java 客户端会如何处理您的接口?

      【讨论】:

      • 感谢您的意见!我真的只是想传递数据! ;) 我仍然认为应该允许以数据为中心的接口,并用适当的属性对其进行标记可以达到这一目的。
      • SOAP、WSDL、XSD、XML 没有接口的概念。如果你编写一个 DataContract,实际上你是在创建一个以数据为中心的接口,因为唯一会被传递的是数据。
      【解决方案4】:

      是的。在添加服务引用之前,您需要引用包含该接口的项目。然后接口将被重新使用。对于使用的任何自定义类也是如此 - 如果包含其定义的项目在添加服务引用之前被客户端项目引用,则 WCF 可以重用这些定义。

      您还需要转到“添加服务引用”对话框中的“高级”选项卡并勾选“在引用的程序集中重用类型”以使其工作。

      【讨论】:

      • 我确实在生成代理之前引用了该项目。它已经在我的代码库中使用了。
      • 编辑了我的帖子并提供了更多详细信息 - 你现在可以试试吗?
      • 感谢您的输入!!该选项已被选中。我也尝试了显式选项来告诉它要重用哪个特定程序集,但它仍然作为对象返回。
      【解决方案5】:

      另一种给猫剥皮的方法。 Dominic 使用 KnownType 属性完成了它。在下面查看他的博客。

      http://blogs.msdn.com/b/domgreen/archive/2009/04/13/wcf-using-interfaces-in-method-signatures.aspx

      【讨论】:

      • 请注意 link-only answers 是不鼓励的,所以答案应该是寻找解决方案的终点(而不是另一个参考中途停留,随着时间的推移往往会变得陈旧)。请考虑在此处添加独立的概要,并保留链接作为参考。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多