Dictionary in wcf - c#

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

Related

Adding parameter to Restful service POST without breaking clients

I have a rest web service, which suppose has a method
int foo(Mytype x)
it has many clients, i wanted to add a parameter to to Mytype and change how foo behaves, As this is a restful service so all clients also implement there version of Mytype any ideas how to accomplish this without breaking any clients?
Have u considered versioning? E.g. server/ver2/foo
I think your only other option would be to implement the new functionality, such that it is not required, e.g.
foo (MyType myType) {
if (myType.newField exists) {
// do new stuff
}
}
One final note, i would recommend combining both approaches, which will ensure that foo is backwards compatible.
I think the safest way would be to go with versioning.
You could provide a new version for MyType which can be handled using the MIME type description in the Accept and Content-Type headers, i.e. like so:
application/json+foo;application,v=2
Another possibility would be to handle the version via the URI/URL, i.e.
https://api.yourhost.com/v2/MyType
Like Damo already said you then have to handle the use of the different resource representations on the server.
I added a int to my POST request and it didn't break anything. So looks like adding a parameter to rest service request class is not really "Breaking Changes" but removing one will certainly break the clients.

WCF returns ArrayOfKeyValueOfintstringKeyValueOfintstring[] instead of Dictionary<int, string> and Array instread of List

So I've spent the last several hours investigating this issue and it is clear that I am not the only one. Why are my Dictionaries and Lists being returned as Arrays?
I understand that Arrays are used as default for the sake of compatibility. WCF makes a conscious effort to distance itself from being .Net dependent. But both my Server and Client are developed in C# .Net so I'm okay.
Here is a sampling of similar questions on just StackOverflow alone:
WCF service returning array instead of List
Why does WCF return myObject[] instead of List like I was
expecting?
WCF service returning an array of dictionary
WCF Proxy Returning Array instead of List EVEN THOUGH Collection
Type == Generic.List
WCF Returning Array instead of List EVEN THOUGH Collection Type ==
Generic.List
Why does my WCF service return and ARRAY instead of a List ?
Array instead of List in WCF Service Proxy Generated using
svcutil.exe
What I have set up:
I am generating the Proxy via this command:
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin>svcutil.exe /language:cs
/out:generatedProxy.cs /config:app.config /ct:System.Collections.Generic.List`1
http://192.168.0.99:9000/ProjectDatabase/??
My service contract looks like this:
[ServiceContract]
public interface IMyContract
{
[OperationContract]
[ServiceKnownType(typeof(Dictionary<int, string>))]
Dictionary<int, string> getClassDictionary();
}
My implementation:
public Dictionary <int, string> getClassDictionary()
{
Dictionary<int, string> myDict = new Dictionary<int, string>();
myDict.Add(1, "Geometry");
myDict.Add(2, "Algebra");
myDict.Add(3, "Graph Theory");
return myDict;
}
Even in my Reference.svcmap I have:
<CollectionMappings>
<CollectionMapping TypeName="System.Collections.Generic.List`1" Category="List" />
</CollectionMappings>
However, despite my best efforts and research I still get:
Dictionary<'int, string'> returning as ArrayOfKeyValueOfintstringKeyValueOfintstring[]
And:
List<'T'> returning as T[]
I feel like I've tried everything and done everything right, but I have to be missing something. So what is it? Thank you for your time, help, and consideration.
Update:
I've even attempted a route of working around the Array imposition, by writing a serializable struct and adding them to an array.
[Serializable]
public struct KeyValuePair<K, V>
{
public K Key { get; set; }
public V Value { get; set; }
}
However, when I return the KeyValuePair<int, string>[]. My proxy is generating a return of KeyValuePairOfintstring[].
Solution is posted below.
Well I found out what was causing the serialization to be so crude.
In my ServiceContract I had the following:
[OperationContract]
List<DataTable> ShowTables();
[OperationContract]
DataTable FetchContacts(string filter = "All");
[OperationContract]
DataTable FetchUsers();
[OperationContract]
DataTable FetchDrops();
After commenting this out and recompiling my WCF Service Library, I found that everything serialized/ deserialized appropriately when generating the proxy.
It seems that when svcutil.exe encounters something that it does not know how to serialize then all bets are off. Quite literally it will ignore your commands/settings and serialize everything as gibberish like ArrayOfKeyValueOfinttringKeyValueOfintstring. So if you receive this error, you should ask yourself if svcutil.exe is able to properly serialize all of what you are returning.
I hope the identification of the source of my issue will help others in the future.

ASP.Net Webservice receiving an undefined number of key-value pairs

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.

How do you get WCF serialization to preserve a non-default Comparer on a generic Dictionary?

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.

Can't transfer list<T> to web service?

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());

Categories

Resources