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

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.

Related

Does UTF-8 encoding have to be explicitly defined in a web service response?

I am supporting a C# (.asmx) web service built on .NET 3.5. It is a very, very simple service that returns, among a few other things, a street address. The scope of the application that provides the service was recently widened and now the DB behind it houses international addresses as well. This means Latin foreign language characters can be stored (accent marks, umlauts, etc) and consequently returned in a web service response.
When I test the service locally using soapUI's automatically generated requests and without adding any special headers or any other intstructive information, I see this element exactly as it's stored in the database - with its accent mark:
<CompositeStreet>140-146 RUE EUGÈNE DELACROIX</CompositeStreet>
However, when a connecting system calls the service the IT staff report that the response contains the question mark typically used as the replacement for a non-supported character:
<compositeStreet>140-146 RUE EUG?NE DELACROIX</compositeStreet>
I'm unsure whose issue this is and I'm concerned that the simplistic design of the service may mean that it falls on my shoulders to recode to somehow ensure any consumer is guaranteed to see the data in UTF-8 encoding. I've been under the impression that UTF-8 was the default encoding and nothing explicit was required. (Please save the discussion about upgrading to WCF for another thread - it's not in scope for this right now.)
Can you have a look at the structure of the service and tell me if there is actually something that needs to be done on my side? (Code drastically slimmed down for the sake of this discussion)
The ASMX page - basically the WebMethod accepts a request and returns the response:
namespace Company.IT.Network.LocationStore.LS_Services
{
using . . .;
[WebService(Namespace = "Company.IT.Network.LocationStore.LS_Services.ClliRecordService")]
public class ClliRecordService : System.Web.Services.WebService
{
public ClliRecordService() { }
[WebMethod(Description = "Lengthy description")]
public VZBServiceResponse ClliLookup_VzbSite(VZBServiceRequest sRequest)
{
VZBServiceResponse sResponse = new VZBServiceResponse(sRequest);
return sResponse;
}
}
And the serializable class that is a property of the VZBServiceResponse type which ends up in the XML response. You'll see all there is is the [Serializable] attribute and the setting of the order of the elements returned.
namespace Company.IT.Network.LocationStore.LS_Lib.BO
{
using ...;
[Serializable]
public class Address
{
private string _compositeStreet;
private string _buildingName;
[XmlElement(Order = 0)]
public string BuildingName
{
get { return _buildingName; }
set { _buildingName = value; }
}
[XmlElement(Order = 1)]
public string CompositeStreet
{
get { return _compositeStreet; }
set { _compositeStreet = value; }
}
}
}
There is really not much more to it than that. No formatting of the XML response through a memory stream (which I've seen in some posts) or specific handling through Serialize or Deserialize methods.
Are there recommendations to improve on this so that service consumers are guaranteed to be presented foreign language characters or is that really up to the calling system? Thank you for any guidance!

"using" of two different libraries with almost identical functions

I'm consuming a SOAP web service. The web service designates a separate service URL for each of its customers. I don't know why they do that. All their functions and parameters are technically the same. But if I want to write a program for the service I have to know for each company is it intended. That means for a company called "apple" i have to use the following using statement:
using DMDelivery.apple;
and for the other called "orange"
using DMDelivery.orange;
But I would like to my program to work for all of them and have the name of the company or the service reference point as a parameter.
Update: If I have to write a separate application for each customer then I would have to keep all of them updated with each other with every small change and that would be one heck of an inefficient job as the number of customers increase.
Can anyone think of a solution? I'll be grateful.
If you have a base contract (interface) for all your services you can use a kind of factory to instantiate your concrete service and only have a reference to your interface in your client code (calling code).
//service interface
public interface IFruitService{
void SomeOperation();
}
//apple service
public class AppleService : IFruitService{
public void SomeOperation(){
//implementation
}
}
Having for example a kind of factory class (you can put your using statements here)
public static class ServiceFactory{
public static IFruitService CreateService(string kind){
if(kind == "apple")
return new AppleService();
else if(kind == "orange")
return new OrangeService();
else
return null;
}
}
And in your calling code (you just add an using statement for the namespace containing your interface):
string fruitKind = //get it from configuration
IFruitService service = ServiceFactory.CreateService( fruitKind );
service.SomeOperation();
You can also use the Dependency Injection principle.
If everything is the same and it's only the endpoint address that is different, maybe you can try changing only that before invoking the web service methods.
MyWebServiceObject ws= new MyWebServiceObject();
ws.Endpoint.Address = new System.ServiceModel.EndpointAddress("http://www.blah.com/apple.asmx");
Use any one client in your implementation. ex. Apple
Write a message inspector and attach this into the out going point
In message inspector replace the name space of the type with appropriate client name space.
EX:
Before Message inspector :MyClinet.Apple.Type
After Message Inspector : MyClient.Orange.Type, if the Provider is Orange.

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.

How to force WebService reference to implement common interface?

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!

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.

Categories