I've been working with some form processing providers and they seem to have a generic receiver for key-value pair data. I'm attempting to do this but every data-structure i've tried to use implements an interface and therefore cannot serialize the container for use in the webmethod.
I've even tried using the base 'object' data type - with no success
[WebMethod]
public void processResponse( object lead ){
Dictionary<string, string> DList = (Dictionary<string,string>) lead;
How can I receive an undefined number of key-value pairs using this webservice so i can perform business logic on the received data and intelligently route the data using a unified input method? Statically typed classes will not work in this instance as different types of leads have different numbers of fields/properties.
Switch to WCF, which by default uses the DataContractSerializer class capable of serializing key-value pairs like the Dictionary<T,K>.
The basic structure of your service will be exactly the same and using BasicHttpBinding you have almost the same communication protocol based on Http and Soap. Spend 30 minutes on a WCF tutorial and you'll barely want to go back to old *.asmxes.
What I ended up doing was add a generic handler. I copied the QueryString parameter into a Dictionary and worked with the values from there. It sets a central point of data-collection and I was able to apply my business logic from there -- seems to have suited the purpose of what I wanted.
Related
I have an WCF service which contains a method that returns a Dictionary.
The generated method in the proxy class returns ArrayOfKeyValueOfstringstringKeyValueOfstringstring array.
how can i use this method ?
The contract method :
[OperationContract]
Dictionary<string, string> GetESGKeywordQuestion();
In the proxy class i have
ArrayOfKeyValueOfstringstringKeyValueOfstringstring[] GetESGKeywordQuestion()
{
// code
}
The accepted answer in another similar thread reference to a link telling you how to have Dictionary at the client side
in .NET to .NET communication. In other words, poor interoperability if you want your service to be consumed by non-dot-net applications like PHP or Java etc. If you had learned/remember data structure in CS, you know dictionary is implemented through non-linear structure like B-tree, so dictionary is not likely to be included in WSDL because of the complexity and variant. The ArrayOfKeyValueOfstringstringKeyValueOfstringstring structure generated for client is well designed and intended. If you want to see consistency on both side and want interoperability, then don't use Dictionary, instead, use a linear structure such as an array of KeyValuePair.
the generated proxy class seems to be wrong..
Try changing it to return the required Dictionary manually..
Dictionary<string, string> GetESGKeywordQuestion()
{
//code
}
And this should allow you to use you GetESGKeywordQuestion() method from your WCF service
i am new but reading some article on wcf i came to know that if you change the ServcieContrcat then you have to change not only the Service end but Clients end too and it's really difficult to manage.
Example 1:
Developer have to create WCF service for Order processing, with following function: GetOrderById, GetOrdersByStatus, SaveOrder
The ServiceContract could looks like following
[ServiceContract]
public interface IOrderService
{
[OperationContract]
Order GetOrderById(int orderId);
[OperationContract]
List<Order> GetOrdersByStatus(OrderStatus orderStatus);
[OperationContract]
void SaveOrder(Order order)
}
after month, for example, project manager say: Ok, our customers need another functions: DeleteOrderById, GetOrdersByCustomerId and don't need GetOrdersByStatus any more, we need GetOrdersByStatusAndCustomerId
Developers have to update ServiceContrcat and update client. As you can see, any changes in the ServiceContrcat is really difficult
so i am looking for best guidance how to develop wcf service which will not create any problem if we extend the functionality or any kind of change but client end will not face any problem. thanks
I had the same problem, basically a perpetual set of changes to methods that would break the interfaces on the clients.
This is what I did: All of my 30+ functions (and the list grows and grows) took strings, ints and bytes() as data types as both input and output parameters, I created a master single endpoint and function that receives, as an input parameter, and sends back as the output, a single simple class. This class, called HostInterface, contains just two parameters, a string (which I use to encapsulate all my strings and ints) and a byte() array (into which I stuff all my binaries)
So, whether a client is calling something simple like Ping() with just one string param, or something complicated like ResumeDownload() with 5 strings, 2 ints and a byte array, all of those parameters get encapsulated in my HostInterface class, the strings and ints into the one String parameters (as XML) and the bytes into the byte field.
When a request is received on the host side here:
Function HostConnect(byval objInbound as HostInterface) as HostInterface
I unpack the string parameter in objInbound, changing the XML into an object, and I unpack the bytes and add them to the byte portion of the same object. Then I check the method name (ping or ResumeDownload) and process accordingly. The diagram below shows the basic idea - all functions operating through a single function that takes and sends the same simple classe as parameters. Thus, my interface never needs to change.
Suppose we start from scratch in Visual Studio 2010 and add a 'WCF Service Aplication'. We add this method and implementation:
// (in IService1.cs)
[OperationContract]
Dictionary<string, string> GetDictionary();
// (in Service1.svc.cs)
public Dictionary<string, string> GetDictionary()
{
return new Dictionary<string, string>(
StringComparer.InvariantCultureIgnoreCase);
}
Then we add a new 'Console Application' to the same solution, add a Service Reference to our service project (using all the default settings), and add this code to Main:
var c = new ServiceReference1.Service1Client();
var d = c.GetDictionary();
d.Add("key",string.Empty);
// Since this *should* be a case-insensitive dictionary,
// this add *should* fail
d.Add("KEY", string.Empty);
Console.WriteLine("Both Add()s succeeded :(");
Console.ReadKey();
We expect this code to fall over, because a case-insensitive dictionary would regard key and KEY as the same key, and so would throw on the second Add.
Unfortunately, when everything is compiled and run, we get a sad face :(, because when the Dictionary comes across the WCF layer it 'forgets' that it was made with a specific, non-default, Comparer, and instead acquires strings default equality comparer.
Is there an easy setting to change so that the Comparer property of the Dictionary will be preserved as it goes across the wire? Or must I create a custom class?
(I've seen XML serialization of a Dictionary with a custom IEqualityComparer but it didn't offer me much enlightenment. I've also seen this codeproject comment from 3 years ago, but it is my question, not an answer)
If you use the standard mechanism like "Add Service Reference" etc., WCF by design will create a completely separate copy of your data structures based on the structure on the wire, e.g. the XML-serialized structure that can be expressed in XML schema (XSD).
This does not include anything that's more behavior (code) rather than actual data - things like comparers etc.
There is no setting to "turn" this on - it just cannot be done. The only way to solve this - when you control both ends of the communication wire, and both are .NET platforms - is to share that common stuff both sides need (service contracts, data contracts etc.) in a separate assembly that both the server as well as the client reference. On the client, you need to make sure to add the reference to that shared assembly before creating the WCF proxy - in that case, the WCF runtime will reuse the existing data structures (like your dictionary with a custom comparer) from the shared assembly, instead of creating new, boiler-plate copies.
I am having some trouble designing my WCF service. Bassically I need the service to recieve an XML document. The xml maps to a class that was generated from xsd.exe. I was originally just had this:
public void AddDocument(string xmlString)
Then I would deserialize the xml into the generated class. I was told this is a bad idea because I am doing extra work since wcf will do the serialization for me if I just use the document class as a parameter like this:
public void AddDocument(MyGeneratedClass document)
I'm new to WCF but if I do it this way I thought I would have to create a datacontract for MyGeneratedClass. The generated class is 20,000+ lines so this would take forever.
Do I need a DataContract? Anyway I think I am missing something so I hope this makes sense and if anyone can point me in the right direction I would greatly appreciate it. Thanks!
I would use simple types if your method only requires one or two parameters, and will return only a single simple type value.
As a general rule:
If you need to pass in more than just a few (less than 5) simple types - use some kind of a Request object, otherwise your call gets unwieldy.
If you need to return more than one single simple type value, use a Response object to bundle up those values.
I would try to avoid sending and receiving XML and parse it - try to send back and forth real well structured (data) objects - much easier to deal with and type-safe and all !
I have the same classes on my server and on my web service.
I have the following WebMethod:
[WebMethod]
public int CreateOrder(List<Purchase> p, string username)
{
o.Add(new Order(p,username));
return o.Count;
}
However the following code, run at server:
protected void CartRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
List<Purchase> l = ((List<Purchase>)Session["Cart"]);
if (e.CommandName == "Order")
{
localhost.ValidateService WS = new localhost.ValidateService();
WS.CreateOrder(l, Session["username"].ToString());
}
}
gives the following error: Argument '1': cannot convert from 'System.Collections.Generic.List<Purchase>' to 'localhost.Purchase[]'.
How can I transfer the list<Purchase> object to the web service?
When using web services like that, by default List<T> gets converted into an array (T[]). Convert your list into an array by doing .ToArray() before passing it to the method.
Another option is to change the web service code generation settings to use lists instead of arrays.
It seems you also have duplicate classes, both a local one called Purchase and the one that's generated over the web service, also called Purchase. Even though they have the same name, they're two different types (their namespaces are different). You'll either have to stick to one set of types, or use something like Automapper to map between your two sets of types.
If you're using svcutil to generate the client proxy classes, you can use the collectionType option to force the proxies to use a type other than the default array. This is certainly what gets used for generating proxies to WCF services; I'm not 100% sure if it's used with ASMX services.
Anyway, this is achieved by doing:
svcutil.exe /collectionType:System.Collections.Generic.List`1 [service url]
It is because the webservice uses SOAP to transfer the data, which is an XML protocol.
It knows nothing about .NET lists or many other fancy objects.
So in your case, it is actually transferring an array, and as Matti already said the solution is then simply to use an Array instead.
You can't serialize List<T> into xml, the <T> bit will obviously turn into a badly formed xml tag.
You could make a new object that inherits from List<T>, which will then serialize nicely and go through your web service, this is a minefield of best practice no-nos but you need to compromise sometimes.
localohost.ValidateService is a proxy class, with his own namespaces for classes: then "Order" is not the same as "localhost.Order"
if your calling web service from an other method in ther same web service class,
try this:
tihs.CreateOrder(l, Session["username"].ToString());