Using ASMX Web Service Entities in WCF Service - c#

We have a good old .asmx web service (let's call it "Message" Web Service) which we have to preserve for backward compatibility.
The .asmx service exposes this method:
[WebMethod(Description = "Do Something")]
public int DoSomething(Entity1 e)
{
...
}
This web service uses some entities referenced from a DLL, for example:
namespace Software.Project.Entities
{
[DataContract]
public class Entity1
{
[DataMember]
public string property1{ get; set; }
// Lots of other properties...
}
}
This DLL is also used by a brand-new WCF service. Now, I have to call the old .asmx method from WCF. To do so, in the WCF project, I added a reference to the .asmx project, using the "Add service reference" wizard (Advanced - Add Web Reference).
Now, great! It is possible for me to call the DoSomething method from WCF, this way:
Entity1 e1 = new Entity1();
Software.Project.WCFService.ServiceReferenceName.Message m = new Software.Project.WCFService.ServiceReferenceName.Message();
m.Url = ConfigurationManager.AppSettings["MessageWebServiceURL"];
int r = m.DoSomething(e1);
Unfortunately, doing so won't work: I get a compiler error like if Entity1 in WCF is not good as argument for method DoSomething. What I have to do is:
Entity1 e2 = new Software.Project.WCFService.ServiceReferenceName.Entity1();
Software.Project.WCFService.ServiceReferenceName.Message m = new Software.Project.WCFService.ServiceReferenceName.Message();
m.Url = ConfigurationManager.AppSettings["MessageWebServiceURL"];
int r = m.DoSomething(e2);
By doing so, the compiler accepts the call; the problem is that Entity1 in my WCF service is full of fields and data and I would have to copy all the data to the new entity.
I also tried adding the reference to the .asmx as a Service Reference, and flagging "Reuse types in reference assembly", but the result was exactly the same.
I can't believe that there isn't a way to make it understand that Entity1 is exactly the same entity! Is that really impossible?

I am sorry, but i think I have bad news.
You can try to use xml serialization instead of data contract serialization, since asmx does not know about it.
Also, this post say this possible but not so easy: .NET 3.5 ASMX Web Service - Invoked via .NET 3.5 Service Reference - Common Class Reuse
Probably you'll find easier to add your translator class.

Related

How do you supply a type that's defined in a web service to another web service (share types)?

I've got 2 WCF services. Service A contains the definition of the type MyEntity. Service B contains a service reference to Service A and therefore can use the type for MyEntity. So I have a method that looks like this:
protected void Update (ServiceA.MyEntity entity)
{
//Do stuff
}
Now I want to use this method in Service A, so I added a service reference for Service B and tried:
protected UpdateServiceB(MyEntity entity)
{
using(ServiceB.ServiceClient client = new ServiceB.ServiceClient())
{
client.Update(entity);
}
}
This didn't work and complained that the types were not the same, even though Service B is using the type defined in Service A. How can I solve this?
UPDATE
I avoided the issue due to time constraints and passed the Guid of MyEntity from Service A to Service B instead. I then used an existing method called 'GetMyEntity(Guid entityId)' in Service A to retrieve the entity in Service B:
protected void Update (Guid entityId)
{
MyEntity entity = new MyEntity();
using (ServiceAClient client = new ServiceAClient())
{
entity = client.GetMyEntity(entityId);
}
//Do stuff
}
Sounds like you are using service references by way of Visual Studio's Add Service Reference command. While adequate, it can arguably become troublesome in medium to large projects due to:
Service types are re-defined in the client rather than using a common library (as you have discovered).
When a service contract changes, this will generally cause the service to be updated but not so in the client because of point 1. Client proxies become stale overtime as the schema evolves. You have to refresh reference
My best suggestion is not to use Add Service Reference and roll your client proxies by hand.
Once performed you will have additional libraries:
A.Contracts.dll - here you define all the service interfaces and data models for service A
B.Contracts.dll - here you define all the service interfaces and data models for service B
Common.Contracts.dll - If A & B have common types, place them here. (canonical data models)
A.Service.dll - service A implementation
B.Serivce.dll - service B implementation
ClientProxies.dll - roll-your-own client proxies for all your services
You don't have to implement all of the above now. All that is required is the manual ClientProxies.dll and you can always iterate to the rest later as required.
More
Tell me more about WCF the Right Way in my other SO answer

"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.

Get custom object via WCF in an Explorer-Extension isn't working but in Winform

I have a WCF Host with something like this:
[ServiceContract]
public interface IMountToOs
{
[OperationContract]
char GetMountDriveLetter();
[OperationContract]
MyTestClass MyTest();
}
public class MyTestClass
{
public string A { get; set; }
public string B { get; set; }
}
Client
private IMountToOs _proxy;
public IMountToOs Proxy
{
get
{
if (_proxy == null)
{
NetTcpBinding binding = new NetTcpBinding();
binding.MaxReceivedMessageSize = 2147483647;
binding.OpenTimeout = TimeSpan.FromMilliseconds(50000);
EndpointAddress address = new EndpointAddress("net.tcp://localhost:1234/MountToOsHost");
//_proxy = new MountToOsClient(binding, address);
ChannelFactory<IMountToOs> factory = new ChannelFactory<IMountToOs>(binding);
_proxy = factory.CreateChannel(address);
}
return _proxy;
}
}
While I can access
MessageBox.Show("Okay - " + Proxy.GetMountDriveLetter());
I can't call this method:
MessageBox.Show("Okay - " + Proxy.MyTest().A);
The complete extension is not working. But only while using it in an extension. Even if I insert a Messagebox in the first line of the extension it is not hit. I don't know why. It seems to run a pre-check and find the call of the custom class which is refused or so...
If I use a winform or so there is no problem.
.net 3.5
curious is that I have a break-point and a message of the hosts side. So I see that the method is not called
Update
now I moved the wcf-call in the Load Method of the extension and get a exception:
System.MissingMethodException: method not found:
"Contracts.Interfaces.MyTestClass
Contracts.Interfaces.IMountToOs.MyTest()".
My winform test and this extension use the same interface so that the method should known from both. no contract or so is outdated
According to what I found here and in the comments of the post: "For creating dynamic service proxy using client channel factory method, you will need datacontracts of the service. If you don't have datacontracts but you have the service URL, then you could use reflection to create proxy at runtime and call the service method."
Seems that the MyTestClass type is not known on the client side, so I think you could use reflection, or share the class between the client and server or much more simple, use the datacontract attribute.
Also, found something on MSDN that says something like this:
"When to use a proxy?
We create proxy using svcutil.exe. The output of this tool gives a proxy class and makes corresponding changes to the application configuration file. If you have a service that you know is going to be used by several applications or is generic enough to be used in several places, you'll want to continue using the generated proxy classes. We use proxy in WCF to be able to share the service contract and entities with the client. Proxies have several restrictions like they need to have gets and sets , contructors can't be exposed , methods other than the service contract cannot be exposed, repetition of code, everytime that we add/modify a service contract/data contract/message contract we need to re-generate the proxy for the client.
When to use ChannelFactory
The other option is using the ChannelFactory class to construct a channel between the client and the service without the need of a proxy . In some cases, you may have a service that is tightly bound to the client application. In such a case, it makes sense to reference the Interface DLL directly and use ChannelFactory to call your methods using that. One significant advantage of the ChannelFactory route is that it gives you access to methods that wouldn't otherwise be available if you used svcutil.exe..
When to use a ChannelFactory vs Proxy class?
A DLL is helpful if the client code is under you control and you'd like to share more than just the service contract with the client -- such as some utility methods associated with entities and make the client & the service code more tightly bound. If you know that your entities will not change much and the client code is less, then a DLL would work better than a proxy. If the client to your service is external to the system, such as API, it makes sense to use a proxy, because it makes sharing the contract easier by giving a code file rather than a DLL."
We cant see the class
MountToOsClient: IMountToOs
So we can only assume it is ok.
[DataContract] // Missing
public class MyTestClass
{
[DataMember] // Missing
public string A { get; set; }
[DataMember] // Missing
public string B { get; set; }
}
MountToOsClient can not expose Mytestclass without these attributes.

Class constructor (from C# web service) won't auto-implement properties in C# MVC

I'm running two instances of VS2010 on my local machine. One instance is running my Web Service (written in C#). The other instance is running my MVC web app (also C#). The MVC web app has a reference to the web service. I can successfully invoke web service methods from within the MVC app.
In my web service is a PageNavigation class:
// PageNavigation.cs
using System;
using System.Collections.Generic;
using System.Text;
public class PageNavigation
{
public string Page_Number { get; set; }
public string Page_Count { get; set; }
public PageNavigation()
{
Page_Number = "1";
Page_Count = "2";
}
}
By default, this should return an object with auto-implemented properties when I call the class constructor:
WebService.PageNavigation pageNavigation = new WebService.PageNavigation();
This works when constructing a PageNavigation object elsewhere in the web service.
pageNavigation.Page_Number
"1"
pageNavigation.Page_Count
"2"
However, running the same line of code on the MVC isn't giving the same result; the object's properties are all null values.
pageNavigation.Page_Number
null
pageNavigation.Page_Count
null
Is this the expected behavior? Is there a way to populate the properties with default values as intended? If more information is needed please let me know and I will update the question.
The service reference only sees the schema of your object, not business logic; in your case, your service reference just created a shell data type in the MVC application. When you create a service reference, it's actually creating another type with the same property names and types as the type defined in the service.
For your particular scenario (simply providing default property values and not more general business logic), you should be able to apply the [System.ComponentModel.DefaultValue] attribute to your properties in order for the class generator to recognize that these properties should be populated with a default value.
Incidentally, if the service reference were reusing existing types (if you had this type in a common library that was referenced both by the service and the application, for example), then your business logic would be intact.
An alternative would be to implement a factory pattern, whereby you call a function on the web service that instantiates (and optionally populates) the data object, then returns it to the client.
Yes, this is expected behaviour. The MVC site is not actually using your PageNavigation class. It is a simple copy (generated when you add the web service reference) containing of all the properties, but none of the methods, including the constructor.
You could work around this by refactoring your service so the entities are in a separate assembly and then you can reuse this assembly on the client as an option when you generate the proxy.
If you insist on using the same types between client and service, then on the "Advanced" tab of the "Add Service Reference" dialog, you can choose to reuse the types in your server assembly.
I would move that class out of the service and into a class library project referenced by the service and by the client.
And I wouldn't do this for such a small reason as default values. this violates SOA by coupling the service and the client. It will obviously not work for clients which are not running .NET.
What serializer are you using to deserialize the response from the server? Some of them (like the DataContractSerializer for example) do not call the default constructor.
The solution that you should use if you are in fact using DataContractSerializer is to use the OnDeserialized attribute like this:
using System.Runtime.Serialization;
public class PageNavigation
{
public string Page_Number { get; set; }
public string Page_Count { get; set; }
public PageNavigation()
{
Init();
}
[OnDeserialize]
void Init()
{
Page_Number = "1";
Page_Count = "2";
}
}

C# Web Service and Web Site sharing library, service returns different "type" of library object

I have a Web service and a Web site (both C#) in the same solution (For now); I also have a class library in the solution. Both the web service and the web site reference this class library.
The web service has a WebMethod that creates an object from the library and returns it. The website invokes this and attempts to put it into a Trainer object (once again, from the same library)
ProFitWebService.Service serviceConn = new ProFitWebService.Service();
ProFitLibrary.Trainer authenticatedTrainer = (ProFitLibrary.Trainer)serviceConn.GetAuthenticatedTrainer(_TrainerLogin.UserName);
however the following occurs: "Cannot convert type ProFitWebService.Trainer to ProFitLibrary.Trainer"
Here is the WebMethod:
[WebMethod]
public ProFitLibrary.Trainer GetAuthenticatedTrainer(string email)
{
ProFitLibrary.Trainer returnTrainer = new ProFitLibrary.Trainer();
SqlCommand cmd = new SqlCommand("SELECT * FROM Trainers WHERE EmailAddress = '" + email + "'", conn);
conn.Open();
SqlDataReader reader;
reader = cmd.ExecuteReader();
while (reader.Read())
{
returnTrainer.TrainerId = reader.GetInt32(reader.GetOrdinal("TrainerId"));
returnTrainer.FirstName = reader.GetString(reader.GetOrdinal("FirstName"));
returnTrainer.LastName = reader.GetString(reader.GetOrdinal("LastName"));
returnTrainer.PhoneNumber = reader.GetString(reader.GetOrdinal("PhoneNumber"));
returnTrainer.Address = reader.GetString(reader.GetOrdinal("Address"));
returnTrainer.City = reader.GetString(reader.GetOrdinal("City"));
returnTrainer.PostalCode = reader.GetString(reader.GetOrdinal("PostalCode"));
returnTrainer.EmailAddress = reader.GetString(reader.GetOrdinal("EmailAddress"));
}
return returnTrainer;
}
Update: Changing the Trainer object to ProFitWebService.Trainer on the Web site fixed the issue:
ProFitWebService.Service serviceConn = new ProFitWebService.Service();
ProFitWebService.Trainer authenticatedTrainer = (ProFitWebService.Trainer)serviceConn.GetAuthenticatedTrainer(_TrainerLogin.UserName);
I think the answer to this is simply that library objects returned from a Web Service will always be type based/prefixed on the service - and I should not reference the class Library from both the Website and the Service - I should just always create the WebService version of the object - ProFitWebService.Trainer etc.
Could someone confirm this as a standard practice when you're using libraries within a web service? or if I'm making this more difficult then it really is!
When creating the web reference to your web service you will get proxy classes generated for you. These proxy classes look like the classes from your library, but they are not the same types. You will need to have some method translating between the library version and the proxy versions of these types if you want to treat objects returned from the web service as types from your shared library.
This type of thing can happen when using reflection, when the actual assemblies are different - either different versions, compile-time or even sometimes when using a different copy of the assembly. I'm not sure if your code uses reflection or not though...
The problem here is that the client of the web service actually is a proxy, or generated object rather than the normal type you were expecting. I think you will have to map the type into the instance you want.
What I did is make extension methods for each class to convert them to the correct signature.
Not so much fun when you have 20 business classes that are shared between 6 web services :'(

Categories