I am curious to see if anyone else has run into this same issue...
I am using Dapper as on ORM for a project and was creating some of my own extension methods off of the IDbConnection interface in order to simplify code, where I ran into (what I found to be) puzzling error.
I will walk through the process I went through.
First, I added an extension method to my project in a static class named DbExtensions like so:
using System.Collections.Generic;
using System.Data;
using System.Linq;
public static class DbExtensions
{
public static T Scalar<T>(
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
var ret = cnn.Query<T>(sql, param as object, transaction, buffered, commandTimeout, commandType).First();
return ret;
}
}
This creates a compile error with the following description:
'System.Data.IDbConnection' has no applicable method named 'Query' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
This is fine, and the error is actually rather helpful as it even tells me how to fix it. So I then try:
using System.Collections.Generic;
using System.Data;
using System.Linq;
public static class DbExtensions
{
public static T Scalar<T>(
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
var ret = SqlMapper.Query<T>(cnn, sql, param, transaction, buffered, commandTimeout, commandType).First();
return ret;
}
}
and it compiles correctly. Something strange is going on though. In Visual Studio, if I take the return value of SqlMapper.Query<T> which should be IEnumerable<T>, and I try to operate on it, Visual Studio gives me NO intellisense properties except for those inherited via object.
Thinking I am just doing something that intellisense isn't smart enough to figure out, I go on my merry way... until I actually try to RUN the code.
When I try to run it, it trips up where I am calling .First() with the following error:
'System.Collections.Generic.List<MyNameSpace.MyClass>' does not contain a definition for 'First'
Now THIS error, I thought was interesting... After banging my head for a while, I realized the first argument was complaining about the dynamic typing...
I suppose this error is occurring because the compiler cannot build the Generic Template because it does not know that Query is returning IEnumerable<T> as it is being executed in the DLR? I would love to hear someone explain this who was knowledgeable. I have essentially found two ways to fix it:
Cast the dynamic param to an object
Cast the returned value to an IEnumerable<T>
using System.Collections.Generic;
using System.Data;
using System.Linq;
public static class DbExtensions
{
public static T Scalar<T>(
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
var ret = SqlMapper.Query<T>(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType).First();
return ret;
}
}
using System.Collections.Generic;
using System.Data;
using System.Linq;
public static class DbExtensions
{
public static T Scalar2<T>(
this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
var ret = ((IEnumerable<T>)SqlMapper.Query<T>(cnn, sql, param, transaction, commandTimeout, commandType)).First();
return ret;
}
}
IN SUMMARY:
I am new to working through the qwerks of the DLR and there seem to be some caveats to keep in mind when messing around with dynamic + Generics...?
I know this isn't a question per-se, but when I actually started writing this I didn't know what was going on and I figured it out in the process! I thought it might help someone else with similar issues though...
As suggested, I will try and Answer my question in an actual answer... (Now that it's been 8 hours)
My understanding of the issue is this:
As described in the referenced question, dynamic types do not have extension methods available to them, but extension methods can be used normally (as instance methods), just as they would be without the this keyword...
for instance:
dynamic list = someListObject;
var item = list.First(); //this will not compile
var item = Enumerable.First(list); //this will compile
As Jon Skeet has pointed out in this answer this is all by design and part of the DLR implementation - where if any invocation has a dynamic argument it will have a return type considered dynamic.
For similar reasons, using dynamic variables in extension methods is a bit wonky...
public static Enumerable<T> ExtensionMethod(this ExtendedObject p1, dynamic p2) {
//Do Stuff
}
dynamic y = something;
var x = new ExtendedObject();
//this works
var returnedEnumerable = x.ExtensionMethod(y);
//this doesn't work
var returnedValue = x.ExtensionMethod(y).SomeEnumerableExtensionMethodLikeFirst()
To make the above example work you can do one of the following:
//cast dynamic as object
var returnedValue = x.ExtensionMethod(y as object).First();
//cast returned object
var returnedValue = ((IEnumerable<KnownType>)x.ExtensionMethod(y)).First();
Related
I need to based on string type - for example 'UserModel'
execute method
Task<IEnumerable<T>> QueryAsync<T>(string sqlStatment, DynamicParameters parameters, CommandType commandType = CommandType.StoredProcedure, int timeout = 90, string connectionId = "Default");
so I have
string TypeName = "UserModel";
Type type = Type.GetType("XXX.Shared.CoreClasses."+ TypeName+", XX.Shared")!;
if(type is null) throw new ArgumentException(string.Format("type {0} not found", TypeName));
IList l = (IList)Activator
.CreateInstance(typeof(List<>)
.MakeGenericType(type))!;
System.Reflection.MethodInfo method = _sql.GetType().GetMethods().Where(c => c.Name == ("QueryAsync")
&& c.GetParameters()[1].ParameterType == typeof(DynamicParameters)).First().MakeGenericMethod(new Type[] { type });
object[] args = { SPname,d, CommandType.StoredProcedure,timeout };//not relevant
so now how to fill this l list with this method ?
like
l= await method.Invoke(this, args)!;
it yeals at me about casting / object canot be awaited ?
how to do this properly ?
is something like this slow ? or nowdays it does not matter until it will have to execute like xxxx times/sec ?
thanks and regards !
The QueryAsync<T> method returns a Task<IEnumerable<T>> instance.
But the result of calling this method through reflection via method.Invoke(this, args) is type-cast as object, and since the type for the generic type parameter is given as a type name string, it is impossible here to cast directly to Task<IEnumerable<TypeIdentifier>> and await it, because the TypeIdentifier has to be known and specified in the source code at compile-time.
object task = method.Invoke(this, args)!;
Although, from the question it looks like the QueryAsync method is an instance of the type of the variable _sql. Therefore, possibly the correct way to invoke this method is perhaps:
object task = method.Invoke(_sql, args)!;
But how can we await the task then and get the result? Another small static helper method will solve this issue. We are calling it through reflection again, thus avoiding the impossible step of casting the object-typed QueryAsync<T> result as some Task<IEnumerable<TypeIdentifier>> in our source code.
private static async Task<IEnumerable> AwaitQueryAsyncResult<T>(Task<IEnumerable<T>> task)
=> await task;
Note the return type. It's a task returning IEnumerable (which works because IEnumerable<T> inherits IEnumerable). In some sense we basically "converted" a Task<IEnumerable<TypeIdentifier>> we are unable to specify in the source code to a Task<IEnumerable> - a concrete type that can be specified in the source code. No more generic type parameter anymore we don't know about at compile-time!
From here on, it's pretty much straightforward. We will call our little generic helper method AwaitQueryAsyncResult<T> through reflection in similar fashion as has been done with the QueryAsync<T> method:
...
object task = method.Invoke(this, args)!;
var miAwaitQueryAsyncResult = typeof(TheTypeWhereAwaitQueryAsyncResultIsDeclared)
.GetMethod(nameof(AwaitQueryAsyncResult), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
.MakeGenericMethod(type);
var resultAsEnumerable = await (Task<IEnumerable>) miAwaitQueryAsyncResult.Invoke(null, new[] { task } );
With QueryAsync<T>'s result now available as usefully typed resultAsEnumerable variable, it should be not too difficult to iterate/loop over it and add its elements to the list in the IList-typed variable l. I will leave the implementation of this little loop as a coffee-time exercise to the reader ;-)
As far as I knew, Object.GetType() should never return null. (related discussion)
Dapper .Query() return private class DapperRow instances to be treated as dynamic objects. I found a strange thing: DapperRow's .GetType() return null.
Here's the sample code to reproduce the problem. Create a C# project, reference Dapper and open a connection to SQL Server (or other database), use .Query() to execute simple select query and retrieve the first row of result. Use GetType() to get the type of result object, the return value is null.
using (SqlConnection cn = new SqlConnection(csSql))
{
var rec = cn.Query("select getdate() as D").Single();
var t = rec.GetType(); // t == null
Console.WriteLine(t.Name); // null reference exception
}
I suspect that dynamic or private type is the cause of null, so I write my class library for test:
namespace Lib
{
public class Blah
{
public static dynamic SecretObject;
static Blah()
{
SecretObject = new PrivateType();
}
}
class PrivateType
{
}
}
In another project, get the dynamic type static field and call GetType():
dynamic obj = Lib.Blah.SecretObject;
Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"
According to the test result, even cast private type as dynamic, I still can get the private type information from GetType(), why DapperRow.GetType() return null?
DapperRow is specifically built and utilized within Dapper to provide highly optimized row returns without reiterating header information. This is to help condense the size of the object and reduce redundant data, making it more efficient.
However, it would appear that the StackExchange team took the meta programming even further than a first glance would indicate.
DapperRow implements the System.Dynamic.IDynamicMetaObjectProvide interface, which requires that the GetMetaObject method be implemented:
System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
System.Linq.Expressions.Expression parameter)
{
return new DapperRowMetaObject(parameter,
System.Dynamic.BindingRestrictions.Empty, this);
}
DapperRowMetaObject is a custom implementation of DynamicMetaObject that essentially hijacks and overrides what methods can be invoked against the dynamic type and what those calls should translate to. In this case, calls to anything other than the DapperRow's IDictionary.Item getter or the DapperRow.SetValue will fail since they are always routed to those two calls, but the value will be defaulted to null for any "get" calls where the target property does not exist in the table.
public bool TryGetValue(string name, out object value)
{
var index = table.IndexOfName(name);
if (index < 0)
{ // doesn't exist
value = null;
return false;
}
...
}
At that point, any methods invoked on a null dynamic value will throw a RuntimeBinderException:
RuntimeBinderException: Cannot perform runtime binding on a null
reference
You can easily test this hypothesis by replacing GetType() with another call that will throw the exact same exception:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());
Keep in mind, the underlying type information of any properties on the dynamic object itself can still be accessed directly:
var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);
Compile Error
'System.Data.SqlClient.SqlConnection' has no applicable method named 'Query' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
Now, I know how to work around the problem, but I'm trying to get a better understanding of the error itself. I have class that I'm building to leverage Dapper. In the end I'm going to provide some more custom functionality to make our type of data access a lot more streamlined. In particular building in tracing and stuff. However, right now it's as simple as this:
public class Connection : IDisposable
{
private SqlConnection _connection;
public Connection()
{
var connectionString = Convert.ToString(ConfigurationManager.ConnectionStrings["ConnectionString"]);
_connection = new SqlConnection(connectionString);
_connection.Open();
}
public void Dispose()
{
_connection.Close();
_connection.Dispose();
}
public IEnumerable<dynamic> Query(string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
// this one works fine, without compile error, so I understand how to
// workaround the error
return Dapper.SqlMapper.Query(_connection, sql, param, transaction, buffered, commandTimeout, commandType);
}
public IEnumerable<T> Query<T>(string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
// this one is failing with the error
return (IEnumerable<T>)_connection.Query(sql, param, transaction, buffered, commandTimeout, commandType);
}
}
but interestingly enough, if I were to simply issue a statement like this:
_connection.Query("SELECT * FROM SomeTable");
it compiles just fine.
So, can somebody please help me understand why leveraging the same overload inside of those other methods is failing with that error?
So, can somebody please help me understand why leveraging the same overload inside of those other methods is failing with that error?
Precisely because you're using a dynamic value (param) as one of the arguments. That means it will use dynamic dispatch... but dynamic dispatch isn't supported for extension methods.
The solution is simple though: just call the static method directly:
return SqlMapper.Query(_connection, sql, param, transaction,
buffered, commandTimeout, commandType);
(That's assuming you really need param to be of type dynamic, of course... as noted in comments, you may well be fine to just change it to object.)
Another solution to the same issue is to apply type casting to the dynamic value.
I encountered the same compile error with:
Url.Asset( "path/" + article.logo );
Which was resolved by doing:
Url.Asset( "path/" + (string) article.logo );
Note: the dynamic value is well-known to be a string, in this case; a fact reinforced by the string concatenation that is present.
I am reading and writing data to and from a file. The data in the file can be floats, doubles, ints etc. The type is not known until runtime. I will refer to data type stored in the file as Tin. Data is read into or written from arrays of type Tout. This type too is not known until runtime.
The code sequence is something like this. In the Open method Tin and Tout are known, we can create read and write methods for the known data types.
Open(...)
{
MethodInfo ReadMethod = typeof(...)GetMethod("ReadGeneric").MakeGenericMethod(new Type[] {typeof(Tin), typeof(Tout)}));
}
The read write loops repeat millions of times and rely on reflection to invoke the appropriate methods as shown below.
Read loop
{
var values = (Tout[])ReadMethod.Invoke(this,new object[]{index});
process ...
}
When examining this code using a performance profiler I find that c collosal amount if time is spent just invoking the runtime read write methods.
How do I speed this up.
Yes, this is due to the fact that the reflection API is thousands of times slower than direct method calls. There are some interesting techniques to work around this however. Check out Jon Skeet's article on using delegates to cache reflection.
There is a static setup cost but once you have done that the time to invoke the delegate repeatedly is equivalent to virtual method calls.
There are also some pre-packaged frameworks to achieve the same thing.
This'll do anything for ya, almost as fast as a direct call.
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
public class FastMethodInfo
{
private delegate object ReturnValueDelegate(object instance, object[] arguments);
private delegate void VoidDelegate(object instance, object[] arguments);
public FastMethodInfo(MethodInfo methodInfo)
{
var instanceExpression = Expression.Parameter(typeof(object), "instance");
var argumentsExpression = Expression.Parameter(typeof(object[]), "arguments");
var argumentExpressions = new List<Expression>();
var parameterInfos = methodInfo.GetParameters();
for (var i = 0; i < parameterInfos.Length; ++i)
{
var parameterInfo = parameterInfos[i];
argumentExpressions.Add(Expression.Convert(Expression.ArrayIndex(argumentsExpression, Expression.Constant(i)), parameterInfo.ParameterType));
}
var callExpression = Expression.Call(!methodInfo.IsStatic ? Expression.Convert(instanceExpression, methodInfo.ReflectedType) : null, methodInfo, argumentExpressions);
if (callExpression.Type == typeof(void))
{
var voidDelegate = Expression.Lambda<VoidDelegate>(callExpression, instanceExpression, argumentsExpression).Compile();
Delegate = (instance, arguments) => { voidDelegate(instance, arguments); return null; };
}
else
Delegate = Expression.Lambda<ReturnValueDelegate>(Expression.Convert(callExpression, typeof(object)), instanceExpression, argumentsExpression).Compile();
}
private ReturnValueDelegate Delegate { get; }
public object Invoke(object instance, params object[] arguments)
{
return Delegate(instance, arguments);
}
}
Profile to find the solution that matches your expectations :
.Net Framework offers plenty of methods to invoke dynamically methods. However they don't perform equally in terms of performance and they are not equally easy to use.
CreateDelegate may be what you're looking for
In the recent versions of .Net Framework, CreateDelegate beat by a factor 50 the MethodInfo invoke:
// The following should be done once since this does some reflection
var method = typeof (...).GetMethod("ReadGeneric");
// Here we create a Func that targets the instance of type which has the
// ReadGeneric method
var func = (Func<Tin, Tout[]>)_method.CreateDelegate(typeof(Func<Tin, Tout[]>), target);
// Func will be 50x faster than MethodInfo.Invoke
// use func as a standard Func like
// var tout = func(index);
Check this post of mine to see benchmark on different method invocations
I'm trying to extend SqlMethods.Like method to support property name rather than property value, i wrote the following extension method :
public static bool Like(this object obj, string propertyName, string pattern)
{
var properties = obj.GetType().GetProperties().Select(p => p.Name);
if(!properties.Contains(propertyName))
throw new Exception(string.Format("Object does not contain property:{0}", propertyName));
return SqlMethods.Like(obj.GetType().GetProperty(propertyName).GetValue(obj, null).ToString(), pattern);
}
however the method throws the following exception :
Method 'Boolean Like(System.Object, System.String, System.String)' has no supported translation to SQL.
how can i write an extension method with transaction to SQL support ?
I found this answer from RichardD that is exactly the correct answer. Reposting for clarity, but original is linked below.
using System;
using System.Linq;
using System.Linq.Expressions;
public static class Extensions
{
public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, string propertyName, string pattern)
{
if (null == source) throw new ArgumentNullException("source");
if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
var a = Expression.Parameter(typeof(T), "a");
var prop = Expression.Property(a, propertyName);
var body = Expression.Call(typeof(SqlMethods), "Like", null, prop, Expression.Constant(pattern));
var fn = Expression.Lambda<Func<T, bool>>(body, a);
return source.Where(fn);
}
}
...
.WhereLike("Description", "%a%b%c%"));
The solution uses expression trees, but all advanced LinqToSql operations will require familiarity with that.
From: http://forums.asp.net/p/1488418/3503874.aspx
What you want to do does not seem to make sense in the contxt of what SqlMethods.Like actually does. When you pass in a property of a class you are essentially telling it to translate that into the equivelent field in the SQL query. e.g.
var result = from names in db.Names
where SqlMethods.Like(names.FullName, '%Smith%')
select names;
would translate to something like:
SELECT *
FROM Names
WHERE Fullname LIKE '%Smith%'
(in practice it would be different using parameters and sp_executeSQL but coneptually that is what it would do).
If you want to pass in the name of a property what does that mean in terms of SQL, conceptually it makes no sense e.g.
SELECT *
FROM Names
WHERE --what would go here-- LIKE '%Smith%'
As such you are not going to be able to create a Linq To SQL method that creates nonsense SQL.
What are you actually trying to do, the chance is that you are going about it completely the wrong way.
Edit:hmm from your comment i think i understand what you want to do, in essense you want to be able to specify the column you are doing a LIKE comparison with at run time. You cannot do it exactly. You could use a stored procedure that used dynamic SQL and took a string parameter for the column. You could then expose this as a method on your data context class.