How to dynamically get the results from `SqlQuery<T>` using reflection - c#

I get the name of the entity that I want to query at runtime so I use reflection to call SqlQuery<T>:
Assembly assm = typeof(DataAccessLayer.MyContext).Assembly;
Type myType = assm.GetTypes().SingleOrDefault(type => type.Name.Equals("Clients"));
MethodInfo genericMethod = typeof(System.Data.Entity.Database).GetMethods()
.Where(method => method.Name == "SqlQuery" && method.IsGenericMethod ==
true).FirstOrDefault();
MethodInfo generic = genericMethod.MakeGenericMethod(myType);
so this work fine I set my params - object[] args = { SomeQuery, new object[] { } };
and then I Invoke it - generic.Invoke(db.Database, args);.
The problem is when I try to get the actual records. I try to assign the invocation to a variable like this :
var records = generic.Invoke(db.Database, args);
but records is type of object and in fact the value of records is the sql query that I've passed in records the invokation. The Visual Studio is giving me option to load the results but it seems that it can be done only manually.
Then I tried with with using dynamic like :
dynamic records = generic.Invoke(db.Database, args);
which produce the same result, first records holds the string representation of my sql query, when I load the result I get the records as dynamic proxies but that's all. It's a little bit better since I can use foreach on records when the type is dynamic but I can't get nothing from this dynamic proxy that I get from the foreach.
At last I tried this :
Assembly assm = typeof(DataAccessLayer.BankAttachmentsEntities).Assembly;
Type myType = assm.GetTypes().SingleOrDefault(type => type.Name.Equals("Clients"));
var listType = typeof(List<>).MakeGenericType(myType);
var list = Activator.CreateInstance(listType);
Hoping that this will give me an instantiated variable of type List<Clients> but it seems not to be the case. Still it's recognized as object and I can't use ToList or other method that will load the result automatically.
How can I get access to the records fetched like this?

Related

C# Covert Type into IEnumerable of same Type?

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.

What is the difference between the two Casts for a Dynamic Object in Linq? [duplicate]

I am working with .NET4.5 and VS2013, I have this query that gets dynamic result from db.
dynamic topAgents = this._dataContext.Sql(
"select t.create_user_id as \"User\", sum(t.netamount) as \"Amount\" from transactiondetail t where t.update_date > sysdate -7 group by t.create_user_id")
.QueryMany<dynamic>();
Following statement fails with compilation error Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type
without even allowing me to run it
topAgents.ToList().Select(agent => new
{
User = agent.User != null ? string.Format("{0}", agent.User).Replace("CORPNTGB\\", "") : null,
Amount = agent.Amount
});
while this one with foreach works just fine.
var data = new List<List<object>>();
foreach (dynamic agent in topAgents)
{
data.Add(new List<object>
{
agent.User != null ? string.Format("{0}", agent.User).Replace("CORPNTGB\\", "") : null,
agent.Amount
});
}
In my eyes after I topAgents.ToList() they could be interpreted as equivalent, is it because I explicitly state that var data = new List<List<object>>(); that second statement is allowed by compiler?
Why doesn't compiler allow LINQ select, but allows for each`?
The problem is that topAgents is dynamic - so your ToList() call is dynamic, and so is Select. That has issues that:
you can't use lambda expressions for dynamic calls like this;
dynamic calls don't find extension methods anyway.
Fortunately, the operations don't need to be dynamic just because the element type is dynamic. You could use:
IEnumerable<dynamic> topAgents = ...;
... or just use var. Both of those should be fine.

C# Reflection Invoke - Returns Generic Object { Type } - Need Type

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
}

Using Reflection and LINQ to query the ApplicationDataService

I am using the VS Lightswitch ServerApplicationContext to access and modify lightswitch data entities within an ApiController.
Let's say I have a Customer entity, and i can query the collection of customers in the lightswitch db using linq:
IEnumerable<Customer> customers = from custs in serverContext.DataWorkspace
.ApplicationData
.Customers
.GetQuery()
.Execute()
where c.AProperty == aProperty
select custs;
or
IEnumerable<Customer> customers =
serverContext.DataWorkspace
.ApplicationData
.Customers
.Where(c => c.AProperty == aProperty)
.Execute();
This works perfectly.
However, I have many more entities and several projects with different entities in each project and I am trying to create a library to allow me to query ServerApplicationContext using reflection.
I have used reflection to get the properties of the ServerApplicationContext object, which gives me access to the EntitySet<T>, but I can not execute any queries against it.
This is the code as it stands:
Type t = serverContext.DataWorkspace.ApplicationData.GetType();
PropertyInfo[] pInfo = t.GetProperties();
foreach (var p in pInfo)
{
// p is equal to {Microsoft.LightSwitch.Framework.EntitySet`1[LightSwitchApplication.Customer] Customers}
MethodInfo mInfo = p.PropertyType.GetMethod("GetQuery");
var result = mInfo.Invoke(p.PropertyType, null) ; //<-- Error Here
}
The error returned is:
An exception of type 'System.Reflection.TargetException' occurred in mscorlib.dll but was not handled in user code
Additional information: Object does not match target type.
Has anyone had any joy with querying EntitySets (including where clauses) using reflection?
The part where you're trying to invoke the GetQuery MethodInfo has the wrong target - the way it's currently written it's trying to call a GetQuery method on an instance of System.Type (obtained from p.PropertyType), which isn't going to work. What you need to do is get the instance of your EntitySet<T> from serverContext.DataWorkspace.ApplicationData first, then invoke the GetQuery method on that instance.
Type t = serverContext.DataWorkspace.ApplicationData.GetType();
PropertyInfo[] pInfo = t.GetProperties();
foreach (var p in pInfo)
{
// p is equal to {Microsoft.LightSwitch.Framework.EntitySet`1[LightSwitchApplication.Customer] Customers}
MethodInfo mInfo = p.PropertyType.GetMethod("GetQuery");
var entitySet = p.GetValue(serverContext.DataWorkspace.ApplicationData); // new line
var result = mInfo.Invoke(entitySet, null); // updated line
}
For details on how to put together a dynamic Where clause against your EntitySet<T>, check out the links in this answer: https://stackoverflow.com/a/4799798/2611587.

Casting Results from Generic Method Invocation?

I'm currently messing about with generics and I'm trying to write a function that I can call to load everything from a database table simply by specifying the table name.
I'm most of the way there; my generic methods all seem to work, but I'm not quite sure how to cast my results into something usable.
This is the guts of the method so far:
private static List<EntityCodeBase> GetCodeLoadResults(CodeTables table)
{
List<EntityCodeBase> results = new List<EntityCodeBase>();
Assembly assm = Assembly.Load(new System.Reflection.AssemblyName("RR"));
Type tableType = assm.GetTypes().Where(u => u.Name.ToLower() == table.ToString().ToLower()).FirstOrDefault();
MethodInfo mi = typeof(SpecificEntity).GetMethod("LoadAll");
mi = mi.MakeGenericMethod(tableType);
mi.Invoke(null, null); //how can I cast the resulting object into a List<EntityCodeBase> ?
return results;
}
Assuming SpecificEntity.LoadAll returns a list of some type derived from EntityCodeBase, you can't cast directly to a List<EntityCodeBase> but you can cast to IEnumerable<EntityCodeBase>. Then you can create a new list:
var ecbList = (IEnumerable<EntityCodeBase>)mi.Invoke(null, null);
return list.ToList();
It might be cleaner however, if you can get the table name from the entity type, either directly by name, using attributes, or using a map. Then you can make GetCodeLoadResults generic in the result type e.g.
private static List<T> GetCodeLoadResults() where T : EntityCodeBase
{
Assembly assm = Assembly.Load(new System.Reflection.AssemblyName("RR"));
Type tableType = //get table type from T
MethodInfo mi = typeof(SpecificEntity).GetMethod("LoadAll");
mi = mi.MakeGenericMethod(tableType);
return (List<T>)mi.Invoke(null, null);
}
If you're not using .Net 4, you can't cast a List<TDerived> to an IEnumerable<TBase>, so you'll have to cast to IEnumerable first:
return ((System.Collections.IEnumerable)mi.Invoke(null, null))
.Cast<EntityCodeBase>()
.ToList();

Categories