Include several navigation properties with Include(string) method - c#

For the first, I'm trying to avoid direct link to EntityFramework in my assembly, so I can't use System.Data.Entity namespace in client code, only in interface implementation class.
I have interface
public interface IEntitySource<T>
where T : class
{
...
IQueryable<T> WithProperties(params string[] properties);
...
}
and it's EF implementation:
public class EfEntitySource<T> : IEntitySource<T>
where T : class
{
public IQueryable<T> WithProperties(params string[] properties)
{
IQueryable<T> queryable = this.DbSet;
foreach (var property in properties)
{
queryable = this.DbSet.Include(property);
}
return queryable;
}
}
and client code:
public IEnumerable<ContentChangeHistory> GetContentActivityAtGroup(Guid groupId)
{
var groupActivity = this.ContentChangeHistorySource
.WithProperties("ContentPost", "ContentItem", "SystemUser")
.Where(cch => cch.ChangeGroupId == groupId);
return groupActivity;
}
but, the code, that executes GetContentActivityAtGroup method, returns ContentChangeHistory collection with only latest navigation property initialized, e.g. SystemUser.
some code modifications, like this:
public IQueryable<T> WithProperties(params string[] properties)
{
foreach (var property in properties)
{
this.DbSet.Include(property);
}
return this.DbSet;
}
gave no results

Change
queryable = this.DbSet.Include(property);
to
queryable = queryable.Include(property);

Related

Mocking IOrderedQueryable and IDocumentQuery

I'm using IDocumentClient to retrieve and update items in a DocumentDB collection.
The code for retrieving a list of documents that match a condition looks like this:
public static async Task<FeedResponse<T>> GetDocuments<T>(
this IDocumentClient client,
string collection,
Expression<Func<T, bool>> filter)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(GetCollectionUri(collection)).Where(filter)
.AsDocumentQuery();
return await query.ExecuteNextAsync<T>().ConfigureAwait(false);
}
In order to unit test the code above against a list of mock items, I've created a mock class:
public class MockIOrderedQueryable<T> : List<T>, IOrderedQueryable<T>, IDocumentQuery<T>
{
public Expression Expression
{
get
{
var content = this.ToList();
return content.AsQueryable().Expression;
}
}
public IQueryProvider Provider => new MyProvider<T>(this.ToList());
public Task<FeedResponse<TResult>> ExecuteNextAsync<TResult>(CancellationToken token = new CancellationToken())
{
var tmp = new FeedResponse<TResult>((IEnumerable<TResult>)this);
return Task.FromResult(tmp);
}
}
The Where filter is an extension method on IQueryable<T>, so I needed an implementation that looks like this:
public class MyQueryable<T> : IQueryable<T>, IDocumentQuery<T>
{
private readonly List<T> _list;
public MyQueryable(List<T> list)
{
_list = list;
}
public Task<FeedResponse<TResult>> ExecuteNextAsync<TResult>(CancellationToken token = new CancellationToken())
{
var tmp = new FeedResponse<TResult>(_list as List<TResult>);
return Task.FromResult(tmp);
}
}
And also an implementation of IQueryProvider that returns the IQueryable instance to my original mock class via CreateQuery:
public class MyProvider<T> : IQueryProvider
{
private readonly List<T> _list;
public MyProvider(List<T> list)
{
_list = list;
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new MyQueryable<TElement>(_list as List<TElement>);
}
}
For brevity, I omitted the code for the methods that throw NotImplementedException and also for the fields that are not used.
This all looks fine, but there's one thing I did not manage to do: applying the real filtering passed as Expression in CreateQuery on the _list member of MyProvider. I tried calling Invoke and retrieving the arguments, but it did not work. The expression returned in MockIOrderedQueryable is probably not the good one (.AsQueryable on the list). I'd like to get to a lambda Expression<Func<T, bool>> and call it on the list.
Any help appreciated.

Entity Framework generic Where for entities that inherit from a specific class

Here I have a generic Repository class I copied from a tutorial page, but specifically my problem is on the two last functions. On my project I have several catalog entities that inherit from CRUDProperties class and there is a property "Activo" in all of them, what I currently want to do is that if the entity inherits from CRUDProperties class I get all the entities with Activo property on true, and if they do not inherit from that class it just gets all entities. But the compiler throws an error stating T is already defined. What should I do?
public class Repository<T> where T : class
{
private readonly projectEntities context;
private IDbSet<T> entities;
string errorMessage = string.Empty;
public Repository(projectEntities context)
{
this.context = context;
}
public T GetById(object id)
{
return context.Set<T>().Find(id);
}
// This is the function that throws me a compilation error
public virtual IList<T> GetAll<T>() where T : CRUDProperties
{
return context.Set<T>().Where(c => c.Activo).ToList();
}
public virtual IList<T> GetAll()
{
return context.Set<T>().ToList();
}
}
The compiler complains about the ambiguous naming of the type parameters. The class already has a type parameter named T, so in the context of the class that type parameter name is already "taken".
But you should be able to accomplish what you want to do simply by renaming the type parameter for the method to something else than T, so your changed method could look like this:
public virtual IList<TCrud> GetAll<TCrud>() where TCrud : CRUDProperties
{
return context.Set<TCrud>().Where(c => c.Activo).ToList();
}
Note: I assume here that CRUDProperties is a class... If it is an interface then you'll also need to copy the class constraint to the method (i.e. change it to where TCrud : class, CRUDProperties)
using this method you can pass custom where clause to to your GetAll Method
public virtual IList<T> GetAll<T>(Expression<Func<T, bool>> predicate)
{
return context.Set<T>().Where(predicate).ToList();
}
In this method we first check if the T type have Activo property, If find this property so we create a custom expression tree and replace with default predicate that returns all records, this function only returns records that have true value in activo property.
public virtual IList<T> GetAll<T>()
{
Expression<Func<T, bool>> predicate = t => true;
if(typeof(T).GetProperty("Activo") != null)
{
var epx = Expression.Parameter(typeof(T), "x");
Expression left = Expression.PropertyOrField(epx, "Activo");
Expression right = Expression.Constant(true);
Expression e1 = Expression.Equal(left, right);
predicate = Expression.Lambda<Func<T, bool>>(e1, new ParameterExpression[] { epx });
}
return context.Set<T>().Where(predicate);
}

Check if entity implements interface and add predicate in generic repo

Some of my entities have IEnabledEntity interface.
I want to check in repository if entity implements interface then add some predicate. I have the following code:
public class Repository<T> : IRepository<T> where T : class, IEntity, new()
{
public IQueryable<T> Get(Expression<Func<T, bool>> predicate, params string[] includes)
IQueryable<T> query = Context.Set<T>();
foreach (var include in includes)
{
query = query.Include(include);
}
query = query.Where(predicate);
var isEnabledEntity = typeof(IEnabledEntity).IsAssignableFrom(typeof(T));
if (isEnabledEntity)
{
query = query.Where(e => ((IEnabledEntity) e).IsEnabled);
}
return query;
}
public interface IEnabledEntity
{
bool IsEnabled { get; set; }
}
public class Test : IBaseEntity, IEnabledEntity
{
// ...
public bool IsEnabled { get; set; }
}
But, I get exception about casting:
Unable to cast the type 'Domain.Test' to type 'Domain.Interfaces.IEnabledEntity'. LINQ to Entities only supports casting EDM primitive or enumeration types.
How to make it work?
Linq-to-Entities only knows models which are classes, that's why an expression can't contain an interface type. However clearly it's possible runtime to access the IsEnabled property if T implements it, so if you do the check yourself with IsAssignableFrom() (like you do), it's possible to use the ExpressionVisitor class to bypass the casting:
internal class IgnoreCast : ExpressionVisitor
{
protected override Expression VisitUnary(UnaryExpression e)
{
if(e.NodeType == ExpressionType.Convert && e.Type.IsAssignableFrom(typeof(e.Operand))
return e.Operand;
else
return e;
}
}
Then you need to create your filter with an extensionmethod which implements the IgnoreCast class:
internal static class LocalExtensions
{
internal static IgnoreCast ic = new IgnoreCast();
internal static IQueryable<T> FilterEnabled<T>(this IQueryable<T> query) where T: class
{
Expression<Func<T,bool>> expr = e => ((IEnabledEntity)e).IsEnabled;
expr = (Expression<Func<T,bool>>)ic.Visit(e);
return query.Where(expr);
}
}
Then you can just use that method in your program:
if(typeof(IEnabledEntity).IsAssignableFrom(T))
query = query.FilterEnabled();
The base method Visit(Expression e) will pass each node of the expression to a more specialized Visit method for that kind of node. The Convert nodetype is a UnaryExpression so this method will be overriden in the derived class. If the unaryexpression is of the Convert nodetype and the operand implements the type it will just return the operand, thus removing the casting.
The type parameter in IQueryable<T> is covariant, so instead of worrying about casting the entity in your expression, just safe-cast the entire query itself and then use Cast<T>() to get it back to your entity type:
public IQueryable<T> Get(Expression<Func<T, bool>> predicate, params string[] includes)
{
IQueryable<T> query = Context.Set<T>();
foreach (var include in includes)
{
query = query.Include(include);
}
query = query.Where(predicate);
var enabledQuery = query as IQueryable<IEnabledEntity>;
if (enabledQuery != null)
query = enabledQuery.Where(e => e.IsEnabled).Cast<T>();
return query;
}

Get Expression<Func<T, object>> for each property accessed by Expression<Func<T, U>>

I have a data access class that acts as an intermediary between logic classes and the underlying datasource, which is interchangeable. This class allows you to query the datasource using lambdas, LINQ-style. A source-agnostic class provides high-level functionality powered by a few basic operations (Add, GetAll, Update, Delete, Commit) that are implemented by small adapter classes, one for each source type (SQL, SQlite, XML serialiser, WCF client, REST client, whatever).
My problem is that some relational data sources (particularly SQLite) aren't smart enough to load relationship properties when I need them; I have to explicitly ask for them to be included. This is fine for my Get methods; I can pass a params array of expressions to load anything I need. With .Any(), however, this feels a bit odd - if I'm asking if there are any Customer records whose Purchases list contains a certain item, I shouldn't then have to tell it to load the Purchases list; that seems like the sort of thing it should be able to figure out.
So my Any() method takes Expression<Func<T, bool>> where T is obviously going to be the type I'm operating on. In the above example, it'd be used something like this:
using (var db = _dataAccessProvider.NewTransaction())
{
return db.Any<Customer>(c => c.Purchases.Contains(someProduct));
}
Is it possible to take the Expression<Func<Customer, bool>> that represents the operation c => c.Purchases.Contains(someProduct)) and work out that the property it's referring to is c => c.Purchases? How would I go about doing that? What about a lambda that touches multiple properties?
Use ExpressionVisitor to find all MemberExpression expressions which reference required object properties.
Quick example:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
class Program
{
sealed class ReferencedPropertyFinder : ExpressionVisitor
{
private readonly Type _ownerType;
private readonly List<PropertyInfo> _properties = new List<PropertyInfo>();
public ReferencedPropertyFinder(Type ownerType)
{
_ownerType = ownerType;
}
public IReadOnlyList<PropertyInfo> Properties
{
get { return _properties; }
}
protected override Expression VisitMember(MemberExpression node)
{
var propertyInfo = node.Member as PropertyInfo;
if(propertyInfo != null && _ownerType.IsAssignableFrom(propertyInfo.DeclaringType))
{
// probably more filtering required
_properties.Add(propertyInfo);
}
return base.VisitMember(node);
}
}
private static IReadOnlyList<PropertyInfo> GetReferencedProperties<T, U>(Expression<Func<T, U>> expression)
{
var v = new ReferencedPropertyFinder(typeof(T));
v.Visit(expression);
return v.Properties;
}
sealed class TestEntity
{
public int PropertyA { get; set; }
public int PropertyB { get; set; }
public int PropertyC { get; set; }
}
static void Main(string[] args)
{
Expression<Func<TestEntity, int>> expression =
e => e.PropertyA + e.PropertyB;
foreach(var property in GetReferencedProperties(expression))
{
Console.WriteLine(property.Name);
}
}
}

How to query all tables that implement an interface

I have implemented an interface for some of my entity classes:
public partial class Order : IReportable
{
public string TableName { get { return "Order"; } }
}
public partial class Client: IReportable
{
public string TableName { get { return "Client"; } }
}
public interface IReportable
{
string TableName { get; }
}
Then I added this to the DbContext:
public virtual DbSet<IReportable> IReportable { get; set; }
When I try to query all the tables that implement this interface (as shown here):
var result = from reportabletable in db.IReportable
where reportabletable.TableName == table_name
select reportabletable
I get the following exception:
The type 'Report.DataAccess.IReportable' was not mapped. Check that
the type has not been explicitly excluded by using the Ignore method
or NotMappedAttribute data annotation. Verify that the type was
defined as a class, is not primitive or generic, and does not inherit
from EntityObject.
I would go for something like this:
Create this extension method
public static class DbContextExtensions
{
public static IEnumerable<T> SetOf<T>(this DbContext dbContext) where T : class
{
return dbContext.GetType().Assembly.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface)
.SelectMany(t => Enumerable.Cast<T>(dbContext.Set(t)));
}
}
And use it like this:
using (var db = new dbEntities())
{
var result = from reportabletable in db.SetOf<IReportable>()
where reportabletable.TableName == table_name
select reportabletable
}
EF doesn't like mapping interfaces directly to tables. You can get around this by making using a generic Repository, as outlined Here!
Then use repository method and supply the Type of the table(s) you want to query. Something like: myRepo.GetAll<myClient.GetType()>();
Get the classes that inherit that interface and run the query for all of them:
var types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes().Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)));
foreach (var mytype in types)
{ // aggregate query results }
Hope this helps! There is probably a more graceful solution
First of all MarcGravell comment is on the money. Its up to you to know which table to query.
Personally I go through list of poco types that implement an interface or have an custom attribute. But if you are keen to go via the DBContext only, here are some extensions that give you access to the "names". You will still need to access that part of the context afterwards one at a time.
Again you can do that via generics, but you can just go directly as you suggest.
You will need to iterate of a list of types.
eg:
ReportRespository : BaseRespository where t : IReport
Check the assembly for Certain types and attributes
eg
/// <summary>
/// POCOs that have XYZ Attribute of Type and NOT abstract and not complex
/// </summary>
/// <returns></returns>
public static List<Type> GetBosDirDBPocoList() {
var result = new List<Type>();
// so get all the Class from teh assembly that public non abstract and not complex
foreach (var t in Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.BaseType != null
&& t.IsClass
&& t.IsPublic
&& !t.IsAbstract
&& !t.IsComplexType()
&& t.GetMyAttribute() != null)) {
result.Add(t);
}
}
return result;
}
public static GetMyAttribute(this Type T) {
var myAttr= T.GetCustomAttributes(true)
.Where(attribute => attribute.GetType()
.Name == "XYZAttr").Cast<BosDir>().FirstOrDefault();
return myAttr;
}
Extensions
public static class DalExtensions {
// DbSet Names is the plural property name in the context
public static List<string> GetModelNames(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetNames(propList);
}
// DbSet Names is the plural property name in the context
public static List<string> GetDbSetTypeNames<T>() where T : DbContext {
var propList = typeof (T).GetProperties();
return GetDbSetNames(propList);
}
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetModelTypes(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetTypes(propList);
}
// DBSet Types POCO types as IEnumerable List
public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
.Name.StartsWith("DbSet"))
.Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
}
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetDbSetTypes<T>() where T : DbContext {
var propList = typeof (T).GetProperties();
return GetDbSetTypes(propList);
}
private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.PropertyType.GenericTypeArguments[0].Name)
.ToList();
return modelTypeNames;
}
private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.Name)
.ToList();
return modelNames;
}
}
}
Accepted solution does not work in EF Core.
Here is my first working draft
public IEnumerable<T> SetOf<T>() where T : class
{
var firstType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.FirstOrDefault(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface);
if (firstType == null) return new List<T>();
var dbSetMethodInfo = typeof(DbContext).GetMethod("Set");
var dbSet = dbSetMethodInfo.MakeGenericMethod(firstType);
IQueryable<T> queryable = ((IQueryable)dbSet.Invoke(this, null)).Cast<T>();
return queryable.ToList().Cast<T>();
}
Then you could use like this
_dbContext.SetOf<ISomeInterface>();
More info here Expose method DbContext.Set(Type entityType)

Categories

Resources