C# Linq using variable for Model - c#

Trying to write a piece of code that is dynamic in that it can accept any number of possible model definitions.
Current hard code is:
var items = _context.Events.ToList();
foreach (var item in items)
{
(...)
}
What I would like to do is to make the _context.Events.ToList(); be more like _context.{variable that holds model name}.ToList();
Something like:
var modelName = "Table1"
var items = _context.modelName.ToList();
foreach (var item in items)
{
(...)
}
I thought about declaring items as a generic variable, that way it was available to the entire method even if set inside an if or switch, but no idea on what to declare it as.
Is something like this possible?

Try this :
var table = (ITable)context.GetType()
.GetProperty(modelName)
.GetValue(context, null);
I hope be helpful :)

Entity Framework has a generic Set<TEntity> accessor, but the type must be known at compile-time:
var foo = _context.Set<Foo>();
If you have the type as a string variable, though, your options are extremely limited. You can technically use reflection to get at the right DbSet, but you're going to lose the generic IQueryable<TEntity> interface, and you'll be stuck with the much more limited IQueryable interface, and by "much more limited", I mean you basically can't do anything but materialize the set.
If you want to type it via a string variable, but still have at least some querying ability, you'll need to employ a base class that's shared between all the entity types you'd want to use in this way. For example, if you have different "event" types, and you can make them all inherit from Event, then you could do something like:
MethodInfo method = typeof(Queryable).GetMethod("OfType");
MethodInfo generic = method.MakeGenericMethod(new Type[] { eventType });
var set = (IQueryable<Event>)generic.Invoke(null, new object[] { _context.Events });
If your eventType was "Film", for example, that would effectively give you the same queryset as something like _context.Set<Film>() (where Film would be a derived class of Event). You could then utilize your normal LINQ query functionality like Where, Select, etc. on set.

Related

Dynamically get a DbSet<T> by Entity class name

I'm trying to use System.Reflections to get a DbSet<T> dynamically from its name.
What I've got right now is:
The DbSet name
The DbSet's Type stored on a variable
The issue I'm facing comes out when trying to use the dbcontext.Set<T>() method, since (these are my tries so far):
When I try to assign to <T> my DbSet Type, it throws me the following compilation error:
"XXX is a variable but is used like a type"
If I try with using both the Extension methods that you will find below in my code (which I made in order to try to get an IQueryable<T>), it returns a IQueryable<object>, which unfortunately is not what I am looking for, since of course when I try to manipulate it with further Reflections, it lacks of all the properties that the original class has…
What am I doing wrong? How can I get a DbSet<T>?
My code is the following, but of course, let me know if you need more infos, clarifications or code snippets.
My Controller's Method:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//FIRST TRY
//throws error: "_type is a variable but is used like a type"
var tableSet = _context.Set<_type>();
//SECOND TRY
//returns me an IQueryable<object>, I need an IQueryable<MyType>
var tableSet2 = _context.Set(_type);
//THIRD TRY
//always returns me am IQueryable<object>, I need an IQueryable<MyType>
var calcInstance = Activator.CreateInstance(_type);
var _tableSet3 = _context.Set2(calcInstance);
//...
}
Class ContextSetExtension
public static class ContextSetExtension
{
public static IQueryable<object> Set(this DbContext _context, Type t)
{
var res= _context.GetType().GetMethod("Set").MakeGenericMethod(t).Invoke(_context, null);
return (IQueryable<object>)res;
}
public static IQueryable<T>Set2<T>(this DbContext _context, T t)
{
var typo = t.GetType();
return (IQueryable<T>)_context.GetType().GetMethod("Set").MakeGenericMethod(typo).Invoke(_context, null);
}
}
EDIT Added TypeFinder's inner code.
In brief, this method does the same of Type.GetType, but searches Type on ALL the generated assemblies
public class TypeFinder
{
public TypeFinder()
{
}
public static Type FindType(string name)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
var result = (from elem in (from app in assemblies
select (from tip in app.GetTypes()
where tip.Name == name.Trim()
select tip).FirstOrDefault())
where elem != null
select elem).FirstOrDefault();
return result;
}
}
UPDATE as requested in the comments, here's the specific case:
In my DB i've got some tables which are really similar each other, so the idea was to create a dynamic table-update method which would be good for every table, just passing to this method the table name, the ID of the row to update and the JSON containing data to update.
So, in brief, I would perform some updates on the table given in input as DbSet type, updating the row with ID==id in input with the data contained inside the JSON, which will be parsed inside an object of type X(the same of dbset)/into a dictionary.
In pseudo-code:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//THIS DOESN'T WORKS, of course, since as said above:
//<<throws error: "_type is a variable but is used like a type">>
var tableSet = _context.Set<_type>();
//parsing the JSON
var newObj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonupdate, _type);
//THIS OF COURSE DOESN'T WORKS TOO
//selecting the row to update:
var toUpdate = tableSet.Where(x => x.Id == id).FirstOrDefault();
if(toUpdate!=null)
{
var newProperties = newObj.GetType().GetProperties();
var toUpdateProperties = toUpdate.GetType().GetProperties();
foreach(var item in properties)
{
var temp = toUpdateProperties.Where(p => p.Name==item.Name)
{
//I write it really in briefand fast, without lots of checks.
//I think this is enough, I hope
temp.SetValue(toUpdate, item.GetValue());
}
}
_context.SaveChanges();
}
return false;
}
returns me an IQueryable<object>, I need an IQueryable<MyType>
Well, that will never work. Your IQueryable cannot be of type IQueryable<MyType>because that would mean the compiler would need to know what MyType is and that is not possible, because the whole point of this exercise is to decide that on runtime.
Maybe it's enough to know that those objects are in fact instances of MyType?
If not, I think you have painted yourself into a corner here and you are trying to figure out what paint to use to get out of there. Take a step back, it's probably not a technical problem. Why do you need to do this? Why do you have the conflicting needs of knowing the type at runtime only and knowing it at compile time?
You need to think about your requirements, not about the technical details.
I needed to dynamically load a single record from the database for each type in a list of known types, to print a test email when an admin is editing the template, so I did this:
List<object> args = new List<object>();
//...
//other stuff happens that isn't relevant to the OP, including adding a couple fixed items to args
//...
foreach (Type type in EmailSender.GetParameterTypes())
{
//skip anything already in the list
if (args.Any(a => a.GetType().IsAssignableFrom(type))) continue;
//dynamically get an item from the database for this type, safely assume that 1st column is the PK
string sql = dbContext.Set(type).Sql.Replace("SELECT", "SELECT TOP 1") + " ORDER BY 1 DESC";
var biff = dbContext.Set(type).SqlQuery(sql).AsNoTracking().ToListAsync().Result.First();
args.Add(biff);
}
Caveat: I know at least one record will exist for all entities I'm doing this for, and only one instance of each type may be passed to the email generator (which has a number of Debug.Asserts to test validity of implementation).
If you know the record ID you're looking for, rather than the entire table, you can use dbContext.Set(type).Find(). If you want the entire table of whatever type you've sussed out, you can just do this:
string sql = dbContext.Set(type).Sql; //append a WHERE clause here if needed/feasible, use reflection?
var biff = dbContext.Set(type).SqlQuery(sql).ToListAsync().Result;
Feels a little clunky, but it works. There is strangely no ToList without Async, but I can run synchronously here. In my case, it was essential to turn off Proxy Creation, but you look like you want to maintain a contextful state so you can write back to db. I'm doing a bunch of reflection later, so I don't really care about strong typing such a resulting collection (hence a List<object>). But once you have the collection (even just as object), you should be able to use System.Reflection as you are doing in your UPDATE sample code, since you know the type and can use SetValue with known/given property names in such a manner.
And I'm using .NET Framework, but hopefully this may translate over to .NET Core.
EDIT: tested and working:
public async Task<bool> MyMethod(string _type)
{
Type type = Type.GetType(_type);
var tableSet = _context.Set(type);
var list = await db.ToListAsync();
// do something
}
// pass the full namespace of class
var result = await MyMethod("Namespace.Models.MyClass")
IMPORTANT NOTE: your DbContext need to have the DbSet declared to work!
public class MyContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
}

Handling lambda expression with generic property name

Working with EPiServer Find and trying to build a generic facet funcionality for it to simplify managing which facet should be enabled. I would like to construct two generic methods, one for adding active filters to perform the hits search and one to perform the available facet filters remaining.
The first method will perform the following (specific code for brand filter):
var brandFilter = client.BuildFilter<FashionProduct>();
foreach (var facet in SelectedGroup.Facets.Where(x => x.Selected))
{
brandFilter = brandFilter.Or(x => x.Brand.Match(facet.Key));
}
query = query.Filter(brandFilter);
I would like to be able to call it in a generic way so I could base the available facets on some simple list of strings or objects. Like this:
query = AddFilterFacet<FashionProduct>(query, "Brand", SelectedGroup.Facets)
So the method would take the type of object to filter on, the query to append the filters on, the name of the property to filter on and the list of values to add.
The second method is similar but relates more to perform the following:
facetQuery = facetQuery.TermsFacetFor(x => x.Brand)
...
var brandFacets = facetResult.TermsFacetFor(x => x.Brand).Terms;
Is it possible to build this kind of functionality? The biggest questionmark I have is how to translate the "Brand" input string to be the Brand Property in x => x.Brand
private void AddFilterFacet<T>(IClient client, ref ITypeSearch<T> query, string propertyName, List<FacetOption> facets)
{
var filter = client.BuildFilter<T>();
foreach (var facet in facets)
{
filter = filter.Or(x => x.????.Match(facet.Key));
}
query = query.Filter(filter);
}
The .Or method takes a
System.Linq.Expressions.Expression<Func<T, Find.Api.Querying.Filter>>
so perhaps something can be used to make a proper generic call to it
It's definitely possible to create generic lambda expressions, it's just not easy and requires a lot of reflection code.
I haven't done it in a while, but maybe if you look at the code i created for something similar a while ago (Generic lambda expressions) it'll help. I'm sure someone with fresher experience will help you out here soon enough.
Decimal precision attribute <-- take a look a this answer witch has code to genereate modelBuilder.Entity<CLASS>().Property(OBJECT=> OBJECT.PROPERTY).HasPrecision(12, 10) automatically from an attribute in a class

C#, Generics, Type and NHibernate

I'm learning the power of generics in C# in conjunction with NHibernate. I'd like to attempt the following in the pasted code.
In an attempt to do some post processing of N number of NHibernate objects I worked on a utility method leveraging generics to make it applicable to all NHibernate mapping classes we use now, or in the future. It works but I need to hard code each call for each mapping class. This is a pain and will need continuing updating as our schema and mappings change over time.
I do have an ever up-to-date list of all mapping classes by string name through the NHibernate mappings I generate on the fly. If there was a way to use this list of string names to call my generics based method, I'd be super happy.
Can anyone tell me if this is possible? Do I need to find another route?
Thanks so much in advance!!!
public static void ProcessSomeItems()
{
// *************************************************************
// As of now I have to list all classes as such to be processed
// It works but I have to update manually when new mapping classes are created
// *************************************************************
NHibDoSomethingUtil<AspnetMembership>();
NHibDoSomethingUtil<AspnetProfile>();
NHibDoSomethingUtil<AspnetRole>();
NHibDoSomethingUtil<AspnetUser>();
// and so forth...
// I have a up-to-date list of all mappings from "HbmMapping" and can get a list of all in the
// list form as below
List<string> mappingNames = new List<string>();
foreach (string mappingName in mappingNames)
{
Type theType = Type.GetType(mappingName);
// I know I'm getting Types and Generics classes and so forth all jumbled but
// how in the heck would I do something like the below?
NHibDoSomethingUtil<theType>(); // Obviously doesn't compile ;-)
}
}
// Generic method
public static void NHibDoSomethingUtil<T>() where T : class
{
using (ISession session = sourceDBSessionFactory.OpenSession())
{
foreach (dynamic item in new List<T>(session.QueryOver<T>().List()))
{
// Process item;
}
}
}
ecsousa gave great input and I was able to accomplish what I needed with something like the following.
foreach (HbmClass mappingClass in mapping.Items)
{
Console.WriteLine(" -- Discovered Mapping: " + mappingClass.Name);
Type mappingClassType = Type.GetType(mappingClass.Name);
var genericMethod = typeof(Migration).GetMethod("NHibDoSomethingUtil");
var method = genericMethod.MakeGenericMethod(mappingClassType);
method.Invoke(null, null);
}
You will need to use Reflection in order to accomplish this. Instead of directly calling NHibDoSomethingUtil, try this:
var genericMethod = typeof(TheClassName).GetMethod("NHibDoSomethingUtil");
var method = genericMethod.MakeGenericMethod(theType);
method.Invoke(null, null);
Note that you have to replace TheClassName by the class containing both methods.
Keep in mind the this kind of code is slow, and you should use it very carefully.

Strongly typed object from string type name (C#)

if you take a look at the following code, you will (hopefully) see what I am trying to archieve. Basically this code does:
A query for generic storag items (they store their type as string)
If the item is a subclass of SearchCriteria, create the correct instance
Add the instance to the list (SearchCriteria is superclass)
Not very elegant is, of course, the pseudo-switch case, which I would have to update for all different criteria I create.
So, my question, is there a "generic" way to create an instance which is strongly typed using a string as "source" for the type.
I know I can use Reflection to create an instance, but this is of type object, so I would not be able to add it to the list. Oh, just got an idea... Create object using reflection, cast it to supertype (SearchCrit), add to list. Real type should still be the "correct subtype" I hope...
Will try it, and update this post with results. Any better ideas?
Chris
private IList<SearchCriteria> _searchCriteriaAll;
public IList<SearchCriteria> SearchCriteriaAll
{
get
{
if (_searchCriteriaAll == null)
{
_searchCriteriaAll = new List<SearchCriteria>();
var tN = typeof (SearchCriteria).ToString();
foreach (var o in DataStorage.LinkedObjects)
{
if (tN.StartsWith(o.TypeName))
{
if (o.TypeName == typeof(StringSearchCriteria).ToString())
_searchCriteriaAll.Add(new StringSearchCriteria(o));
}
}
}
return _searchCriteriaAll;
}
}
EDIT:
Thanks for the tips, the "correct" way would definitly be the factory pattern. I will look into that. For now, I use this hack, because the subclasses are so small, I dont want a factory for each one.. (and this place is currently the only one with such a "fancy" feature)
private IList<SearchCriteria> _searchCriteriaAll;
public IList<SearchCriteria> SearchCriteriaAll
{
get
{
if (_searchCriteriaAll == null)
{
_searchCriteriaAll = new List<SearchCriteria>();
var tN = typeof (SearchCriteria).ToString();
foreach (var o in DataStorage.LinkedObjects)
{
if (tN.StartsWith(o.TypeName))
{
var newO = Activator.CreateInstance(typeof(SearchCriteria).Assembly.FullName, o.TypeName);
var newCrit = newO.Unwrap() as SearchCriteria;
newCrit.DataStorage = o;
_searchCriteriaAll.Add(newCrit);
}
}
}
return _searchCriteriaAll;
}
}
Generics and reflection don't make good friends. A simpler approach here is to use the non-generic list interface:
_searchCriteriaAll = new List<SearchCriteria>();
IList list = (IList) _searchCriteriaAll;
...
Type type = typeof(SearchCriteria).Assembly.GetType(o.TypeName);
list.Add(Activator.CreateInstance(type));
(where o.TypeName includes the namespace information, but doesn't have to be assembly-qualified)
This is still runtime type-safe (it'll throw at runtime if it is wrong), and still adjusts the same list.
Note also that we only look inside Assembly directly via Assembly.GetType().
I'd say you're looking for the Factory Method Pattern.
There's a C# sample here - the first link explains the pattern better, the second is the right language for you.
It's not entirely clear to me what you are trying to achieve, but you can create a Type from a string like this:
var t = Type.GetType(typeName);
If you want to examine whether it's a proper subtype, you can use the IsAssignableFrom method.

List to Anonymous Type in c#

The short story: I want to convert a list/dictionary into an anonymous object
Basically what I had before was:
var model = from item in IEnumerable<Item>
select new
{
name = item.Name
value = item.Value
}
etc.
If I have name, item.Name in a list or dictionary, how can I go about creating the same anonymous object model?
Edit: Clarification:
If the dictionary contains [name, item.Name] and [value, item.Value] as Key Value pairs, how would I go about creating the model without assuming that you know neither name nor value?
Since a List<T> implements IEnumerable<T> your existing code should work exactly the same way:
var model = from item in yourList
select new { name = item.Name };
For a Dictionary<TKey,TValue> you could simply do this:
var model = from item in yourDictionary
select new {
name = item.Key
value = item.Value
};
This works because Dictionary<TKey,TValue> implements IEnumerable<KeyValuePair<TKey,TValue>> so in the second expression item will be typed as KeyValuePair<TKey,TValue> which means you can project a new type using item.Key and item.Value.
It sounds like you just want to get the values from a dictionary? If so, MyDictionary.Values should do the trick for you.
If you want to construct, somewhere else, another anonymous object, that is type-compatible with the one you generate from your IEnumerable<Item>, you can do that by ensuring that the anonymous type you construct has:
The same number of members
Members with the same type
Members with the same name
... in the same order
If you do that, they will map to the same single generated type.
Now, why you would want to do that is beyond me, so I'm pretty sure I didn't understand your question. You should post more information about what you want to do.
Anyway, here's the code to produce an anonymous object that is type-compatible with the one from your IEnumerable:
var x = new
{
name = dict["name"],
value = dict["value"]
};
Since this obeys all the rules above, it will be of the same type as your objects generated from your Linq query.

Categories