Reflection: How to Invoke Method with parameters - c#

I am trying to invoke a method via reflection with parameters and I get:
object does not match target type
If I invoke a method without parameters, it works fine. Based on the following code if I call the method Test("TestNoParameters"), it works fine. However if I call Test("Run"), I get an exception. Is something wrong with my code?
My initial purpose was to pass an array of objects e.g. public void Run(object[] options) but this did not work and I tried something simpler e.g. string without success.
// Assembly1.dll
namespace TestAssembly
{
public class Main
{
public void Run(string parameters)
{
// Do something...
}
public void TestNoParameters()
{
// Do something...
}
}
}
// Executing Assembly.exe
public class TestReflection
{
public void Test(string methodName)
{
Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
Type type = assembly.GetType("TestAssembly.Main");
if (type != null)
{
MethodInfo methodInfo = type.GetMethod(methodName);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(methodInfo, parametersArray);
}
}
}
}
}

Change "methodInfo" to "classInstance", just like in the call with the null parameter array.
result = methodInfo.Invoke(classInstance, parametersArray);

You have a bug right there
result = methodInfo.Invoke(methodInfo, parametersArray);
it should be
result = methodInfo.Invoke(classInstance, parametersArray);

A fundamental mistake is here:
result = methodInfo.Invoke(methodInfo, parametersArray);
You are invoking the method on an instance of MethodInfo. You need to pass in an instance of the type of object that you want to invoke on.
result = methodInfo.Invoke(classInstance, parametersArray);

The provided solution does not work for instances of types loaded from a remote assembly. To do that, here is a solution that works in all situations, which involves an explicit type re-mapping of the type returned through the CreateInstance call.
This is how I need to create my classInstance, as it was located in a remote assembly.
// sample of my CreateInstance call with an explicit assembly reference
object classInstance = Activator.CreateInstance(assemblyName, type.FullName);
However, even with the answer provided above, you'd still get the same error. Here is how to go about:
// first, create a handle instead of the actual object
ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
// unwrap the real slim-shady
object classInstance = classInstanceHandle.Unwrap();
// re-map the type to that of the object we retrieved
type = classInstace.GetType();
Then do as the other users mentioned here.

I tried to work with all the suggested answers above but nothing seems to work for me. So i am trying to explain what worked for me here.
I believe if you are calling some method like the Main below or even with a single parameter as in your question, you just have to change the type of parameter from string to object for this to work. I have a class like below
//Assembly.dll
namespace TestAssembly{
public class Main{
public void Hello()
{
var name = Console.ReadLine();
Console.WriteLine("Hello() called");
Console.WriteLine("Hello" + name + " at " + DateTime.Now);
}
public void Run(string parameters)
{
Console.WriteLine("Run() called");
Console.Write("You typed:" + parameters);
}
public static string StaticString()
{
return "static string example";
}
public string TestNoParameters()
{
Console.WriteLine("TestNoParameters() called");
return ("TestNoParameters() called");
}
public void Execute(object[] parameters)
{
Console.WriteLine("Execute() called");
Console.WriteLine("Number of parameters received: " + parameters.Length);
for(int i=0;i<parameters.Length;i++){
Console.WriteLine(parameters[i]);
}
}
}
}
Then you have to pass the parameterArray inside an object array like below while invoking it. The following method is what you need to work
private object ExecuteWithReflection(string methodName,object parameterObject = null)
{
Assembly assembly = Assembly.LoadFile("Assembly.dll");
Type typeInstance = assembly.GetType("TestAssembly.Main");
MethodInfo methodInfo = typeInstance.GetMethod(methodName);
ParameterInfo[] parameterInfo = methodInfo.GetParameters();
object result = null;
if (typeInstance != null) //non static
{
if(methodInfo.IsStatic == false)
{
//instance is needed to invoke the method
object classInstance = Activator.CreateInstance(typeInstance, null);
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(classInstance, null);
}
else
{
result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
}
}
else //handle static
{
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(null, null);
}
else
{
result = methodInfo.Invoke(null,new object[] { parameterObject } );
}
}
}
return result;
}
This method makes it easy to invoke the method, it can be called as following
ExecuteWithReflection("Hello");
ExecuteWithReflection("Run","Vinod");
ExecuteWithReflection("TestNoParameters");
ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});
ExecuteWithReflection("StaticString");

I'am posting this answer because many visitors enter here from google for this problem.
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );
when external .dll -instead of this.GetType(), you might use typeof(YourClass).

I would use it like this, its way shorter and it won't give any problems
dynamic result = null;
if (methodInfo != null)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
}

Assembly assembly = Assembly.LoadFile(#"....bin\Debug\TestCases.dll");
//get all types
var testTypes = from t in assembly.GetTypes()
let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
where attributes != null && attributes.Length > 0
orderby t.Name
select t;
foreach (var type in testTypes)
{
//get test method in types.
var testMethods = from m in type.GetMethods()
let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
where attributes != null && attributes.Length > 0
orderby m.Name
select m;
foreach (var method in testMethods)
{
MethodInfo methodInfo = type.GetMethod(method.Name);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(classInstance, parametersArray);
}
}
}
}

I m invoking the weighted average through reflection. And had used method with more than one parameter.
Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file
Object weightedobj = cls.newInstance(); // invoke empty constructor
Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object
return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method

On .Net 4.7.2 to invoke a method inside a class loaded from an external assembly you can use the following code in VB.net
Dim assembly As Reflection.Assembly = Nothing
Try
assembly = Reflection.Assembly.LoadFile(basePath & AssemblyFileName)
Dim typeIni = assembly.[GetType](AssemblyNameSpace & "." & "nameOfClass")
Dim iniClass = Activator.CreateInstance(typeIni, True)
Dim methodInfo = typeIni.GetMethod("nameOfMethod")
'replace nothing by a parameter array if you need to pass var. paramenters
Dim parametersArray As Object() = New Object() {...}
'without parameters is like this
Dim result = methodInfo.Invoke(iniClass, Nothing)
Catch ex As Exception
MsgBox("Error initializing main layout:" & ex.Message)
Application.Exit()
Exit Sub
End Try

Related

Generate Emit statements from IL

I want to add method body without going through Emit statements. Similar to:
public static void Replicate<T>(this MethodBuilder methodBuilder, Func<T> func)
{
var body = func.Method.GetMethodBody();
var instructions = body.GetILAsByteArray();
methodBuilder.CreateMethodBody(instructions, instructions.Length);
}
var methodBuilder = typeBuilder.DefineMethod("GetExtensionCollection", MethodAttributes.Public, typeof(Stream), Type.EmptyTypes);
methodBuilder.Replicate(() =>
{
var assembly = Assembly.GetExecutingAssembly();
return assembly.GetManifestResourceStream(assembly.GetName().Name + ".Extensions.xml");
});
When I decompile the generated assembly using dnSpy, the IL code is completely wrong, and the C# code throws exception.
I have also tried the following, based on Create a copy of method from IL and I have modified it to use Mono.Reflection:
public static void Replicate<T>(this MethodBuilder methodBuilder, Func<T> func)
{
var il = methodBuilder.GetILGenerator();
foreach (var local in func.Method.GetMethodBody().LocalVariables)
il.DeclareLocal(local.LocalType);
foreach (var instrcustion in func.Method.GetInstructions())
{
if (instrcustion.OpCode.OperandType == OperandType.InlineBrTarget)
{
il.Emit(instrcustion.OpCode, Convert.ToInt32(instrcustion.Operand));
continue;
}
if (instrcustion.OpCode.OperandType == OperandType.ShortInlineBrTarget)
{
continue;
}
if (instrcustion.OpCode.OperandType == OperandType.InlineString)
{
il.Emit(instrcustion.OpCode, instrcustion.Operand.ToString());
continue;
}
if (instrcustion.OpCode.OperandType == OperandType.InlineType)
{
il.Emit(instrcustion.OpCode, instrcustion.Operand as Type);
continue;
}
if (instrcustion.OpCode.FlowControl == FlowControl.Call)
{
var methodInfo = instrcustion.Operand as MethodInfo;
if (methodInfo == func.Method)
il.Emit(instrcustion.OpCode, methodBuilder);
else
il.Emit(instrcustion.OpCode, methodInfo);
continue;
}
il.Emit(instrcustion.OpCode);
}
}
This one works, but I need:
Preferably get the first Replicate to work
If not successful, build a comprehensive version of the second Replicate

Calling functions from string with parameters [duplicate]

I am trying to invoke a method via reflection with parameters and I get:
object does not match target type
If I invoke a method without parameters, it works fine. Based on the following code if I call the method Test("TestNoParameters"), it works fine. However if I call Test("Run"), I get an exception. Is something wrong with my code?
My initial purpose was to pass an array of objects e.g. public void Run(object[] options) but this did not work and I tried something simpler e.g. string without success.
// Assembly1.dll
namespace TestAssembly
{
public class Main
{
public void Run(string parameters)
{
// Do something...
}
public void TestNoParameters()
{
// Do something...
}
}
}
// Executing Assembly.exe
public class TestReflection
{
public void Test(string methodName)
{
Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
Type type = assembly.GetType("TestAssembly.Main");
if (type != null)
{
MethodInfo methodInfo = type.GetMethod(methodName);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(methodInfo, parametersArray);
}
}
}
}
}
Change "methodInfo" to "classInstance", just like in the call with the null parameter array.
result = methodInfo.Invoke(classInstance, parametersArray);
You have a bug right there
result = methodInfo.Invoke(methodInfo, parametersArray);
it should be
result = methodInfo.Invoke(classInstance, parametersArray);
A fundamental mistake is here:
result = methodInfo.Invoke(methodInfo, parametersArray);
You are invoking the method on an instance of MethodInfo. You need to pass in an instance of the type of object that you want to invoke on.
result = methodInfo.Invoke(classInstance, parametersArray);
The provided solution does not work for instances of types loaded from a remote assembly. To do that, here is a solution that works in all situations, which involves an explicit type re-mapping of the type returned through the CreateInstance call.
This is how I need to create my classInstance, as it was located in a remote assembly.
// sample of my CreateInstance call with an explicit assembly reference
object classInstance = Activator.CreateInstance(assemblyName, type.FullName);
However, even with the answer provided above, you'd still get the same error. Here is how to go about:
// first, create a handle instead of the actual object
ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
// unwrap the real slim-shady
object classInstance = classInstanceHandle.Unwrap();
// re-map the type to that of the object we retrieved
type = classInstace.GetType();
Then do as the other users mentioned here.
I tried to work with all the suggested answers above but nothing seems to work for me. So i am trying to explain what worked for me here.
I believe if you are calling some method like the Main below or even with a single parameter as in your question, you just have to change the type of parameter from string to object for this to work. I have a class like below
//Assembly.dll
namespace TestAssembly{
public class Main{
public void Hello()
{
var name = Console.ReadLine();
Console.WriteLine("Hello() called");
Console.WriteLine("Hello" + name + " at " + DateTime.Now);
}
public void Run(string parameters)
{
Console.WriteLine("Run() called");
Console.Write("You typed:" + parameters);
}
public static string StaticString()
{
return "static string example";
}
public string TestNoParameters()
{
Console.WriteLine("TestNoParameters() called");
return ("TestNoParameters() called");
}
public void Execute(object[] parameters)
{
Console.WriteLine("Execute() called");
Console.WriteLine("Number of parameters received: " + parameters.Length);
for(int i=0;i<parameters.Length;i++){
Console.WriteLine(parameters[i]);
}
}
}
}
Then you have to pass the parameterArray inside an object array like below while invoking it. The following method is what you need to work
private object ExecuteWithReflection(string methodName,object parameterObject = null)
{
Assembly assembly = Assembly.LoadFile("Assembly.dll");
Type typeInstance = assembly.GetType("TestAssembly.Main");
MethodInfo methodInfo = typeInstance.GetMethod(methodName);
ParameterInfo[] parameterInfo = methodInfo.GetParameters();
object result = null;
if (typeInstance != null) //non static
{
if(methodInfo.IsStatic == false)
{
//instance is needed to invoke the method
object classInstance = Activator.CreateInstance(typeInstance, null);
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(classInstance, null);
}
else
{
result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
}
}
else //handle static
{
if (parameterInfo.Length == 0)
{
// there is no parameter we can call with 'null'
result = methodInfo.Invoke(null, null);
}
else
{
result = methodInfo.Invoke(null,new object[] { parameterObject } );
}
}
}
return result;
}
This method makes it easy to invoke the method, it can be called as following
ExecuteWithReflection("Hello");
ExecuteWithReflection("Run","Vinod");
ExecuteWithReflection("TestNoParameters");
ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});
ExecuteWithReflection("StaticString");
I'am posting this answer because many visitors enter here from google for this problem.
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );
when external .dll -instead of this.GetType(), you might use typeof(YourClass).
I would use it like this, its way shorter and it won't give any problems
dynamic result = null;
if (methodInfo != null)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
}
Assembly assembly = Assembly.LoadFile(#"....bin\Debug\TestCases.dll");
//get all types
var testTypes = from t in assembly.GetTypes()
let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
where attributes != null && attributes.Length > 0
orderby t.Name
select t;
foreach (var type in testTypes)
{
//get test method in types.
var testMethods = from m in type.GetMethods()
let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
where attributes != null && attributes.Length > 0
orderby m.Name
select m;
foreach (var method in testMethods)
{
MethodInfo methodInfo = type.GetMethod(method.Name);
if (methodInfo != null)
{
object result = null;
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0)
{
// This works fine
result = methodInfo.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { "Hello" };
// The invoke does NOT work;
// it throws "Object does not match target type"
result = methodInfo.Invoke(classInstance, parametersArray);
}
}
}
}
I m invoking the weighted average through reflection. And had used method with more than one parameter.
Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file
Object weightedobj = cls.newInstance(); // invoke empty constructor
Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object
return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method
On .Net 4.7.2 to invoke a method inside a class loaded from an external assembly you can use the following code in VB.net
Dim assembly As Reflection.Assembly = Nothing
Try
assembly = Reflection.Assembly.LoadFile(basePath & AssemblyFileName)
Dim typeIni = assembly.[GetType](AssemblyNameSpace & "." & "nameOfClass")
Dim iniClass = Activator.CreateInstance(typeIni, True)
Dim methodInfo = typeIni.GetMethod("nameOfMethod")
'replace nothing by a parameter array if you need to pass var. paramenters
Dim parametersArray As Object() = New Object() {...}
'without parameters is like this
Dim result = methodInfo.Invoke(iniClass, Nothing)
Catch ex As Exception
MsgBox("Error initializing main layout:" & ex.Message)
Application.Exit()
Exit Sub
End Try

How to implement TLS-ALPN in .NET C# for a HTTP/2 server

does anyone know, how I can implement the TLS-ALPN in .NET?
I've implemented a basic HTTP/2 server, but without TLS encryption.
I searched in google, but I only found resources for C, Java or other languages, but nothing for .NET (C#)
According to HttpTwo project on Github it is not possible currently because of a bug.
Update: It's not supported in .NET. You can vote for it here: https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/6264363-add-support-for-alpn-to-system-net-security-sslstr
quote:
The HTTP/2 RFC states that secure connections must use ALPN to
negotiate the protocol. Unfortunately, .NET's SslStream has no ability
to specify application protocols as part of the TLS authentication, so
it can't support ALPN. There's an issue tracking this on
dotnetfix however it seems like this isn't going to happen very
soon (especially on mono and .NET 4.x).
It actually is possible. With some reflection you can inject any extension in client or server hello.
Here's some code to give you an idea:
// Refer IANA on ApplicationProtocols: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
public static void FixALPN(params string[] protocols) {
if (Interlocked.Increment(ref cntFixALPN) > 1)
{
throw new Exception("FixALPN should be called only ONCE, put it in your Main or use a static constructor.");
return;
}
// get the needed (internal) System types
string tpname = typeof(System.Net.HttpListener).AssemblyQualifiedName;
Type tpiface = Type.GetType(tpname.Replace("HttpListener", "SSPIInterface"));
Type tpgsspi = Type.GetType(tpname.Replace("HttpListener", "GlobalSSPI"));
Type tpsdc = Type.GetType(tpname.Replace("HttpListener", "SafeDeleteContext"));
Type tpsecbuf = Type.GetType(tpname.Replace("HttpListener", "SecurityBuffer"));
// create ALPN buffer
ConstructorInfo ci = (from x in tpsecbuf.GetConstructors() where x.GetParameters().Length == 4 select x).First();
var secbufempty = ci.Invoke(new object[] { new byte[0], 0, 0, 0 });
byte[] btsalpn = GetALPNBuffer(protocols);
var secbufalpn = ci.Invoke(new object[] { btsalpn, 0, btsalpn.Length, 18 });
// grab the object to replace...
FieldInfo fi = tpgsspi.GetField("SSPISecureChannel", BindingFlags.NonPublic | BindingFlags.Static);
var secchan = fi.GetValue(null);
// ...and the method(s) we'll use in our intercepted call(s)
MethodInfo miSDC_ISC = tpsdc.GetMethod("InitializeSecurityContext", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo miSDC_ASC = tpsdc.GetMethod("AcceptSecurityContext", BindingFlags.NonPublic | BindingFlags.Static);
// fake the internal interface
var result = new InterfaceImplementer(tpiface, (mcm) => {
MethodInfo mi = (MethodInfo)mcm.MethodBase;
object[] args = mcm.Args;
object ret = null;
if (mi.Name == "InitializeSecurityContext") // For Client Mode
{
if (args[5] == null) // empty input, new connection
{
dynamic[] secbufs = (dynamic[])Activator.CreateInstance(miSDC_ASC.GetParameters()[6].ParameterType, new object[] { 1 });
secbufs[0] = secbufalpn;
object[] sdcargs = new object[] { 0, args[0], args[1], args[2], args[3], args[4],
null,
secbufs,
args[6], args[7]
};
ret = miSDC_ISC.Invoke(null, sdcargs);
args[0] = sdcargs[1];
args[1] = sdcargs[2];
args[7] = sdcargs[9];
}
else
{
ret = mi.Invoke(secchan, args);
}
}
else if (mi.Name == "AcceptSecurityContext") // For Server Mode
{
dynamic[] secbufs = (dynamic[])Activator.CreateInstance(miSDC_ASC.GetParameters()[6].ParameterType, new object[] { 3 });
secbufs[0] = args[2];
secbufs[1] = secbufempty;
secbufs[2] = secbufalpn;
object[] sdcargs = new object[] { 0, args[0], args[1], args[3], args[4],
null,
secbufs,
args[5], args[6]
};
ret = miSDC_ASC.Invoke(null, sdcargs);
args[0] = sdcargs[1];
args[1] = sdcargs[2];
args[6] = sdcargs[8];
}
else
ret = mi.Invoke(secchan, args);
return new ReturnMessage(ret, args, args.Length, mcm.LogicalCallContext, mcm);
}).GetTransparentProxy();
// and set it, done
fi.SetValue(null, result);
}
.NET Core 2.1.2 includes the necessary changes to SslStream required to support ALPN. It isn't documented yet, but the pull request that adds it is here

Moq and throwing a SqlException

I have the following code to test that when a certain name is passed to my method, it throws a SQL exception (there is reason to that one, although it sounds a little odd).
mockAccountDAL.Setup(m => m.CreateAccount(It.IsAny<string>(),
"Display Name 2", It.IsAny<string>())).Throws<SqlException>();
However, this won't compile because SqlException's constructor is internal:
'System.Data.SqlClient.SqlException' must be a non-abstract type with
a public parameterless constructor in order to use it as parameter
'TException' in the generic type or method
'Moq.Language.IThrows.Throws()'
Now, I could change this to state that it should throw Exception, but that wouldn't work for me, because my method should return one status code if it is a SqlException and another if it is any other exception. That's what my unit test is testing.
Is there any way to achieve this without either changing the logic of the method I'm testing, or not testing this scenario?
If you need test cases for the Number or Message properties of the exception, you could use a builder (which uses reflection) like this:
using System;
using System.Data.SqlClient; // .NetCore using Microsoft.Data.SqlClient;
using System.Linq;
using System.Reflection;
public class SqlExceptionBuilder
{
private int errorNumber;
private string errorMessage;
public SqlException Build()
{
SqlError error = this.CreateError();
SqlErrorCollection errorCollection = this.CreateErrorCollection(error);
SqlException exception = this.CreateException(errorCollection);
return exception;
}
public SqlExceptionBuilder WithErrorNumber(int number)
{
this.errorNumber = number;
return this;
}
public SqlExceptionBuilder WithErrorMessage(string message)
{
this.errorMessage = message;
return this;
}
private SqlError CreateError()
{
// Create instance via reflection...
var ctors = typeof(SqlError).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
var firstSqlErrorCtor = ctors.FirstOrDefault(
ctor =>
ctor.GetParameters().Count() == 7); // .NetCore should be 8 not 7
SqlError error = firstSqlErrorCtor.Invoke(
new object[]
{
this.errorNumber,
new byte(),
new byte(),
string.Empty,
string.Empty,
string.Empty,
new int()
//,new Exception() // for .NetCore
}) as SqlError;
return error;
}
private SqlErrorCollection CreateErrorCollection(SqlError error)
{
// Create instance via reflection...
var sqlErrorCollectionCtor = typeof(SqlErrorCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
SqlErrorCollection errorCollection = sqlErrorCollectionCtor.Invoke(new object[] { }) as SqlErrorCollection;
// Add error...
typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(errorCollection, new object[] { error });
return errorCollection;
}
private SqlException CreateException(SqlErrorCollection errorCollection)
{
// Create instance via reflection...
var ctor = typeof(SqlException).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
SqlException sqlException = ctor.Invoke(
new object[]
{
// With message and error collection...
this.errorMessage,
errorCollection,
null,
Guid.NewGuid()
}) as SqlException;
return sqlException;
}
}
Then you could have a repository mock (for instance) throw an exception like this (this example uses the Moq library):
using Moq;
var sqlException =
new SqlExceptionBuilder().WithErrorNumber(50000)
.WithErrorMessage("Database exception occured...")
.Build();
var repoStub = new Mock<IRepository<Product>>(); // Or whatever...
repoStub.Setup(stub => stub.GetById(1))
.Throws(sqlException);
This should work:
using System.Runtime.Serialization;
var exception = FormatterServices.GetUninitializedObject(typeof(SqlException))
as SqlException;
mockAccountDAL.Setup(m => m.CreateAccount(It.IsAny<string>(), "Display Name 2",
It.IsAny<string>())).Throws(exception);
However, using GetUninitializedObject has this caveat:
Because the new instance of the object is initialized to zero and no
constructors are run, the object might not represent a state that is
regarded as valid by that object.
If this causes any problems, you can probably create it using some more involved reflection magic but this way is probably the simplest (if it works).
I just tried this out, and it worked for me:
private static void ThrowSqlException()
{
using (var cxn = new SqlConnection("Connection Timeout=1"))
{
cxn.Open();
}
}
// ...
mockAccountDAL.Setup(m => m.CreateAccount(It.IsAny<string>),
"Display Name 2", It.IsAny<string>()))
.Callback(() => ThrowSqlException());
For me to produce an SqlException with a message it was the simplest way using the Uninitialized Object method:
const string sqlErrorMessage = "MyCustomMessage";
var sqlException = FormatterServices.GetUninitializedObject(typeof(SqlException)) as SqlException;
var messageField = typeof(SqlException).GetField("_message", BindingFlags.NonPublic | BindingFlags.Instance);
messageField.SetValue(sqlException, sqlErrorMessage);
I wrote this before finding this question/answer. Might be useful for someone just wanting a SQL exception with a particular number.
private static SqlException CreateSqlExceptionWithNumber(int errorNumber)
{
var sqlErrorCollectionCtor = typeof(SqlErrorCollection).GetConstructor(
BindingFlags.NonPublic | BindingFlags.Instance,
null,
CallingConventions.Any,
new Type[0],
null);
var sqlErrorCollection = (SqlErrorCollection)sqlErrorCollectionCtor.Invoke(new object[0]);
var errors = new ArrayList();
var sqlError = (SqlError)FormatterServices.GetSafeUninitializedObject(typeof(SqlError));
typeof(SqlError)
.GetField("number", BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(sqlError, errorNumber);
errors.Add(sqlError);
typeof(SqlErrorCollection)
.GetField("errors", BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(sqlErrorCollection, errors);
var exception = (SqlException)FormatterServices.GetUninitializedObject(typeof(SqlException));
typeof(SqlException)
.GetField("_errors", BindingFlags.NonPublic | BindingFlags.Instance)
?.SetValue(exception, sqlErrorCollection);
return exception;
}
public class SqlExceptionMock
{
public static SqlException ThrowSqlException(int errorNumber, string message = null)
{
var ex = (SqlException)FormatterServices.GetUninitializedObject(typeof(SqlException));
var errors = GenerateSqlErrorCollection(errorNumber, message);
SetPrivateFieldValue(ex, "_errors", errors);
return ex;
}
private static SqlErrorCollection GenerateSqlErrorCollection(int errorNumber, string message)
{
var t = typeof(SqlErrorCollection);
var col = (SqlErrorCollection)FormatterServices.GetUninitializedObject(t);
SetPrivateFieldValue(col, "_errors", new List<object>());
var sqlError = GenerateSqlError(errorNumber, message);
var method = t.GetMethod(
"Add",
BindingFlags.NonPublic | BindingFlags.Instance);
method.Invoke(col, new object[] { sqlError });
return col;
}
private static SqlError GenerateSqlError(int errorNumber, string message)
{
var sqlError = (SqlError)FormatterServices.GetUninitializedObject(typeof(SqlError));
SetPrivateFieldValue(sqlError, "_number", errorNumber);
if (!string.IsNullOrEmpty(message)) SetPrivateFieldValue(sqlError, "_message", message);
return sqlError;
}
private static void SetPrivateFieldValue(object obj, string field, object val)
{
var member = obj.GetType().GetField(
field,
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance
);
member?.SetValue(obj, val);
}
}

How to find the FULL name of the calling method in C#

How can I find the full name of a calling method in C#? I have seen solutions:
How I can get the calling methods in C#
How can I find the method that called the current method?
Get Calling function name from Called function
But they only give me the top level. Consider the example:
namespace Sandbox
{
class Program
{
static void Main(string[] args)
{
test();
}
static void test()
{
var stackTrace = new StackTrace();
var methodBase = stackTrace.GetFrame(1).GetMethod();
Console.WriteLine(methodBase.Name);
}
}
}
This simply outputs 'Main'. How can I get it to print 'Sandbox.Program.Main'?
It's for a simple logging framework that I am working on.
Adding onto Matzi's Answer:
Here is the solution:
namespace Sandbox
{
class Program
{
static void Main(string[] args)
{
test();
}
static void test()
{
var stackTrace = new StackTrace();
var methodBase = stackTrace.GetFrame(1).GetMethod();
var Class = methodBase.ReflectedType;
var Namespace = Class.Namespace; // Added finding the namespace
Console.WriteLine(Namespace + "." + Class.Name + "." + methodBase.Name);
}
}
}
It produces 'Sandbox.Program.Main' like it should.
This is something like here.
MethodBase method = stackTrace.GetFrame(1).GetMethod();
string methodName = method.Name;
string className = method.ReflectedType.Name;
Console.WriteLine(className + "." + methodName);
I think the best way to get the full name is:
this.GetType().FullName + "." + System.Reflection.MethodBase.GetCurrentMethod().Name;
Or try this:
string method = string.Format("{0}.{1}", MethodBase.GetCurrentMethod().DeclaringType.FullName, MethodBase.GetCurrentMethod().Name);
And if you want to display the most recent function call, you can use:
StackTrace st = new StackTrace();
StackFrame sf = st.GetFrame(0);
var methodName = sf.GetMethod();
But if you want to display the tree of calling functions, you can do it like this:
if (st.FrameCount >1)
{
// Display the highest-level function call
// in the trace.
StackFrame sf = st.GetFrame(st.FrameCount-1);
Console.WriteLine(" Original function call at top of call stack):");
Console.WriteLine(" {0}", sf.GetMethod());
}
For more information.
With this method you can reliably get the full name
public void HandleException(Exception ex, [CallerMemberName] string caller = "")
{
if (ex != null)
{
while (ex.InnerException != null)
ex = ex.InnerException;
foreach (var method in new StackTrace().GetFrames())
{
if (method.GetMethod().Name == caller)
{
caller = $"{method.GetMethod().ReflectedType.Name}.{caller}";
break;
}
}
Console.WriteLine($"Exception: {ex.Message} Caller: {caller}()");
}
}
In the System.Reflection.MethodBase method GetCurrentMethod, you can find full information about the call stack using classes, etc.
The current calling namespace which is not equal as the current namespace:
var mNamespace = new StackTrace().GetFrames()?.Select(x =>
{
try
{
return x.GetMethod().ReflectedType?.Namespace;
}
catch (Exception)
{
return string.Empty;
}
}).First(x => x != new StackTrace().GetFrame(0).GetMethod().ReflectedType?.Namespace);

Categories