In my application I use Dapper to access the database.
I've the following example code:
public IEnumerable GetByParentId(Type childType, string table)
{
IDbConnection _connection = _dbProvider.GetConnection();
var _sqlString = "select * from " + table;
IEnumerable _ret = _connection.Query(_sqlString).ToList();
//return IEnumerable<Dapper.SqlMapper.FastExpando>
return _ret;
}
It is possible to cast FastExpando item to my childType or force Dapper to return a strongly typed collection?
I cannot change my method signature!
You can call Query method via reflection and supply generic TItem argument of your childType. Then, Dapper will return IEnumerable and you will be able to cast it.
Also, you could fork Dapper (it is not very big) and make a very simple overload of Query, that will include (Type childType) argument and call proper methods inside.
What you face is the problem of C# in working with generics. Being statically typed language, C# is bad a working with dynamic types. If you want to work dynamically, you always end up with reflection.
Here's a sample of how you could call query method with type arguments. You may have to correct this a bit:
public IEnumerable GetByParentId(Type childType, string table)
{
IDbConnection _connection = _dbProvider.GetConnection();
var _sqlString = "select * from " + table;
var t = typeof(SqlMapper);
var genericQuery = t.GetMethods().Where(x => x.Name == "Query" && x.GetGenericArguments().Length == 1).First(); // You can cache this object.
var concreteQuery = genericQuery.MakeGenericMethod(childType); // you can also keep a dictionary of these, for speed.
var _ret = (IEnumerable)concreteQuery.Invoke(null, new object[] { _connection, _sqlString });
return _ret;
}
Append:
Also, I see a more general design problem here. You want to specify type dynamically, but then want to get statically typed objects, that you will be able to cast (I assume statically, or you want to continue with reflection?). Then... Why do you create dynamic interface in the first place? You say, that you can't change the interface, but this looks a bit stupid. It seems, that all your context is statically typed, but then, for some reason, you have one dynamically typed method.
If you know types during compilation time (or via generic arguments in the runtime), then you should simply change your method to something like this:
public IEnumerable<T> GetByParentId<T>(string table)
{
IDbConnection _connection = _dbProvider.GetConnection();
var _sqlString = "select * from " + table;
var _ret = _connection.Query<T>(_sqlString);
return _ret;
}
Related
I have some methods that execute arbitrary SQL against a database and serialize that data collection into a list of a concrete type. That data is then serialized into JSON and stored in a cell in a table. Later, I need to come back and deserialize that data back into its original collection so that it can be used.
I'm having some issues figuring out how to take a Type object and create a collection of that type in order to deserialize it. Here is how my code operates:
public async Task ExecuteWidget(Guid runGuid, string widgetName, Type type, string sql,
IEnumerable<SqlParameter> parameters)
{
var report = operationsContext.ReportRuns.FirstOrDefault(n => n.RunGuid == runGuid);
CheckReportStatus(report);
var param = parameters.ToList();
var result = edwContext.Database.SqlQuery(type, sql, param.ToArray<object>());
var query = result.GetQuery(param);
var data = await result.ToListAsync();
var widgetData = new ReportRunWidgetData()
{
ReportRunId = report?.ReportRunId ?? -1, // This should never be null.
WidgetName = widgetName,
WidgetData = new JavaScriptSerializer().Serialize(data),
Query = query
};
operationsContext.ReportRunWidgetDatas.Add(widgetData);
await operationsContext.SaveChangesAsync();
}
My fetching logic looks something like this:
public object FetchWidgetData(Guid runGuid, string widgetName, Type dataType)
{
var data = operationsContext.ReportRuns
.Include("ReportRunWidgetDatas")
.FirstOrDefault(n => n.RunGuid == runGuid)?
.ReportRunWidgetDatas.FirstOrDefault(n => n.WidgetName == widgetName)?
.WidgetData;
if (data == null) return null;
var deserialized = new JavaScriptSerializer().Deserialize(data, dataType);
return deserialized;
}
Now when the ExecuteWidget method is called, the type parameter is populated by the widget's DTO datatype. For example HeadlineWidgetDTO. However, the execute command gets the data back as a List<HeadlineWidgetDTO>. When my FetchWidgetData method is called, the dataType supplied is still HeadlineWidgetDTO, but it actually needs to be of type IEnumerable<HeadlineWidgetDTO> to deserialize properly.
Given just the type for an individual data row, how can I create a Type object that is instead a collection of that type?
This is mostly a duplicate of How to use Activator to create an instance of a generic Type and casting it back to that type?, however it's hard to tell.
Basically, if you have a type object Type theType, you need to do something like:
var listType = typeof(List<>);
var typeParams = new [] {theType};
var listOfTType = listType.MakeGenericType(typeParams);
var newListOfT = Activator.CreateInstance(listOfTType);
At that point, you have a variable of type object, but that references an object of type List<WhateverYourTypeIs>. Say, theType is typeof(int), then you will have an object of List<int>. Casting it to something usuable is a whole other question though. If you want to add something to that list, I suspect the best way would be to get a MethodInfo for the Add method and Invoke it.
I thought of another way to do this if the type has a default constructor and isn't too expensive to create. Here's a sample (creating a List<int> - but that's just the way I have it coded):
var type = typeof(int);
var dummy = Activator.CreateInstance(type);
var listOfType = new[] {dummy}.ToList();
When you are finished, the listOfType variable is typed as a List<object> but refers to a List<int>. It's mostly mostly workable - for example, you can call Add(object someObj) on it. You won't get compile type parameter type checking, but you will be able to use it.
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);
I have a stored procedure call interface that I'm using to handle results from stored procedures with entity (using Translate method to translate the results of our stored procedure into entities that can be tracked and used in EF as normal)
Here's the basic code...
List<object> current = new List<object>();
object item = ((Type)currenttype.Current).GetConstructor(System.Type.EmptyTypes).Invoke(new object[0]);
ObjectContext actualContext = ((IObjectContextAdapter)context).ObjectContext;
string className = "";
EntityContainer container = null;
string setName = "";
className = ((Type)currenttype.Current).ToString();
container = actualContext.MetadataWorkspace.GetEntityContainer(((IObjectContextAdapter)context).ObjectContext.DefaultContainerName, DataSpace.CSpace);
setName = (from meta in container.BaseEntitySets
where meta.ElementType.FullName == className
select meta.Name).FirstOrDefault();
var t = typeof(ObjectContext).GetMethod("Translate", new Type[] { typeof(DbDataReader), typeof(string), typeof(MergeOption) }).MakeGenericMethod(item.GetType()).Invoke(actualContext, new object[] { reader, setName, MergeOption.AppendOnly });
The issue is that I can't do anything with 't' that I want, it's type is listed as
object {System.Data.Entity.Core.Objects.ObjectResult<POCOClass>}. I can't call any of the normal methods that I can normally on the ObjectResult type such as ToArray or ToList.
I need a way to convert it into System.Data.Entity.Core.Objects.ObjectResult<POCOClass>. The difference being that 't' is listed as type object first.
I cannot use any strongly typed casts because the types will change depending on the stored procedure. I've tried using the dynamic keyword instead of var for t and I've also tried using Convert.ChangeType. It never changes from the object base type. dynamic t returns this the following error:
'System.Data.Entity.Core.Objects.ObjectResult<POCOClass>' does not contain a definition for 'ToList'
Thought I know for a fact it does...
To clear up confusion, here's a watch screenshot. The first line is what's being returned, I want it to be like the second (see Type column).
Edit: might be getting closer... I added this:
var listedT = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(item.GetType()).Invoke(null, new object[] { t });
current.AddRange(listedT); // Error here...
listedT becomes a object {System.Collections.Generic.List<ReportCatalog.Models.Catalog_Reports>} and I get the error, cannot convert from object to System.Collections.Generic.IEnumerable<object>.
Since ObjectResult<T> implements also the non-generic IEnumerable interface, cast it to this type and enumerate it.
var e = (IEnumerable)t;
foreach (object o in e) {
//TODO: use o
}
I'm new to C#. I want to take a list as argument and return another from the data I get from the first one.
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = new List<DestinationGenericMapProps>(datas);
return result;
}
I get this error:
Error 241 The best overloaded method match for System.Collections.Generic.List<VDDataUpdaterGeneric.DataObjects.DestinationGenericMapProps>.List(int) has some invalid arguments
I know this is probably pretty basic but I'm new to C# and struggle with this. Thanks for your help.
List<BoutiqueInWebService> is not a List<DestinationGenericMapProps>.
This will not work unless BoutiqueInWebService is derived from DestinationGenericMapProps.
Basically, there is a List<T>(IEnumerable<T>) constructor, but the T's have to be the same.
Either change your return type to List<BoutiqueInWebService> and change your new statement:
private List<BoutiqueInWebService> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = new List<BoutiqueInWebService>(datas);
return result;
}
or change your parameter to be of type List<DestinationGenericMapProps>:
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<DestinationGenericMapProps> datas)
{
var result = new List<DestinationGenericMapProps>(datas);
return result;
}
Alternatively, if you know how to make a DestinationGenericMapProps from a BoutiqueInWebService, you can use System.Linq and perform a select against the argument:
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = datas.Select(x => new DestinationGenericMapProps() { ... }).ToList();
return result;
}
Your method return type is a list of DestinationGenericMapProps, but you're trying to create list of BoutiqueInWebService (which is data).
You can do this to match your return type:
private List<DestinationGenericMapProps>
ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
return (from d in datas
select new DestinationGenericMapProps()
{
// map properties here
Prop1 = d.SomePropInData
}).ToList();
}
You're getting the error because you're trying to populate a list of one type (DestinationGenericMapProps) with objects from a list of a different type (BoutiqueInWebService) which isn't type safe.
You can only do this if BoutiqueInWebService inherits from DestinationGenericMapProps.
C# supports function overloading, which means that a class can have more than one function with the same name as long as the parameters are different. The compiler decides which overload to call by compairing the types of the parameters. This applies to constructors too.
The List class has a three overloads of its constuctor:
List<T>()
List<T>(IEnumerable<T>)
List<T>(int)
I assume that you are trying to use the second of those as it will create a new list from the passed in one. For the list you are creating T is a DestinationGenericMapProps. So the constructors are:
List<DestinationGenericMapProps>()
List<DestinationGenericMapProps>(IEnumerable<DestinationGenericMapProps>)
List<DestinationGenericMapProps>(int)
The list you have passed in has T set to BoutiqueInWebService. As such the compiler is trying to find a constructor like this in the list above.
List<DestinationGenericMapProps>(IEnumerable<BoutiqueInWebService>)
As it can't find one it raises the error you have recieved.
Is it possible to cast a BoutiqueInWebService to a DestinationGenericMapProps object? If so you could do this:
var result = datas.Cast<DestinationGenericMapProps>().ToList()
If no direct cast is possible it may be possible to do a long hand cast like this:
var result = datas.Select(o => new DestinationGenericMapProps() { PropA = o.PropA, PropB = o.PropB /* etc */}).ToList();
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I pass an anonymous type to a method?
I have the following LINQ Statement, whose output has to be processed in another method:
var data = from lines in File.ReadAllLines(TrainingDataFile)
.Skip(ContainsHeader ? 1 : 0)
let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
let target = f[TargetVariablePositionZeroBased]
select new { F=f, T=target };
What should be the datatype of the parameter in the method that will take this data?
You can not return the anonymous data types from a method. You can define a class and return object of that class from query and pass it to target method.
public class SomeClass
{
public string F {get; set;}
public string T {get; set;}
}
var data = from lines in File.ReadAllLines(TrainingDataFile)
.Skip(ContainsHeader ? 1 : 0)
let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
let target = f[TargetVariablePositionZeroBased]
select new SomeClass { F=f, T=target };
You can pass the query result IEnumerable<SomeClass> to method as parameter.
public void MethodToCall(IEnumerable<SomeClass> someClass)
{
}
To call the method by passing the query result (IEnumerable<SomeClass>) that is stored in data in this sample code
MethodToCall(data);
You can't very easily pass anonymous types around. You can either create a class, or since your data has only two properties, use a Tuple:
select new Tuple<List<string>, string> (f, target);
If I have the data types correct, then the data type of the parameter would be:
IEnumerable<Tuple<List<string>, string>>
and you would reference F and T using the Tuple properties Item1 and Item2.
1) Just to pass the result of the query, make your function generic, that will do:
var data = from lines in File.ReadAllLines(TrainingDataFile)
.Skip(ContainsHeader ? 1 : 0)
let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
let target = f[TargetVariablePositionZeroBased]
select new { F=f, T=target };
SomeMethod(data);
public void SomeMethod<T>(IEnumerable<T> enumerable)
{
// ^^choose the return type..
}
Simple. If the processing inside the method is something so simple this will do. But you won't be able to access properties F and T inside the method.
To do so:
2) You can use the "cast by example" trick shown here by Eric. To quote him:
We use method type inference and local variable type inference to tell
the compiler "these two things are the same type". This lets you
export an anonymous type as object and cast it back to anonymous type.
...the trick only works if the example and the source objects were
created in code in the same assembly; two "identical" anonymous types
in two different assemblies do not unify to be the same type.
SomeMethod(data);
public void SomeMethod(IEnumerable<object> enumerable)
{
var template = new { F = new List<string>(), T = string.Empty };
foreach (var item in enumerable)
{
var anonymousType = item.CastToTypeOf(template);
//print string.Join(", ", anonymousType.F) + " - " + anonymousType.T //compiles
//or whatever
}
}
//a more generic name perhaps is 'CastToTypeOf' as an extension method
public static T CastToTypeOf<T>(this object source, T example) where T : class
{
return (T)source;
}
The catch here is that SomeMethod now is tailor made for your anonymous type, since you're specifying a specific type inside the method, so its better to not make the function generic (though you can do) and to give a suitable name for the function.
3) If function is just for your unique type now, I would better have them all wrapped in a single method and not pass at all - no hassle! :)
4) Or you can delegate the action to be done on your anonymous type. So method signature would be like:
SomeMethod(data, d => print string.Join(", ", d.F) + " - " + d.T);
public void SomeMethod<T>(IEnumerable<T> enumerable, Action<T> actor)
{
foreach (var item in enumerable)
actor(item);
}
If it matters you can have Func delegate as well by having one more type argument.
5) Rely on fiddly reflection to get the properties from your anonymous type otherwise.
6) Use dynamic keyword on method argument and now you have dynamic typing. Both the above doesnt give you benefits of static typing.
7) You will be better off having a separate class that holds F and T. And that the best of all. But ask yourself do they together represent something as an entity?
8) If not, just pass an IEnumerable<Tuple> or IDictionary depending on what matters.
It all depends on what/how you want to achieve with the method. Personally, I would go for the approach 2 in a hobby project (for the fun involved), but in production code 3, 4, 7, 8 depending on the context.