I have the following code (example):
public dynamic GetData(string name)
{
using(var ctx = GetObjectContext())
{
switch (name)
{
case "entity1":
return ctx.entity1.ToList();
case "entity2":
return ctx.entity2.ToList();
......
default:
return null;
}
}
}
I want to avoid switch in this sample. How can I find needed entity class by name, call the ToList() method and return data? Can I do this using reflection?
You can do it using reflection, however you will also need to use generics because the type of list returned by the ToList() method is different for each entity type.
You can access a property getter through reflection like so:
var enumerable = typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null);
Whereas [ClassNameOfContext] is the name of the class that ctx is an instance of. This is not obvious from your code, but you know it :-)
The problem is that enumerable will be an object and has to be casted to IEnumerable<EntityType> where EntityType is the type of entity you are accessing. In other words, it depends on the name you are passing. If you use generics to determine the type, you will be able to properly cast the object and don't have to return a dynamic even.
public TEntity Get<TEntity>(string name)
{
...
and transform the line from above:
var enumerable = (IEnumerable<TEntity>)(typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null));
return enumerable.ToList();
here you go!
Addendum: You could, conceivably, get rid of the string parameter, too - having names of types or properties in strings should be avoided where possible because it is not type safe. The compiler does not recognize it, and IDE features such as refactorings don't account for it. The problem here is that the property names are usually the pluralized form of the entity type names. But you could use reflection to find the property whose type matches the TEntity. I leave this as an exercise :-)
You can use code like this
private IEnumerable<TEntity> GetList<TEntity>(string connectionString, Func<object, T> caster)
{
using (var ctx = new DbContext(connectionString))
{
var setMethod = ctx.GetType().GetMethod("Set").MakeGenericMethod(typeof(T));
var querable = ((DbSet<object>)setMethod
.Invoke(this, null))
.AsNoTracking()
.AsQueryable();
return querable
.Select(x => caster(x))
.ToList();
}
}
To call like this:
var branchList = GetList<Branch>("connectionStringName", x => (Branch)x);
You can remove .AsNoTracking() and remove .ToList(), then you will get pure IQueryable which you can query further.
I've created a method to include all related entities with some help of the great answer of #chiccodoro.
using the entity "Product" which has 7 navigation properties
public static IQueryable<T> IncludeAllEntities<T>(this DbSet<T> entity, DataContext context) where T : class
{
var querable = entity.AsQueryable();
var type = typeof(T);
var entityType= context.Model.FindEntityType(type);
var navs = entityType?.GetNavigations();
if (navs==null)
{
return null;
}
List<string> navNames = new List<string>();
foreach (var nav in navs)
{
navNames.Add(nav.Name);
}
try
{
var agg = navNames.Aggregate(querable, (acc, name) => acc.Include(name));
return agg;
}
catch (Exception ex)
{
throw;
}
}
here I am getting the type of the entity to get the navigation properties, then adding the name of each one to a list of string, then aggregating over the list to include each entity.
then we can use this extension method like this:
var record= await _context.Products.IncludeAllEntities(_context).FirstOrDefaultAsync(x => x.Id == key);
Related
I have the following generic method that I need to be able to perform a LINQ Where query in:
public static List<T> GetItems<T>(Guid parentId = new Guid()) where T : new()
{
var db = new SQLiteConnection(_dbPath);
List<T> result;
if (parentId != Guid.Empty)
{
result = db.Table<T>().Where(i => i.ParentId.Equals(parentId)).ToList();
}
else
{
result = db.Table<T>().ToList();
}
db.Close();
return result;
}
The compiler doesn't like the following line
result = db.Table<T>().Where(i => i.ParentId.Equals(parentId)).ToList();
error: cannot resolve 'ParentId'
Is it possible to use generics in this way in a LINQ query? Note that object of type T will always have a ParentId property.
You should concretize T parameter with some interface which will include required values. Also, you should add this interface to all types that contains this field or base type for its classes.
public interface IHierarchy
{
public Guid ParentId { get; }
}
public static List<T> GetItems<T>(Guid parentId = new Guid())
where T : IHierarchy, new()
{
var db = new SQLiteConnection(_dbPath);
List<T> result;
if (parentId != Guid.Empty)
{
result = db.Table<T>().Where(i => i.ParentId.Equals(parentId)).ToList();
}
else
{
result = db.Table<T>().ToList();
}
db.Close();
return result;
}
If you have 2 types of entities and the first contains required values and the second does not, then you can have two overloads for this scenario.
You used a generic type and the compiler don't know which entity are you going to use.
Just use reflection feature of .NET language.
result = db.Table<T>().Where(i => i.GetType().GetProperty("ParentId").GetValue(src, null).Equals(parentId)).ToList();
The problem is that in your code you assume that every T has a GUID property ParentId, while in fact you only required that every T has a default constructor. You need to require that every T has a ParentId.
You could do this by requiring that every T implements some interface. Like other answers suggest, however this is quite a nuisance, because for every class that you want to use this function for you'll need to implement this interface.
The function Enumerable.Where seems to be able to do the same job, without requiring any interface from the input items. So let's use the same method:
As input we tell which property to use (in your case ParentId) and with which value to compare (in your case parentId).
The only requirement we have is that we must be able to compare ParentId with parentId: it should be IEquatable
public List<T> GetItems<T, TKey>(Func<T, TKey> keySelector, Tkey value)
TKey : IEquatable<TKey>,
{
...
result = db.Table<T>()
.Where(t => keySelector(t).Equals(value))
.ToList();
}
Usage:
Guid value = ...
var items = GetItems<MyClass, Guid>(item => item.ParentId, value);
This function will also work with other classes and other properties:
int studentId = ...
var students = GetItems<Student, int>(student => student.Id, studentId);
var otherStudents = GetItems<Student, string>(student => student.Name, "John");
Two side remarks:
- you use new Guid() to define some default Guid. It is faster to use Guid.Empty
- You will not create new items of type T. They are already in your dbContext.Table. Therefore you don't need new().
- However, if your Table requires that T is a class then you should require that. See your Table definition:
where T : class
I have created my own InsertOrUpdate() implementations for a few types like this:
public IEnumerable<Genre> InsertOrUpdate(IEnumerable<Genre> genres)
{
foreach (var genre in genres)
{
var existingGenre = _context.Genres.SingleOrDefault(x => x.TmdbId == genre.TmdbId);
if (existingGenre != null)
{
existingGenre.Update(genre);
yield return existingGenre;
}
else
{
_context.Genres.Add(genre);
yield return genre;
}
}
_context.SaveChanges();
}
The return type of IEnumerable<T> is required because it will be used to insert the root object in the datacontext. This method basically retrieves the attached object if it exists and updates it with the newest values if it does or inserts it as a new object if it doesn't. Afterwards this attached object is returned so it can be linked to the root object in the many-to-many tables.
The problem now is that I have several of these collections (genres, posters, keywords, etc) and each type's ID is differently setup: sometimes it's called TmdbId, sometimes Id and sometimes Iso. It's one thing to use an interface and rename them all to Id but the problem exists in that they are also different types: some are int and some are string.
The question is easy: how I do turn this into something more generic to avoid this code duplication?
So far I have been toying around with
public IEnumerable<T> InsertOrUpdate<T>(IEnumerable<T> entities, Func<T, bool> idExpression) where T : class
{
foreach (var entity in entities)
{
var existingEntity = _context.Set<T>().SingleOrDefault(idExpression);
if (existingEntity != null)
{
_context.Entry(existingEntity).CurrentValues.SetValues(entity);
yield return existingEntity;
}
else
{
_context.Set<T>().Add(entity);
yield return entity;
}
}
_context.SaveChanges();
}
but obviously this won't work since I have no access to the inner entity variable. Sidenote: IDbSet<T>().AddOrUpdate() does not work in my scenario.
You could try:
public IEnumerable<T> InsertOrUpdate<T>(IEnumerable<T> entities, Func<T, object[]> idExpression) where T : class
and
var existingEntity = _context.Set<T>().Find(idExpression(entity));
called with something like
movie.Genres = new List<Genre>(InsertOrUpdate(movie.Genres, x => new object[] { x.Id }));
(note that a method that returns a IEnumerable<> is very dangerous... If you don't enumerate it, like
InsertOrUpdate(movie.Genres, x => x.Id);
then the method won't be executed fully, because it will be lazily executed "on demand")
If you only have single-key tables, you can change it to:
public IEnumerable<T> InsertOrUpdate<T>(IEnumerable<T> entities, Func<T, object> idExpression) where T : class
and
var existingEntity = _context.Set<T>().Find(new object[] { idExpression(entity) });
and
movie.Genres = new List<Genre>(InsertOrUpdate(movie.Genres, x => x.Id));
When EntityFramework executes a LINQ query, if query returns somethings as dynamic class, I can't get real type of result.
I have an abstract class:
abstract class Person
{
public string Name { get; set; }
//and about 1000 other properties
}
And 2 derived classes:
class RealPerson : Person
{
public void Print()
{
Console.WriteLine("Type=RealPerson, Name=" + Name);
}
}
class LegalPerson : Person
{
public void Print()
{
Console.WriteLine("Type=LegalPerson, Name=" + Name);
}
}
My LINQ TO SQL query:
var lst =
EFContext.Persons.Select(item=> new { DynamicClass_Name = item.Name }).ToList();
Now for every item in lst, I need to know type of its class to cast this item as that type, but item.GetType() returns a dynamic type.
For example assume that one of items of lst is RealPerson (called dynamicTypeItem), so if I know type of it is RealPerson I will cast this dynamicTypeItem to RealPerson using this code:
var value = dynamicTypeItem.GetType().GetProperty("DynamicClass_Name").GetValue(dynamicTypeItem);
var result = (RealPerson)Activator.CreateInstance(typeof(RealPerson));
result.GetType().GetProperty("Name").SetValue(result, value);
But I don't know type of this dynamicTypeItem (It has a dynamic type);
How to realize type of every item of lst?
It is very important that the above query selects only and only 1 property (Name property) of entities, so I can't use this code:
var lst =
EFContext.Persons.ToList().Select(item=> new { DynamicClass_Name = item.Name, Type=item.GetType() });
So I need knowing type of every item of lst before converting this item to dynamic type.
EDIT1
more explanation: result can't be Person because Person is abstract. result is RealPerson or LegalPerson, And when I select only one of properties of RealPerson or LegalPerson during convertion strongly type to anonymous type the type of original entity is missed.
You want to elicit the type from the value of one property of a database table? There is no way to select the type from that, since the type information is not fetched from database.
If you explain what it is you really need we might still be able to help you, but this constraint:
It is very important that the above query selects only and only 1 property (Name property) of entities
makes what you are trying to achieve impossible. You must select something more from the database.
How I could imaging doing it, although I would have a look at database design first if that is at all possible:
public partial class Person {
public Person() {
_dotnetType = this.GetType().Fullname;
_dotnetAssembly = this.GetType().Assembly.Fullname;
}
private string _dotnetType;
private string _dotnetAssembly;
public string DotNetType { get { return _dotnetType; } set { _dotnetType = value } }
public string DotNetAssembly { get { return _dotnetAssembl; } set { _dotnetAssembly = value } }
}
// Example usage
var peeps = from person in Entities.Persons
select new { Name = person.Name, Type = DotNetType, Assembly = DotNetAssembly };
var loadedPeople = peeps.ToList() // enumerate it
.Select( p => {
var instance = Activator.CreateInstance(p.Assembly, p.Type);
var property = p.GetType().GetProperties().First(prop => prop.Name == "Name");
property.SetValue(instance, p.Name, null);
});
I haven't tried this code, but it should work, just ensure that the parameterless constructor in Person gets called. The key point is that the database will "lose" the type information, so its better to store it as strings. Do remember that you need to add the columns to the database as well and map them!
This looks like an XY problem. Your question is, how do I instanciate objects from a table using Entity Frame (AND NOT Linq To SQL), where I have a discriminator?
The answer is simple.
YOU DON'T!
Use Single Table inheritance for your Entity Framework model and your LinqToEntities queries would be pretty simple.
Once you have that, there should be absolutely NO REASON WHAT SO EVER FOR YOUR PROJECTION.
You should be think about objects and not tables with an ORM.
So you could do the following to get all LegalPersons with name 'Alice'
var legallyAlices = EFContext.Persons.OfType<LegalPerson>()
.Where(x => x.Name == 'Alice');
OR
var legallyAlices = from legalPerson in EFContext.Persons.OfType<LegalPerson>()
where legalPerson.Name == 'Alice'
select legalPerson;
I have a method:
public static void GetObjects()
{
using(MyContext context = new MyContext())
{
var objects = context.Bars.Where(b => b.Prop1 != null)
.Select(b => new MyObject{Prop = b.Prop1, Name = b.Name})
.ToList();
foreach(var object in objects)
{
// do something with the object
}
}
}
I refactored the method to make it more general so that I can pass in a Func so that I can specify the where statement and what property from the Bars table gets assigned to MyObject.Prop like this:
public static void GetObjectsV2(Func<Bar, bool> whereFunc, Func<Bar, string> selectPropFunc)
{
using(MyContext context = new MyContext())
{
var objects = context.Bars.Where(whereFunc)
.Select(b => new MyObject{Prop = selectPropFunc(b), Name = b.Name})
.ToList();
foreach(var object in objects)
{
// do something with the object
}
}
}
GetObjectsV2 seems to run much slower than GetObjects. Are there any reasons this would affect performance, and if so, are there any ways around this while still keeping the function flexible?
The reason it is running slower is because you are passing in a Func<Bar, bool> which forces the context to retrive ALL Bars and then run the Func on the returned result set. A way to make this run better is to pass in Expression<Func<Bar, bool>>
Putting that all together will result in the following:
public static void GetObjectsV2(Expression<Func<Bar, bool>> whereFunc, Expression<Func<Bar, string>> selectPropFunc)
{
using(MyContext context = new MyContext())
{
var objects = context.Bars.Where(whereFunc)
.Select(selectPropFunc)
.ToList();
foreach(var object in objects)
{
// do something with the object
}
}
}
As I discovered in my own question, .Where(o => whereFunc(o)) is not the same as .Where(whereFunc) in the Entity Framework.
The first one, .Where(Expression<Func<Bar, bool>>) works like any other linq call, simply appending the expression to the expression tree.
In the second case, .Where(Func<Bar, bool>>), it will compile and evaluate the linq call (which so far is just context.Bars) before applying the whereFunc predicate.
So, to answer your question, the second one is much slower because it is pulling the entire Bars table into memory before doing anything with it. Using .Where(o => whereFunc(o)) instead should fix that
(or, as Mark suggests, change the type of whereFunc to Expression<Func<Bar, bool>>, which Func<Bar, bool> is implicitly convertible to)
I'm trying to writing a generic method that will load a record of a specific type, with a specific ID. Here's one way that works:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.ID == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
public abstract long ID { get; set; }
}
You can ignore the call to LinqUtils.GetDataContext<T>(); that's a utility function I've got to deal with the fact that I have multiple data contexts in my program. The point is that now I can declare any of my classes as subclasses of LinqableTable, and I can easily instantiate a record of that table just by calling LinqedTable<MyType>.Get(ID).
This has some limitations, however. Firstly, it forces all of my tables to have an I
identity field of type long, named ID. Secondly, because I'm using an abstract method, I am forced to go to the O/R designer and change the inheritance property of every ID field in my system to "override".
I want more flexibility than that. So naturally, I tried reflection, and came out with the following:
public abstract class LinqedTable<T> where T : LinqableTable {
public static T Get(long ID) {
DataContext context = LinqUtils.GetDataContext<T>();
var q = from obj in context.GetTable<T>()
where obj.IDValue == ID
select obj;
return q.Single<T>();
}
}
public abstract class LinqableTable {
internal long IDValue {
get { return (long)IDProperty.GetValue(this, null); }
set { IDProperty.SetValue(this, value, null); }
}
internal PropertyInfo IDProperty {
get { return this.GetType().GetProperty(IDPropertyName); }
}
internal protected virtual string IDPropertyName {
get { return "ID"; }
}
}
Theoretically, this allows me to override the ID column name, the cast to long should be OK with any integral data type, and I don't need to go defining all my ID columns as overrides.
BUT
Linq doesn't like this. On the call to q.Single<T>(); I get a runtime error:
The member 'EISS.Utils.LinqableTable.IDValue' has no supported translation to SQL.
OK, today I learned that Linq does some kind of magic on the back end; it doesn't instantiate obj and just read the IDValue property. So must be there's some attribute that needs to be set on the IDValue property that lets Linq do its thing.
But what?
Linq to SQL tries to translate your linq-query into SQL, but it does not know how to translate your property to a column name in the DB.
A good explanation can be found here on SO:
simple linq to sql has no supported translation to SQL
But how to solve it, is another matter. I have with succes used the apporoach from this thread:
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/df9dba6e-4615-478d-9d8a-9fd80c941ea2/
Or you can use dynamic query as mentioned here by scott guthrie:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Having read these posts: Generic Data Access using LINQ to SQL and C#,
LINQ-to-SQL: Generic Primary Key function and
Calling a generic method with Type
My colleague and I came up with the following digest:
We added the following method to our datacontext (in a partial class).
public T GetInstanceByPrimaryKey<T>(object primaryKeyValue) where T : class
{
var table = this.GetTable<T>();
var mapping = this.Mapping.GetTable(typeof(T));
var pkfield = mapping.RowType.DataMembers.SingleOrDefault(d => d.IsPrimaryKey);
if (pkfield == null)
throw new Exception(String.Format("Table {0} does not contain a Primary Key field", mapping.TableName));
var param = Expression.Parameter(typeof(T), "e");
var predicate =
Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(param, pkfield.Name), Expression.Constant(primaryKeyValue)), param);
return table.SingleOrDefault(predicate);
}
Then, where we need to instanciate from the type name and primary key value:
string name = "LinqObjectName";
int primaryKey = 123;
var dc = new YourDataContext();
Type dcType = dc.GetType();
Type type = dcType.Assembly.GetType(String.Format("{0}.{1}", dcType.Namespace, name));
MethodInfo methodInfoOfMethodToExcute = dc.GetType().GetMethod("GetInstanceByPrimaryKey");
MethodInfo methodInfoOfTypeToGet = methodInfoOfMethodToExcute.MakeGenericMethod(name);
var instance = methodInfoOfTypeToGet.Invoke(dc, new object[] { primaryKey });
return instance;
Hope this helps!
Since LINQ statements referred to a LINQ-to-SQL IQueryable are translated to SQL queries, you will have to use the AsEnumerable extension (which will in turn cause a read of all the items in the database) and do reflection-related stuff on that IEnumerable.
EDIT
As required here's a clarificationAs specified in a comment, what I meant was something like:
(from obj in context.GetTable<T>() select obj).AsEnumerable().Where(x => x.IDValue == ID)
Unlike a query executed on an IQueryable, which can be perfectly translated to SQL such as
context.GetTable().Where(x => x.Text == "Hello")
which gets converted to something similar to
SELECT * FROM TABLE_MAPPED_TO_TYPE_T WHERE Text = 'Hello'
a query executed against an IEnumerable - in your case - will be executed by fetching all the entries of your table and then applying code-wise the specified filter.