Pass array of parameters to a web method - c#

I would like to pass some parameters to a web method as an array. The web method's signature does not have the params keyword.
I have a variable number of parameters (as the web method accepts) so I cannot put the array into n single variables.
How can this be done?

params is just syntactic sugar, why not just do something like this:
var myWebService = new MyWebService();
myWebService.MyMethod(new string[] { "one", "two", "three" });
The method signature on the web service side would just be:
public void MyMethod(string[] values);
If you post your web method maybe I can provide a better answer.
EDIT
If you can't modify the web method signature, then I would use an extension method to wrap the difficult to call web service. For example, if our web service proxy class looks like:
public class MyWebService
{
public bool MyMethod(string a1, string a2, string a3, string a4, string a5,
string a6, string a7, string a8, string a9, string a10)
{
//Do something
return false;
}
}
Then you could create an extension method that accepts the string array as params and makes the call to MyWebService.
public static class MyExtensionMethods
{
public static bool MyMethod(this MyWebService svc, params string[] a)
{
//The code below assumes you can pass in null if the parameter
//is not specified. If you have to pass in string.Empty or something
//similar then initialize all elements in the p array before doing
//the CopyTo
if(a.Length > 10)
throw new ArgumentException("Cannot pass more than 10 parameters.");
var p = new string[10];
a.CopyTo(p, 0);
return svc.MyMethod(p[0], p[1], p[2], p[3], p[4], p[5],
p[6], p[7], p[8], p[9]);
}
}
You could then call your web service using the extension method you created (just be sure to add a using statment for the namespace where you declared you extension method):
var svc = new MyWebService();
svc.MyMethod("this", "is", "a", "test");

Why don't you use..an array?
[WebMethod]
public string Foo(string[] values)
{
return string.Join(",", values);
}

Ignore the fact that it is a web method for a moment. Ultimately, it is a method. And like all methods, is either defined to take parameters or not. If it is not defined to take parameters, then you can't pass parameters to it, can you? Not unless you have access to the source code and are able to chance it's definition.
If it is defined to take parameters, then the question is, is it defined to take array parameters or not? If not, then you can't pass parameters to it (not unless you can change it so that it can.)

Related

Pass in array of certain size to test with xunit

I need help with writing unit test with xunit. I am trying to test a validation where array length cannot be greater than 20MB
[Theory]
[InlineData((arrayGreaterThan20MB())]
public void Fail_when_documentSize_is_greater(byte[] documentSize)
{
_getAccountFileViewModel.Content = documentSize;
}
Private Function
private static byte[] arrayGreaterThan20MB()
{
byte[] arr = new byte[5000000000];
return arr;
}
I am not sure what is the best way to test this. I am getting a error when I am trying to pass function in inline data.
Error "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type"
Just declare the array within the test itself, instead of trying to pass via inline data attribute
[Theory]
public void Fail_when_documentSize_is_greater() {
byte[] overSizedDocument = new byte[5000000000];
_getAccountFileViewModel.Content = overSizedDocument;
//...
}
You can not use the result of a method call as parameter of an attribute. That's what the error is telling you. You can only pass constants or literals. For example like this:
[Theory]
[InlineData("a", "b")]
public void InlineDataTest(string first, string second)
{
Assert.Equal("a", first);
Assert.Equal("b", second);
}
XUnit has some other attributes, that can help you here. E.g. there is the MemberData attribute, that allows you to specify a method name of a method that provides the test data. Note that it returns an IEnumerable of object array. Each object array will be used to call the test method once. The content of the object array are the parameters. For example:
[Theory]
[MemberData(nameof(DataGeneratorMethod))]
public void MemberDataTest(string first, string second)
{
Assert.Equal("a", first);
Assert.Equal("b", second);
}
public static IEnumerable<object[]> DataGeneratorMethod()
{
var result = new List<object[]>(); // each item of this list will cause a call to your test method
result.Add(new object[] {"a", "b"}); // "a" and "b" are parameters for one test method call
return result;
// or
// yield return new object[] {"a", "b"};
}
In the case you mentioned above, the simplest way would be just to call your method that creates the test data within your test method.
[Theory]
public void Fail_when_documentSize_is_greater()
{
_getAccountFileViewModel.Content = arrayGreaterThan20MB();
}
There is another attribute called ClassData that can use a data generator class.
More detailed information can be found on this blog post

System.Reflection.TargetParameterCountException when Parameter count can be dynamic

All methods in the "ProbabilitiesTheory" class accept dynamic count of parameters - it means that there can be put as many parameters as one wants. But .NET still says "System.Reflection.TargetParameterCountException" when invoking a method, that has "params" keyword in its parameters.
Here's the code:
internal static class ProbabilitiesTheory
{
static public double GetMediumValue(params double[] integers)
{ }
}
class Program
{
static void Main(string[] args)
{
MethodInfo[] methods = Type.GetType("ConsoleApplication1.ProbabilitiesTheory").GetMethods();
while (true)
{
Console.WriteLine("Write the name of the method\n");
string NameOfMethod = Console.ReadLine();
Console.WriteLine("Write the parameters of the method using the following format:
parameter1;parameter2;parameter3;parameterN\n");
string ParametersOfMethod = Console.ReadLine();
foreach (var i in methods)
{
if (i.Name == NameOfMethod)
{
object[] #parameters = (from t in ParametersOfMethod.Split(';') where t != "" select (object)Convert.ToDouble(t)).ToArray();
i.Invoke(null, #parameters); // Exception HERE
}
}
Console.WriteLine("______");
}
}
}
It is absolutely ok with LINQ expression there, i get what i need to get: object[] containing dynamic amount of double values.
How do i solve this problem?
As far as reflection is concerned, a params array is just an array with a fancy syntactical sugar. You could solve the immediate problem for most of your methods by adjusting your code like so:
double[] #parameters = (from t in ParametersOfMethod.Split(';') where t != "" select Convert.ToDouble(t)).ToArray();
i.Invoke(null, new[] { #parameters});
The gist of this is that a params array is just a single parameter at run-time, and the ability to add a variable amount of values to it is just a nicety done by the compiler.
You can confirm this with a snippet like this:
void Main()
{
var parameterCount = typeof(Test).GetMethod("Foo").GetParameters().Count();
Console.WriteLine(parameterCount); // Output: 2
}
// Define other methods and classes here
public static class Test
{
public static void Foo(double x, params double[] y)
{}
}
If you need to invoke a function that uses a params array with user provided values when the params array is not the only parameter, you're going to need to get the method parameter count and work out where the array actually starts, then wrap things accordingly.

Calling a generic method with some known type arguments

I'm trying to call a generic but don't know how this should work. I looked around the internet and could not find a solution whichmatches my case.
I got following method:
public void method<T>(string name, string sheet, List<List<T>> xList, List<List<T>> yList)
Now I want to simply invoke this method. When I do so I get an error and it tells me that I have to specify the type arguments. When I specify them then it tells me that a "one-type-argument" is needed. What am I doing wrong here? I read that this should be done by reflection or something else but can't get it to work.
I tried to invoke the method with:
method<string, string, List<List<DateTime>>, List<List<Double>>>(name, sheet, xList, yList)
but this does not work!
You need
public void method<T1, T2>(string name, string sheet, List<List<T1>> xList, List<List<T2>> yList)
instead of
public void method<T>(string name, string sheet, List<List<T>> xList, List<List<T>> yList)
you only have 1 generic parameter. If T is a string then you would invoke your method like
method<string>(name,sheet,xList,yList);
if T is a DateTime then you would invoke like
method<DateTime>(name,sheet,xList,yList);
Or if you need 2 generic Parameters then you would do
method<T,U>(string name,string sheet,List<List<T>> xList, List<List<U>> xList);
And invoke like
method<DateTime,Double>(name,sheet,xList,yList);
The generic type on your xlist and ylist must be the same. For example this works.
void Main() {
var foo = new List<List<Foo>>();
var foo2 = new List<List<Foo>>();
method("sdf", "sdf", foo, foo2);
}
class Foo{}
But in order to pass two lists of different types you would have to change your method signature to public void method<T,U>(string name, string sheet, List<List<T>> xList, List<List<U>> yList). Then something like this...
void Main() {
var foo = new List<List<Foo>>();
var bar = new List<List<Bar>>();
method("sdf", "sdf", foo, bar);
}
class Foo{}
class Bar{}
Would be possible.

C# Template options

I'm trying to do something which would work in C++, but C# is a bit of a challenge for me.
I have a method which does a bit of parsing, database access, web access/etc and eventually ends up with a series of strings to add to a container. Sometimes I need to add it to a hashset or list or etc.
So, in c++, this would look like this :
<template T>
bool GetStrings(T& container)
{
...
std::string foo = "bar";
...
container.add(foo); // This statement is within a loop and if checks
...
return true;
}
I tried that in C# :
private bool GetStrings<T>(ref T cont)
{
string foo = "BAR";
cont.Add(foo); // T does not contain a definition for Add ...
return true;
}
A coworker suggested using the container's base class/interface instead. So, I tried this out (after seeing List, Hashset, etc have a common interface definition) :
private bool GetStrings(ref ICollection<string> cont)
{
string foo = "BAR";
cont.Add(foo);
return true;
}
I want to be able call this method like this :
HashSet<string> a = new HashSet<string>();
List<string> b = new List<string>();
// etc other classes containing "Add" method
if (GetString(ref a)) ... // These aren't all in one place, but spread out
if (GetString(ref b)) ... // and the types are based on what is useful in
if (GetString(ref c)) ... // each particular context.
if (GetString(ref d)) ...
Now the method itself compiles, but I can't invoke it. I get Best overload has invalid arguments, "Argument 1: cannot convert from 'ref System.Collections.Generic.List' to 'ref System.Collections.Generic.ICollection'"
I figure its just a type cast needed thing. So, I try :
if (GetString(ref (ICollection<string>)a)) ...
Then I get "A ref or out argument must be an assignable variable". So, the question is... can this be done in C#, am I just totally on the wrong track? I also looked into passing an Object ref and trying to call 'GetType' and 'GetMethod' and such to figure out if Add was available and such but couldn't figure out how to call the Add method.
Use generic type constraints.
You can do the following that compiles and runs:
class Program
{
static void Main(string[] args)
{
Test t = new Test();
HashSet<string> a = new HashSet<string>();
List<string> b = new List<string>();
if (t.GetString(ref a))
{
}
if (t.GetString(ref b))
{
}
}
public class Test
{
public bool GetString<T>(ref T cont) where T : ICollection<string>
{
string foo = "BAR";
cont.Add(foo);
return true;
}
}
}
By the way all collections are already reference types, unless you want to change the original variable, the ref is not necessary. See here for an explanation.
You need to use a generic constraint on your method:
private bool GetStrings<T>(T cont) where T : ICollection<string>
{
string foo = "BAR";
// The compiler knows that T is always ICollection<string>, and can infer that Add
// is a valid method call
cont.Add(foo);
return true;
}
Additionally, you don't need to pass your list by reference - it's already a reference. In C++ parlance, it's like you're passing a pointer to a pointer there.

Passing method as parameter to function

Is there a way of passing in a method to a function as a parameter and then calling it via list.Sort()? I've tried this:
public static string BuildHumanSitemap(Func<TreeNode, TreeNode, int> sortMethod, params string[] classNames)
{
//calling list sort on method passed as parameter
nodes.sort(sortMethod);
}
Where the functions i want to pass in all take the same params e.g.
private static int SortByDateCreated(TreeNode x, TreeNode y)
{
DateTime xT = (DateTime)x["DocumentCreatedWhen"];
DateTime yT = (DateTime)y["DocumentCreatedWhen"];
return xT.CompareTo(yT);
}
I've also tried using an Action delegate type but the sort method complains when i pass it as a parameter. Can anyone offer a suggestion on how to do this?
Thankyou
Create new Comparison delegate and pass it to Sort method:
nodes.Sort(new Comparison<TreeNode>(sortMethod));
Maybe instead of taking in a Func<,,> delegate, you should consume a Comparison<> delegate. Because that's what List<> wants (for historical reasons; the List<>.Sort method was written for .NET 2.0, before the Func delegates were introduced).
Therefore:
public static string BuildHumanSitemap(Comparison<TreeNode> sortMethod, params string[] classNames)
{
//calling list sort on method passed as parameter
nodes.Sort(sortMethod);
}
Then call your method very simply like this:
BuildHumanSitemap(SortByDateCreated);
where SortByDateCreated is the "method group" from your question.
There's no need for first creating a delegate instance of type Func<TreeNode, TreeNode, int> and then create another delegate instance (of type Comparison<TreeNode>) which references the first one.
Of course you can also call your BuildHumanSitemap method with a lambda arrow as the first argument.
It works this way:
TreeView.TreeViewNodeSorter = new CustomNodeSorter();
private class CustomNodeSorter : IComparer
{
public int Compare(object x, object y)
{
DateTime xT = (DateTime)x["DocumentCreatedWhen"];
DateTime yT = (DateTime)y["DocumentCreatedWhen"];
return xT.CompareTo(yT);
}
}
Solution with IComparer<T>.
Comparer
public class MyTreeNodeComparer : IComparer<TreeNode>
{
public int Compare(TreeNode x, TreeNode y)
{
DateTime xT = (DateTime)x["DocumentCreatedWhen"];
DateTime yT = (DateTime)y["DocumentCreatedWhen"];
return xT.CompareTo(yT);
}
}
Usage
list.Sort(new MyTreeNodeComparer());

Categories

Resources