EF + generic repository + loading related entity: only explicit load works - c#

I have two simple POCO classes; I'm trying to get the MyY property below hydrated with the an instance of Y. I've tried a number of ways to do this, and think I might be missing something obvious or simple.
public class X
{
public int Id { get; set;}
public virtual Y MyY { get; set; }
}
public class Y
{
public int Id { get; set; }
// ...
}
I've turned lazy loading off via this call in my subclass of DbContext's constructor:
Configuration.LazyLoadingEnabled = false;
When retrieving an X I have tried
context.Set<X>.Include("MyY").FirstOrDefault(x => ....);
which did not work. I tried
var result = context.Set<X>.FirstOrDefault(x => ....);
context.Entry(result).Reference("MyY").Load();
which works, but requires two round-trips to the database. I tried
context.Set<X>.Select(x => new { X = x, Y = x.MyY }).FirstOrDefault(x => ...);
which also works, but "weakens" my model (ordinarily projecting to a new type is not so bad, but the "shape" of these EF POCOs works perfectly for the DTOs I'll be sending through WCF later).
I finally tried removing virtual from the MyY property as suggested in an answer to another question, but that had no effect at all.
Finally, I want to use the generic repository pattern. What I have ended up with is the following design, shown in part, which supports explicit-load (not preferred) and eager-load when modified to work properly. How do I modify it to get the single db round-trip eager-load?
public class EFRepository : IRepository
{
public T Get<T>(Specification<T> specification) where T : class, IEntity
{
var result = ApplyEagerLoading(context.Set<T>()).FirstOrDefault(specification.IsMatch);
ApplyPostQueryLoading(new List<T> { result });
return result;
}
// doesn't really seem to work yet...
private DbSet<T> ApplyEagerLoading<T>(DbSet<T> set) where T : class, IEntity
{
var ls = loadSpecs.GetOrAdd(typeof(T), () => new List<LoadSpec>());
foreach (var spec in ls.Where(s => !s.ExplicitLoad))
set.Include(spec.PropertyName);
return set;
}
// works, but wrong on so many levels...
private void ApplyPostQueryLoading<T>(IEnumerable<T> entities) where T : class, IEntity
{
var ls = loadSpecs.GetOrAdd(typeof(T), () => new List<LoadSpec>());
foreach (var e in entities)
foreach (var spec in ls.Where(s => s.ExplicitLoad))
if (spec.IsCollection)
context.Entry(e).Collection(spec.PropertyName).Load();
else
context.Entry(e).Reference(spec.PropertyName).Load();
}
private readonly IDictionary<Type, IList<LoadSpec>> loadSpecs = new Dictionary<Type, IList<LoadSpec>>();
private class LoadSpec
{
internal string PropertyName;
internal bool ExplicitLoad;
internal bool IsCollection;
}
}
Example uses:
// add a rule to load MyY explicitly
repository.AddLoadRule<X>(x => x.MyY, explicit:true, isCollection:false)
...
var x = repository.Get<X>(new Specification<X>(x => x.Id == 5));
// add a rule to load MyY with X
repository.AddLoadRule<X>(x => x.MyY, explicit:false)
...
// x.MyY will be null! Doesn't work!
var x = repository.Get<X>(new Specification<X>(x => x.Id == 5));
An Update Based on The Answer:
It turns out my temp code examples lied (those one-liners above). I had actually cached the result of .Include in a local variable but applied the .FirstOrDefault against the .Set<X> not the result of .Include. Here is the fix to ApplyEagerLoading, which mirrors what others have suggested in related questions:
private IQueryable<T> ApplyEagerLoading<T>(IEnumerable<T> set) where T : class, IEntity
{
var ls = loadSpecs.GetOrAdd(typeof(T), () => new List<LoadSpec>());
var query = set.AsQueryable();
return ls.Where(s => !s.ExplicitLoad).Aggregate(query, (current, spec) => current.Include(spec.PropertyName));
}

This should work:
X entity = context.Set<X>().Include(x => x.MyY).FirstOrDefault();
IF it doesn't the problem must be elsewhere.
If you need some eager loading strategy check this answer.

Related

C# how to return 1 row from List<T> object passed as parameter

I need to return one row of List from my function Selectus.
So I pass to the function Selectus object that reflects database table fields and I need to return one row which match the parameter looking_for:
public static List<T> Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.ToList();//here one record should be returned
}
I do not know how to select one row from the list assuming that T parameter is vary. In SelectusT> method I want to pass as T different objects which reflect fields in database table rather than creatinig separate methods for each select. e.g. call Selectus, where object passed is public class ProductCodes { public int ID { get; set; } public string SapIndex { get; set; } public string SapName { get; set; } }. Then I want to call another Selectus<ProductTypes> for another table etc... So I want to write generic/overall method and use it universally for all types of my objects which reflects the fields of few database tables. The SapIndex property is always in the same place of all objects...
Using prop[1] is incredibly fragile. Who says that the property you're currently interested in is always going to be in second place? What if someone adds another property tomorrow? What if not every T that you use have the same property in the second place on its list of properties? It is quite unclear what your actual goal is here and why you've taken the reflection route.
You would be better off using inheritance or interface implementation here. I'm going to use an interface in this answer, but either would work.
For the sake of clarity, let's assume there is a Code field in all your possible lists, and this is the property you're trying to match with.
Define a reusable interface:
public interface ICodeEntity
{
string Code { get; }
}
Apply your interface to all of the classes that you intend to use for your Selectus method.
public class Person : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
public class Document : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
Add a generic type constraint that limits the use of T only to types that implement your interface.
public static List<T> Selectus<T>(string code)
where T : ICodeEntity
You can now write your code in a way that it relies on the type in question having a Code property, and the compiler will help enforce it.
var db = OrmLiteBaza().Open();
var list = db.Select<T>().ToList();
db.Dispose();
return list.Where(item => item.Code == code).ToList();
Usage examples:
List<Person> peopleWithCodeABC = Selectus<Person>("ABC");
List<Person> documentsWithCodeXYZ = Selectus<Document>("XYZ");
// This will fail if Animal does not implement ICodeEntity
var compilerError = Selectus<Animal>("ABC");
I might not understand fully what you want, but instead of string looking_for you could pass in a Func<,> delegate which acts as a selector.
Something like:
public static List<TField> Selectus<T, TField>(Func<T, TField> selector)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Select(selector); // 'using System.Linq;'
return list_selected_record.ToList();
}
Then I believe it could be called like this:
var list_one = Selectus((ProductCodes x) => x.SapIndex);
var list_two = Selectus((ProductTypes x) => x.SapIndex);
var list_three = Selectus((ProductCodes x) => x.SapName);
With this syntax I leave out the <ProductCodes, string> generic arguments to the method since they can be inferred.
Hmm, maybe you want it in the opposite dimension. You could do:
public static List<T> Selectus<T>(Func<T, bool> predicate)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Where(predicate); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus((ProductCodes x) => x.SapIndex == "ABC");
var list_two = Selectus((ProductTypes x) => x.SapIndex == "ABC");
var list_three = Selectus((ProductCodes x) => x.SapName == "DaName");
or:
var list_one = Selectus<ProductCodes>(x => x.SapIndex == "ABC");
var list_two = Selectus<ProductTypes>(x => x.SapIndex == "ABC");
var list_three = Selectus<ProductCodes>(x => x.SapName == "DaName");
But if it is going to always be the "same" property, like always x.SapIndex (but for different types of x), then Flater's answer looks good.
Otherwise, if you insist, your reflection approach should be possible. Use propety's name, not its index! Let me try:
public static List<T> Selectus<T>(string looking_for)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
const string prop_name = "SapIndex";
var prop = typeof(T).GetProperty(prop_name); // can blow up for bad T
var list_selected_record = select_all_list
.Where(x => (string)(prop.GetValue(x)) == looking_for); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus<ProductCodes>("ABC");
var list_two = Selectus<ProductTypes>("ABC");
you can change code to return just one element
public static T Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.FirstOrDefault();//here one record should be returned
}

How can i select generic columns from a table but make sure my keycolumn is selected too?

I have a table in my database with a lot of columns. I want to have a class where i load columns specified in the constructor into a list. I dont want to load all columns because that takes too long. Additionaly i may want to apply functions on specific columns becuase some data needs to be sanitized. Later i want to be able to return rows from this list by a keycolumn that is fixed (no need to specify it in the constructor).
This is kinda what i want:
public class DataHolder<TType> where TType:class
{
private List<TType> _data;
public DataHolder(DataContext context,Expression<Func<MyTable, TType>> select)
{
_data = context.MyTable.Select(select).DoSanitation().ToList();
//do sanitation on a column if it is in _data here
}
public TType Get(int id)
{
return _data.Single(d => d.Id == id);
}
}
And then i want to use it kinda like this:
var datHolder = new DataHolder(context, x=> new{x.Column1,x.Column2});
var row= datHolder.Get(123);
And row should have the fields "Column1" and "Column2" and "Id".
So i tried it by using anonymous types but because anonymous types cant use interfaces i am not able to make sure the type has the field "Id". Also the whole sanitation thing doesnt make sense on a anonymous type.
I have the sense that i am doing something i should not do or am not seeing a simple solution. I also had a look into Ado.Net which seems like it solve my problems because i can assemble columns adhoc. But all my other code runs with ef core so i am not sure if i should proceed in that direction.
You can't do this with anonymous types, but with types, known at compile time, you can do something like this:
public interface IEntity
{
public int Id { get; }
}
public class DataHolder<TType>
where TType : class, IEntity
{
private static readonly Lazy<IEnumerable<PropertyInfo>> MyTableProperties = new Lazy<IEnumerable<PropertyInfo>>(() => GetPublicInstanceProperties<MyTable>());
private static readonly Lazy<Expression<Func<MyTable, TType>>> Selector = new Lazy<Expression<Func<MyTable, TType>>>(GetSelector);
private readonly IReadOnlyDictionary<int, TType> data;
public DataHolder(MyContext context, Action<TType> doSanitation)
{
var entities = context.MyTable
.Select(Selector.Value)
.ToList();
foreach (var entity in entities)
{
doSanitation(entity);
}
data = entities.ToDictionary(_ => _.Id);
}
public TType Get(int id) => data[id];
private static Expression<Func<MyTable, TType>> GetSelector()
{
var lambdaParameter = Expression.Parameter(typeof(MyTable));
var memberBindings = GetPublicInstanceProperties<TType>()
.Select(propertyInfo => Expression.Bind(propertyInfo, Expression.MakeMemberAccess(lambdaParameter, MyTableProperties.Value.FirstOrDefault(p => p.Name == propertyInfo.Name))));
var memberInit = Expression.MemberInit(Expression.New(typeof(TType)), memberBindings);
return Expression.Lambda<Func<MyTable, TType>>(memberInit, lambdaParameter);
}
private static IEnumerable<PropertyInfo> GetPublicInstanceProperties<T>() => typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
}
GetSelector method is just a simple mapper: it assigns property of TType object from the property of MyTable object with the same name.
Usage:
using (var context = new MyContext())
{
var dataHolder = new DataHolder<EntityA>(context, entity =>
{
// TODO:
});
var row = dataHolder.Get(1);
}
where EntityA is:
public class EntityA : IEntity
{
public int Id { get; set; }
public int A { get; set; }
}

Find Max(Id) of DbSet<T> where T is unknown

How do I find the biggest Id of a DbSet.Set<T>()?
Note: not DbSet<TEntity>.
I don't know the type at runtime.
Context: I have 20 tables/entities, which I'm using a generic method to do processing.
The process involves looking up the biggest Id of that table/entity and comparing it with the record at hand.
If the record's id is bigger than the database's, than it would be inserted into the database.
So far I've tried using reflection:
DbSet<T> table = DbContext.Set<T>();
var lastRecord = table.LastOrDefault(); // throws not supported error
var idProperty = lastRecord.GetType().GetProperties()
.FirstOrDefault(p => p.Name.Equals("Id");
int maxId = (int)idProperty.GetValue(lastRecord);
I've also tried using an interface cast:
interface ICommonEntity
{ // this interface allows the generic method
string StringId { get;} // to know how to handle entity Id's of
int? IntId { get; } // different types (string vs int).
}
var whatever = table.OrderByDescending(e => (e as ICommonEntity).IntId).FirstOrDefault();
int maxId = (whatever as ICommonEntity).IntId ?? 0;
But the above yields the following error:
The 'TypeAs' expression with an input of type xx is not supported. and a check of type yy. Only entity types and complex types are supported in LINQ to Entities queries
Additional data: All my entities have the column/property Id of type int.
Web searches that I've done mainly point to solutions that the type is known e.g. TEntity, db.Users.xxx() etc..
Update
In response to Ian's answer, I can't use Id directly. Why?
One of my entity has a field named Id, but is of type string.
class EntityStringId : ICommonEntity
{
public string Id { get; set; }
public string StringId => Id;
public int? IntId => null;
}
class EntityIntId : ICommonEntity
{
public int Id { get; set; }
public string StringId => null;
public int? IntId => Id;
}
And if I try to use IntId for ordering,
private void SomeMethod<T>(string file)
//where T : class // original
//where T : ICommonEntity // cannot. DbContext.Set<T>(); requires class
where T : class, ICommonEntity // throws exception
{
var table_T = DbContext.Set<T>();
var maxId = table_T.Max(e => e.IntId); // throws exception ↓
}
The specified type member 'IntId' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties are supported.
For a better picture, my method's logic:
private void ProcessCsvToDb<T>(
DbSet<T> table,
T csvRecord) where T : class
{
var iRecord = csvRecord as ICommonEntity;
T dbRecord = null;
if (!string.IsNullOrEmpty(iRecord.StringId))
{
dbRecord = table.Find(iRecord.StringId);
}
else if (iRecord.IntId != null)
{
dbRecord = table.Find(iRecord.IntId);
}
}
In order to do this without a base class/interface, you will need to manually compose the expression:
public static IOrderedQueryable<int> OrderById(Type entityType)
{
var dbSet = context.Set(entityType);
var item = Expression.Parameter(entityType, "item");
var property = Expression.Property(item, "Id");
var lambda = Expression.Lambda<Func<T, int>>(property, item);
// the above generates:
// item => item.Id
return dbSet.OrderByDescending(lambda);
}
You can build expression to sort by Id, but DynamicQueryable class does it for you:
DbSet<T> table = assignFromSomeWhere();
var maxId = table.OrderBy("Id desc").FirstOrDefault();
DynamicQueryable also gives you different extension methods (dynamic Where, Select). Obviously it is bigger satisfaction to build expressions on your own, but sometimes it is very complicated and this library helps a lot.
If you have an interface, as discussed in comments, is there any reason you can't do this to avoid the cast:
public static int? GetMaxId<T>(DBSet<T> dbSet)
where T : ICommonEntity
{
return dbSet.OrderByDescending(e => e.Id).FirstOrDefault();
}

How to reuse data mapping Expression as a compiled Expression?

I have this code in LinqPad:
void Main() {
var a = Product.Select( DDL.ToDDL).Take(1).Dump();
}
That successfully queries SQL for two table fields instead of all of them using this POCO class (not sure if the inner static makes the outer class static too):
public class DDL {
public static readonly Expression<Func<Product, DDL>> ToDDL =
o => new DDL {
id = o.identifier,
name = o.pbfeattext
};
public int id {get;set;}
public string name {get;set;}
}
So I'd like to write the first line of code this way:
var a = Product.Select(o => o.AsDDL()).Take(1).Dump();
I've tried writing this code:
public static class DDL3 {
public static DDL AsDDL (this Product p) {
return new DDL {
id = p.identifier,
name = p.pbfeattext
};
}
}
Which produces the same result, but it retrieves the entire Product record in SQL instead of just the two fields we need. I've also tried this code, but it requires doing var a = Product.AsDDL().Take(1).Dump();. I need it in the other form (o => o.AsDDL()). (I've even tried using a constructor function, but SQL doesn't understand the function in that form.)
public static class DDL3
{
public static DDL AsDDL (this Product p)
{
return new DDL {
id = p.identifier,
name = p.pbfeattext
};
}
}
How would one code the Linq Expression?
AutoMapper might be for you. see this: https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions
It requires to configure mapping once, but after that, you can reuse DTO mapping anytime.
Here's an example from it:
public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
Mapper.CreateMap<OrderLine, OrderLineDTO>()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name);
using (var context = new orderEntities())
{
return context.OrderLines.Where(ol => ol.OrderId == orderId)
.Project().To<OrderLineDTO>().ToList();
}
}
This is what worked for me. I define the Expression as above, then a compile on the Expression right below it. This is really a View Model taking a POCO and mapping it to itself. You can add attributes to the methods so that JSON.NET doesn't serialize them (assuming...) The reason for Map and ToMap is the first is used to project fields in Entity Framework for example, and the other is used on in-ram collections.
public class DDL {
public static readonly Expression<Func<Product, DDL>> Map =
o => new DDL {
id = o.identifier,
name = o.pbfeattext
};
public static readonly Func<Product, DDL> ToMap =
DDL.Map.Compile();
public int id {get;set;}
public string name {get;set;}
}

Casting to a derived type in a LINQ to Entities query with Table Per Hierarchy inheritance

I have a LINQ to entities model with Table Per Hierarchy inheritance. I have a query over the base type, and I want to do specific type-dependent logic. For example:
IQueryable<BaseType> base = ...
// this works fine
var result = base.Select(b => b is DerivedType1 ? 1 : 2).ToList();
// this doesn't compile to SQL
var result2 = base.Select(b => b is DerivedType1 ? ((DerivedType1)b).DerivedProperty : null).ToList();
Is there any way to do something like this without processing IQueryables of each derived type separately:
// I'd rather not do this:
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty);
var resultB = base.OfType<DerivedType2>().Select(d => default(int?));
var result = resultA.Concat(resultB).ToList();
Direct casting to an entity type like (DerivedType1)b isn't supported with LINQ-to-Entities but the as operator (b as DerivedType1) is, hence you could try:
var result2 = base
.Select(b => b is DerivedType1
? (b as DerivedType1).DerivedProperty
: null)
.ToList();
OfType<DerivedType1>()
will return an IEnumerable, if possible, try to change to base-type to IEnumerable instead of IQueryable, you might en up in some SQL restrictions when using IQueryable.
That is of course if you are not actually quering a database?
You can use EntityFramework.Extended to improve the performance of the query instead of doing 2 round trips to DB.
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty).Future();
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)).Future();
var result = resultA.Concat(resultB).ToList();
In this case only one round trip to bd is executed.
This framework is very useful for many other things int EF
You could have a method on your base type that's overridden in your derived types to provide the relevant property value.
public class MyBaseClass
{
public virtual int GetSomething()
{
throw new NotImplementedException();
}
}
public class MyDerivedClass1 : MyBaseClass
{
public int SomeProperty { get; set; }
public override int GetSomething()
{
return this.SomeProperty;
}
}
public class MyDerivedClass2 : MyBaseClass
{
public int SomeOtherProperty { get; set; }
public override int GetSomething()
{
return this.SomeOtherProperty;
}
}
Then you could:
var result = base.Select(b => b.GetSomething()).ToList();
Try this, I have never done anything with needing to do this kind of this but this should do it. Also if you use base, first of all don't because it is a keyword but if you must, use #base the # in front of the name denotes that it is not used as a keyword.
var resultA = base.Select(aVar =>
(aVar is DerivedType1) ?
(DerivedType)(((DerivedType1)aVar).DerivedProperty)
:
(DerivedType)(default(int?))
).ToList();

Categories