我有以下项目结构:
我想要做的是将我的ClientServer库分发为一个包含所有API的包装器,客户端可以使用它来连接到API并提取信息。
由于API使用ClientServer库中定义的类型,我希望在ClientServer库中添加ServiceReference会理解API返回类型实际上是来自同一个库的,因为引用将是
我这样做的原因是我只需要在一个地方定义发送到服务器和从服务器发送的类,而且还提供一个“内置”机制,供客户端使用API而不需要任何知识。 如何连接和没有其他依赖性图书馆(例如专用模型库)。
以下是我希望它如何工作的基本示例:
ClientServer Library:
public class Person {
public string Name { get; set; }
public int Age { get; set; }
.....
}
[ServiceContract]
public interface IPeopleService {
[OperationContract]
public Person[] Find(Person person);
}
Api项目
public class PeopleService : ClientServerLibrary.IPeopleServer {
public ClientServerLibrary.Person Find(ClientServerLibrary.Person person) {
// implementation for finding people based on the input person criteria.
}
}
鉴于上面的示例,我想将PeopleService的引用添加到ClientServer库中 所以使用我的图书馆的人可以做一些事情:
PeopleServiceClient people = new PeopleServiceClient() // Generated from the service references
// Here "Person" needs to be of type ClientServerLibrary.Person
Person person = people.Find(new Person() { Name = "Gary" });
但目前正在重新生成所有类。 我已尝试勾选并解开“添加服务对话”中的所有选项,但其结果始终相同。
希望我已经很好地解释了我的意图? 感谢。
编辑:所有项目都在使用C#.NET v4,因此没有差异。
答案 0 :(得分:3)
正如here所述,WCF代理生成器(通过svcutil或Add Service Reference)不会查看当前程序集。
您必须将数据合同和服务接口分成另一个库,如下所示:
因此,在尊重您的标准“[without]专用模型库”时,似乎没有简单的方法可以做到这一点。
如果您的唯一目标是能够提供包含所有消费者需求的一个 DLL文件,请查看ILMerge以组合这些程序集。
答案 1 :(得分:2)
好的,最后为了完全满足我的所有要求,我基本上不得不放弃使用自动化工具的想法,并自己手工制作每个客户。
这并不像听起来那么难。
我基本上创建了一个抽象的通用基类ServiceClient,其中T是ServiceContract接口(我已经定义过)
ServiceClient的工作类似于VS和CLI工具(svcutil,Etc)生成的工作。它通过ServiceChannel属性向派生类公开了“Channel”。这可以通过以下方式实现:
// T here is the ServiceContract as configured by the derived class.
ChannelFactory<T>.CreateChannel(binding, endpoint);
我现在可以按如下方式创建客户端:
class PeopleServiceClient : ServiceClient<IPeopleService>, IPeopleService {
public Person[] Find(Person person) {
// Delegate all responsability to the channel (which is connected to the API)
return base.ServiceChannel.Find(person);
}
}
因为所有内容都与IPeopleService接口相关联,所以我可以保证每个方法都已实现,或者至少在编译时会注意到更改。
我现在可以像这样使用客户端:
using(var client = new PeopleServiceClient()) {
Person[] people = client.Find(new Person() { Name = "Gary" });
}
所以回答我的问题:
是的,您可以拥有一个包含模型,合同和客户的全方位图书馆;您只需自己编写代码
因此,如果您不介意编写更多的类而不依赖于VS生成的代码,那么我认为这种方法是可以接受的。
如果您发现它有用,请在ServiceClient类的初稿之下( WILL 是错误和无法预料的问题,因此请仅用于学习目的):
本软件按“原样”提供,并且作者不对本软件作出任何保证,包括对适销性和适用性的所有暗示保证。在任何情况下,作者均不对由于使用,数据或利润损失而导致的任何特殊,直接,间接或间接损害或任何损害负责,无论是在合同,疏忽或其他正常行动中,是由于与本软件的使用或性能有关。
/// <summary>
/// Abstract base class for Service Clients wishing to utilise an API
/// via some service contract channel.
/// </summary>
public abstract class ServiceClient<T> : IDisposable where T : class {
/// <summary>
/// Creates and configures the ServiceClient and opens the connection
/// to the API via its Channel.
/// </summary>
protected ServiceClient(EndpointAddress endpoint, Binding binding) {
this.ServiceChannel = ChannelFactory<T>.CreateChannel(binding, endpoint);
(this.ServiceChannel as ICommunicationObject).Open();
}
/// <summary>
/// Closes the client connection.
/// </summary>
public void Close() {
(this.ServiceChannel as ICommunicationObject).Close();
}
/// <summary>
/// Releases held resources.
/// </summary>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Closes the client connection and releases any additional resources.
/// </summary>
protected virtual void Dispose(bool disposing) {
if (disposed) return;
if (disposing) {
if (this.ServiceChannel != null) {
this.Close();
this.ServiceChannel = null;
this.disposed = true;
}
}
}
/// <summary>
/// Provides a derived class access to the API via a dedicated channel.
/// </summary>
protected T ServiceChannel { get; private set; }
/// <summary>
/// Indicates if this instance has been disposed.
/// </summary>
private bool disposed = false;
}