Using Reflection and LINQ to query the ApplicationDataService - c#

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.

Related

How to use the where clause with IQueryable<dynamic>

I am using Entity Framework Core with reflection to generate some forms dynamically. Everything is working except the WHERE Clause. I get the following error:
An expression tree may not contain a dynamic operation
I am able to fix this by converting my IQueryable to a List, but that introduces different problems that i would like to avoid.
Here is my code:
public async void ViewCollection(PropertyInfo propertyInfo)
{
Type propertyType = propertyInfo.PropertyType;
InversePropertyAttribute inversePropertyAttribute = (InversePropertyAttribute)ReflectionHelpers.GetAttribute(propertyInfo, typeof(InversePropertyAttribute));
//GET THE TYPE OF THE COLLECTION
Type collectionType = propertyInfo.PropertyType.GenericTypeArguments[0];
//GET THE INVERSE PROPERTY INFO
PropertyInfo inverseProperty = collectionType.GetProperty(inversePropertyAttribute.Property);
//GET THE FOREIGN KEY ATTRIBUTE FROM THE INVERSE PROPERTY
ForeignKeyAttribute foreignKeyAttribute = (ForeignKeyAttribute)ReflectionHelpers.GetAttribute(inverseProperty, typeof(ForeignKeyAttribute));
//GET THE FOREIGN KEY PROPERTY FROM THE FOREIGN KEY ATTRIBUTE
PropertyInfo foreignKeyProperty = collectionType.GetProperty(foreignKeyAttribute.Name);
//GET INCLUDED TYPE NAMES BY FOREIGN KEY
IEnumerable<string> includedTypes = collectionType.GetProperties().Where(p => p.PropertyType.IsClass).Where(p => ReflectionHelpers.HasAttribute(p, typeof(ForeignKeyAttribute))).Select(r => r.Name);
//GET THE DATA SET
IQueryable<dynamic> items = ReflectionHelpers.GetDbCollectionByType(Db, collectionType);
//INCLUDE THE INCLUDED TYPES BY NAME
foreach (string includedType in includedTypes) items = items.Include(includedType);
//THIS IS WHERE THE ERROR IS
items = items.Where(i => foreignKeyProperty.GetValue(i, null) == PrimaryKeyProperty.GetValue(Item, null));
await ShowCollection(collectionType, items, propertyInfo.Name);
}
How can i solve this with out changing my type to a list?
You can't. IQueryable is part of language integrated query (LINQ) - it's statically typed, so you cannot use value types.
IQueryable behind the scenes is an expression tree that represents a query that has not yet been executed. The expression tree part represents the query. The static types represent the data that the query operates over.
Your best alternative is to build the expression tree by hand using the expression tree API.
You can't build queries over dynamic because it's not supported natively in expression trees. Instead you should base your work on either non generic IQueryable or generic IQueryable<object>. e.g. if the ReflectionHelpers.GetDbCollectionByType is calling DbContext.Set<T> dynamically (similar to here Dynamically access table in EF Core 2.0), you should be able to cast it to IQueryable<object>:
var items = (IQueryable<object>)ReflectionHelpers.GetDbCollectionByType(Db, collectionType);
To add Where clause, you shouldn't use reflection calls inside the predicate expression, but build it dynamically using the Expression class methods (from System.Linq.Expressions namespace). Something like this:
// (object i) => (({collectionType})i).{ForeignKey} == Item.{PrimaryKey}
var parameter = Expression.Parameter(typeof(object), "i");
var body = Expression.Equal(
Expression.Property(Expression.Convert(parameter, collectionType), foreignKeyProperty),
Expression.Property(Expression.Constant(Item), PrimaryKeyProperty));
var predicate = Expression.Lambda<Func<object, bool>>(body, parameter);
and then
items = items.Where(predicate); // still IQueryable<object>

Dynamically casting an Entity Model from SQL query results

I need to dynamically cast a SQL return value to a model like this
var data = _db.Query<myModel>(myStoredProcedure, p, commandType: System.Data.CommandType.StoredProcedure);
The above works as it should as the model is explicit in the casting.
However, myModel could be any type of model class that I have in my project. I am able to get the actual model through reflection and the use of a string variable called "TableName"
I thought I could go this routine to set it but I get the error of 'entityObject is a variable but used like a type'
//First we need to find the project that holds all of our entity models in the assembly
var assembly = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.FullName.Contains("MyProject.Models")).FirstOrDefault();
//Now we need to search through the assembly to match the Entity to the supplied TableName
var type = assembly.GetTypes()
.FirstOrDefault(t => t.Name == localTableName);
//Once found then we create a dynamic instance of the entity using reflection
if (type != null)
{
var ctx = new MyProject.Models.Entities();
//Create the DBSet here
System.Data.Entity.DbSet myDbSet = ctx.Set(type);
//Now create the actual entity reference which is just an object at this point
var entityObject = myDbSet.Create();
--> Errors here var data = _db.Query<entityObject>(myStoredProcedure, p, commandType: System.Data.CommandType.StoredProcedure);
}
Should I use ExpandoObject? If so how can convert the expandoobject into the entityClass?

Get strongly typed Entity Framework entities using reflection

My question: is it possible - and if so, how? - to have results from a query using Entity Framework populated with fully typed objects when initialized using reflection?
The conditions are the context and entities must be able to be in an external dll not referenced directly in the project.
I do not want to have to manually loop through the object graph and reflect all types.
This is the code I have currently.
Assembly metaAssembly = AppDomain.CurrentDomain.GetAssemblies().
SingleOrDefault(assembly => assembly.GetName().Name == "Data_Meta");
TypeHelper myTypeHelper = new TypeHelper();
Type dbContextType = myTypeHelper.FindDerivedTypes(metaAssembly, typeof(System.Data.Entity.DbContext)).ToList().FirstOrDefault();
using (var ctx = (DbContext)Activator.CreateInstance(dbContextType))
{
ctx.Configuration.LazyLoadingEnabled = false;
var curEntityPI = ctx.GetType().GetProperties().Where(pr => pr.Name == "Worker").First();
var curEntityType = curEntityPI.PropertyType.GetGenericArguments().First();
var set = ctx.Set(curEntityType);
var t = set.ToString();
Type generic = typeof(DataAccess.Models.Repository.EF.GenericEfDataRepository<,>);
Type[] typeArgs = { curEntityType, dbContextType };
Type constructed = generic.MakeGenericType(typeArgs);
MethodInfo methodInfo = constructed.GetMethod("GetAll");
object repositoryInstance = Activator.CreateInstance(constructed, new object[] { ctx });
var navigationPropertyType = typeof(Expression<>).MakeGenericType(
typeof(Func<,>).MakeGenericType(curEntityType, typeof(object)));
var navigationProperties = Array.CreateInstance(navigationPropertyType, 0);
var result = methodInfo.Invoke(repositoryInstance, new object[] { navigationProperties });
}
I can generate a database query and get the correct number of results, however items in the list returned to do not evaluate to a type unless I put a reference in the project to the dll which defeats my purpose. I should say I am resolving the location of the dll and loading it via the AppDomain.CurrentDomain.AssemblyResolve event.
Many thanks

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

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?

Linq2SQL - selecting items using reflection

I'm trying to load a collection of entities using Linq2SQL. The problem is, I dont know what the entities are,IList<object>. I have tried to select them using reflection, but I get an out of memory error when I do the select, I presume because the context is unable to parse my expression,and is loading everything from the DB.
If anyone has any advice on this, or an alternative way to do what I want, please let me know.
foreach (object entity in requiredEntities)
{
Type entityType = entity.GetType();
IQueryable<object> entityTable = (IQueryable<object>)dataContext.GetTable(entityType);
// grab the objects primary key field
var pkeyField = entityType.GetProperties().SingleOrDefault(p =>
p.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute),true)
.Cast<System.Data.Linq.Mapping.ColumnAttribute>()
.Any(attrib => attrib.IsPrimaryKey));
object pkeyValue = pkeyField.GetValue(entity,null);
Func<object,bool> primaryKeySelector = o => pkeyField.GetValue(o,null) == pkeyValue;
// crash here, out of memory exception
object result = entityTable.Where(primaryKeySelector).SingleOrDefault();
}
By using a delegate you are forcing it to use LINQ-to-Objects, which is why it is running out of memory. What you need to do is build an Expression instead. Equally, it is bad practice to use the attributes as that is not the only model that LINQ-to-SQL supports; it is preferable to look at dataContext.Mapping.GetMetaType(entityType) to get the primary key.
If you have 4.0, the following should work:
var entityType = typeof(User);
var metaType = dataContext.Mapping.GetMetaType(entityType);
var member = metaType.DataMembers.Single(m => m.IsPrimaryKey).Member;
var param = Expression.Parameter(entityType);
var body = Expression.Equal(Expression.MakeMemberAccess(param, member),
Expression.MakeMemberAccess(Expression.Constant(entity), member));
dynamic table = dataContext.GetTable(entityType);
object result = Cheeky(table, body, param);
with
static T Cheeky<T>(ITable<T> source, Expression body, ParameterExpression param)
where T : class
{
var predicate = Expression.Lambda<Func<T, bool>>(body, param);
return source.SingleOrDefault(predicate);
}

Categories