How to force WebService reference to implement common interface? - c#

I'm creating a study project using .net web services and I came across with this problem:
In order to provide for an opportunity to change the web server or even it's nature (it's the part of the task) I created an interface in a separate .dll that every possible (web)services must implement. Say,
public interface IDataAccess
{
// Group of methods which are used for login/logout
bool isUserRegistered(string username);
bool authorize(string username, string password);
//...
}
And I make the web service implement this interface:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Server : System.Web.Services.WebService, IDataAccess
{
//...
}
Then, in the client, I create a reference (namespace WebReference) to this service specifying to reuse type in all assemblies and try to do the following:
private IDataAccess webService = (IDataAccess)(new WebReference.Server());
but this assignment throws exception in runtime stating the convertion can't be done, and, indeed, in the Reference.cs (which is a part of what is created by adding reference to Web Service there is a redeclaration of Server class which doesn't declare IDataAccessImplementation:
public partial class Server : System.Web.Services.Protocols.SoapHttpClientProtocol {
//...
}
So, my question is how to make this reference implement that common interface IDataAccess without manually editting the file Reference.cs?

Firstly, you really don't need to implement the interface on the server side - that will do nothing for the generated code.
Next, note that the declaration is of a partial class. You can use that to your advantage.
All you need to do is create another C# file, which has:
public partial class Server : SoapHttpClientProtocol, IDataAccess {}
That's all you need (in the right namespace and with the right using directives). No code - that's all provided in the generated class. The C# compiler will blend the two declarations, and then you can just use:
private IDataAccess webService = new WebReference.Server();
... or better yet, inject it via a constructor so that you can write tests which don't need to use the real implementation at all!

Related

Sharing WSDL types without XSD

I can't seem to find an example of generating proxies from WSDLs with shared types but without having any XSDs to go along with them. Can anyone please mark this as duplicate and point me to an example please?
Here are 2 services, each has its own namespace and a common type. The only thing that is publicly accessible are their WSDLs, there is no type's XSD or its .dll to pass to wsdl.exe /sharedtypes or svcutils and without it I end up with identical class Foo that I can't pass in to SetFoo and class Foo1.
The best I could come up with is generating proxies programmatically and detecting duplicates via CodeDOM, ignoring DataContract/WebServiceBinding namespaces, but it's a huge mess...
[WebService(Namespace = "http://tempuri.org/FOO1")]
public class Service1 : WebService
{
[WebMethod]
public Foo GetFoo()
{
return new Foo();
}
}
[WebService(Namespace = "http://tempuri.org/FOO2")]
public class Service2 : WebService
{
[WebMethod]
public void SetFoo(Foo foo)
{
}
}
public class Foo
{
public int Bar { get; set; }
}
There is a way of doing this, which is outlined here.
In your case you can skip the first step, generate the proxy from service 1 and then use the /r flag on svcutil to reference the service 1 proxy assembly when you generate your service 2 proxy.
This will ensure your service 2 proxy will use the same instance of Foo from your service 1 proxy.
However, have you considered just hosting a single service with two operations? It would save you a lot of work.
Edit: Also have a look at this post:
http://blogs.msdn.com/b/youssefm/archive/2009/10/09/reusing-types-in-referenced-assemblies-with-svcutil-s-r-switch.aspx
First off, you need to set the [DataContract(Namespace="some namespace here")] for all common service data types, otherwise when the WSDL and XSDs are generated then you will have objects from two difference namespace --- this is absolutely essential. The namespace value will only apply to the types defined in the XSD and not in the WSDL. XSD = data, WSDL = service.
The XSDs and WSDL and generated if, and only if, you have the META service behavior set - add this behavior and then you can navigate to the URL. The URL of the META service behavior will then have a link to your WSDLs and XSDs.
I use the following piece of code to self-host services in windows services rather than through IIS, however the same principals apply....
/// <summary>
/// Enables meta data output for a service host.
/// </summary>
/// <param name="host">The service host.</param>
/// <remarks>Must be invoked prior to starting the service host.</remarks>
public static void SetupMetaDataBehaviour(ServiceHost host)
{
ServiceMetadataBehavior metaDataBehaviour = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metaDataBehaviour == null)
{
metaDataBehaviour = new ServiceMetadataBehavior();
metaDataBehaviour.HttpGetEnabled = true;
host.Description.Behaviors.Add(metaDataBehaviour);
}
else
{
metaDataBehaviour.HttpGetEnabled = true;
}
}
after adding your two web references:
double click on the second web service reference
in the object browser navigate to the definition of Foo
right click on Foo and choose go to definition
delete the definition for the class Foo
add a using statement for the namespace of webservice one
find and replace all instances of <namespace-of-service-reference-2>.Foo with just Foo
This should fix your problem as it forces the autogenerated code for both service references to use the same class declaration.

Why doesn't WCF work the same way as ASMX?

Not sure what I'm doing wrong but I have two services one is WCF and the other is a ASMX service.
I'm trying to call array of doubles the same way I did in my asmx version.
Here is an image of the two services:
I got a fix to being able to call that method but I would like to know why ArrayOfDouble isn't showing up the same way in my serviceref2 as my serviceref1?
This is the WCF version:
namespace WcfSum
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface SumListWCF
{
[OperationContract]
string CalculateSum(List<double> listDouble);
}
}
namespace WcfSum
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
public class SumList : SumListWCF
{
public string CalculateSum(List<double> listDouble)
{
return listDouble.Sum().ToString();
}
}
}
This is the ASMX version:
namespace CalculateWebServiceSum
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class SumList : System.Web.Services.WebService
{
[WebMethod]
public string CalculateSum(List<double> listDouble)
{
return listDouble.Sum().ToString();
//return listDouble.Select(n => (double)n).ToString();
}
}
}
Previous post was here: WCF array of doubles not called successfully
This provided the fix but doesn't explain why it doesn't operate the same way. Or if there was a way of getting it to act the same. Which makes me feel like im fundamentally missing something?
EDIT
P.s these are just running locally.
There is nothing in the SOAP or WSDL standards that specifies how something like a List<double> should be serialized. ASMX apparently invented a complexType in the XML schema to represent array of double.
WCF is much better than ASMX. When you use "Add Service Reference", you get to decide how to handle repeated elements like your array of doubles. You can choose for them to be treated as an array, as a List<T>, etc.
There would be negative value in restricting WCF to the limitations of ASMX, which is a legacy technology.
You are using a generic List<> parameter, which is not supported for asmx and wcf due to interoperability with languages that do not support generic collections. See also these questions.
This question mentions a generated ArrayOfInt, so the ArrayOf* type name may be a, uhm, generic solution to deal with generic types.

Transport classes by WCF

My goal is to load an external class in a running application environment (like a plugin model). Creating an instances of the class in an running environment is not the problem (the classes using an Interface). The problem is to get the class which must be available from a central WCF services.
Is it possible to transport an class or assembly to the client by using WCF?
Something like this:
[ServiceContract]
public interface ISourceData
{
[OperationContract]
xxx GetClassData { get; set; } // <-- here to get data the class to app can create an instances of this
}
I hope that you understand my situation. Thanks.
First of all, the attribute in your sample above must be OperationContract, not DataContract. The DataContract attribute is for the class that you want to return in GetClassData.
The problem in your situation is that on the client side the class itself is not replicated when you add the service reference, but a stub is generated for the properties that you define in your DataContract. So you get the data, but not the logic.
You could now create an assembly which defines the data classes to be exchanged and add them to both the service and the client, but as I understand your question, you want to dynamically load assemblies in the service and send these "implementations" to the client without the client actually having access to the DLL that implements the class. This may not be possible in an easy way.
EDIT
Re-reading your question I now understand that you do not want to "transfer an instance", but you want to transfer the class definition. One way would be to actually transfer the source code for the class and try to use Reflection.Emit to create a dynamic assembly. A sample of this can be found here.
Yes , you can .
and also you must to define the type of your class like ↓
[ServiceKnownType(typeof(xxx))]
public interface IService
I think you need the assembly on the client so you need to transfer the dll containing the assembly to the client, then have the client save it in a plugins directory for the app and have the app and load it from there.
Although I image that this is going to be a permissions nightmare to get the app to be able to use the dlls downloaded from the service.
You would mark up the classes used in your interface like this:
[ServiceContract]
public interface ISourceData
{
[OperationContract]
MyClass GetClassData();
}
[DataContract]
public class MyClass
{
[DataMember]
public string MyMember1 {get; set;} // included in transport
public int MyMember2 {get; set;} // not included
}

Is it possible to use generic DataContract's from the client end?

I know when you create a service you can create a generic DataContract:
[DataContract(Name = "Get{0}Request")
public sealed class GetItemRequest<T>
where T : class, new() { ... }
[DataContract(Name = "Get{0}Response")
public sealed class GetItemResponse<T>
where T : class, new() { ... }
[ServiceContract]
public void MyService : IMyService
{
[OperationContract]
GetItemResponse<Foo> GetItem(GetItemRequest<Foo> request);
}
This generates a GetFooRequest and GetFooResponse definition for my WSDL. Now, what I'm curious about is if it is possible to go in the other direction?
Is it possible to create a client that uses the Generic DataContracts and pass those to the server as a concrete object? I attempted this after adding a Service Reference and it didn't really work out so well. So this is more of me wondering if there is any way (even if it means not adding a Service Reference) to do this?
Ultimately, WCF is going to look at the contract class. If that is generated from WSDL/MEX it won't have this (since this isn't how it is expressed in the metadata) - but if your client has the code as above, then sure it should work fine.
If you add a library reference (i.e. a dll / project reference) to your DTO dll from the client, and ensure WCF has shared-assemblies enabled, it should work. If it still baulks, then cheat: use a service reference just to get the config data. Then delete the service reference but keep the configuration (those config files are a pain otherwise). Then it should locate the type from the library.

Web Service proxy class to implement interface

I am looking for a way to have the generated proxy class for a Web Reference (not WCF) implement a common interface in order to easily switch between web service access and "direct" access to our business layer in the client application, something like:
public IBusiness GetBusinessObject()
{
if (_mode = "remote")
return new BusinessWebService.Business(); // access through web service proxy class
else
return new Business(); // direct access
}
However, custom types (e.g. the CustomSerializableType in the examples below) aren't referenced in the generated proxy class. Instead new, identical types are generated, which makes it impossible for the proxy class to implement the interface.
Is there some way to make the generated proxy class reference these types, or am I going about this all wrong? Should I consider converting the web service to a WCF service instead?
Details
Our solution consists of these four projects:
A business library (contains business logic, accesses data store)
A common library (contains common functionality, including the CustomSerializableType)
A web service (acts as a proxy between remote clients and the business layer)
A windows application
Our client wants the windows application to be able to run in two different modes:
Local mode, where the application simply uses the business library directly to access data
Remote mode, where the application communicates with the web service to access data
In order to do this, we have created an interface, IBusiness, which is located in the common library and contains all business methods.
Interface
public interface IBusiness
{
CustomSerializableType DoSomeWork();
}
Business layer
public class Business : IBusiness
{
public CustomSerializableType DoSomeWork()
{
// access data store
}
}
Web service
public class WebServiceBusiness : IBusiness
{
private Business _business = new Business();
[WebMethod]
public CustomSerializableType DoSomeWork()
{
return _business.DoSomeWork();
}
}
Generated proxy class (a ton of code left out for readability)
public partial class Business
: System.Web.Services.Protocols.SoapHttpClientProtocol
{
public CustomSerializableType DoSomeWork()
{
// ...
}
public partial class CustomSerializableType {
// PROBLEM: this new type is referenced, instead of the
// type in the common library
}
}
Assuming that the default namespace for your client is "Client", and that your web reference is named "Proxy", then do the following;
In the root of your client project, create a folder named "Proxy".
In that folder, create a class named "Business".
Make that class public and partial, and have it implement your IBusiness interface
This way, you do not need to modify the Reference.cs. You should never modify Reference.cs, or any other file produced through code generation.
Note that this violates the principals of SOA by tightly binding your client to your service. At the very least, you should define those interfaces in a separate project, so that you are only sharing the "interface" project between the client and service.

Categories