Nested Lambda for use with Fluent Interface - c#

Given the following types:
class Parent { List<Child> Children {get;set;}}
class Child {List<Child> GrandChildren {get;set;}}
class Helper<TEntity> {List<string> Properties {get;set;}}
And given the following methods on Helper...
public Helper AddProps<TEntity, TProp>(Expression<Func<TEntity, TProp>> exp)
{
this.Properties.Add(GetPropInfo(exp).Name);
}
public PropertyInfo GetPropInfo(Expression<Func<TEntity, TProp>> exp)
{
return (PropertyInfo)((MemberExpression)(expression.Body)).Member;
}
I am able to do this:
Helper<Parent> myHelper = new Helper<Parent>();
myHelper.AddProps(x => x.Children);
The string list "Properties" on myHelper would then contain the value "Children", the name of the property passed through the expression.
What I want to do now is to be able to achieve the same thing, only with the ability to reflect type hierarchy.
Would it look like this ?
x => x.Children { xx => xx.GrandChildren }
Or is it even possible, and what would be involved? I've seen nested lambda's before but don't know what's involved.
Thanks in advance!
EDIT
It seems there is some confusion so I'll try to clarify. I want to be able to create a string that looks like this "Object.SubObject.SubSubObject" using lambda expressions and method chaining. My example does this, but only for one level deep ( a property of a class). What I want to do is extend this to go to any depth.
For Example, I'd like to use lambda expressions with a fluent interface that would look something like this....
AddProps(x => x.Children).AddProps(xx => xx.GrandChildren) and that would add "Children.GrandChildren" to my "Properties" string list.

It might make things easier if the AddProps method is generic, instead of the entire Helper Class.
In this way you could have this code:
var helper = new Helper();
helper.AddProps<Parent>(x => x.Children);
helper.AddProps<Child>(x => x.GrandChildren);
You might also want to retain more than just the property name so that you know which type the property belongs to. I guess you could have a Dictionary to store the properties for each type you've registered.
P.S. Knowing why you want to do this might help the SO community better answer your question.

I ended up using an alternate solution that worked quite well. It uses this new class...
public class PartBuilder
{
private List<string> Parts;
/// <summary>
/// Gets a dot delimited string representing the parts
/// </summary>
public string Value
{
get
{
return string.Join(".", this.Parts.ToArray());
}
}
/// <summary>
/// Creates a new PartBuilder
/// </summary>
private PartBuilder()
{
this.Parts = new List<string>();
}
/// <summary>
/// Creates a new PartBuilder
/// </summary>
public static PartBuilder Create()
{
return new PartBuilder();
}
/// <summary>
/// Gets a property name from an expression
/// </summary>
public PartBuilder AddPart<TEntity, TProp>(Expression<Func<TEntity, TProp>> expression)
{
PropertyInfo prop = (PropertyInfo)((MemberExpression)(expression.Body)).Member;
this.Parts.Add(prop.Name);
return this;
}
}
Now that I have this new class, I can do this...
string typeHierarchy = PartBuilder.Create()
.AddPart((Parent p) => p.Children)
.AddPart((Child pp) => pp.GrandChildren)
.Value;
The variable "typeHierarchy" now has the value of "Children.GrandChildren".
It's not as elegant as I would have hoped, but it's type safe and easy to use.

Related

Linq - How to use Select then Where but Select Entire Entity?

As a stop measure, I'd like developers to get data based on specific criteria but I don't want to expose the entire object for a where clause.
dbContext.BigThing
.Select(s => new LesserThing { FieldA= s.FieldA, FieldB = s.FieldB })
.Where(someQueryExpression)
.TakeTheEntireEntity();
The someQueryExpression will be of type Expression<Func<LesserThing, bool>>.
The TakeTheEntireEntity is sudo code for, how do I get the entire data model? I can use the dbContext again as an inner Where clause but this would trigger 2 queries and evaluate client side, which is bad. One trip to the db is required.
The idea here is to allow developers to consume this service but prevent them from querying Where SomeNonIndexedField cannot be used.
There are many ways to accomplish this (none out-of-the box though), with all they requiring expression tree manipulation.
But if the goal is just to limit the fields available in the Where conditions, there is much simpler approach which works out-of-the-box and requires less coding to apply.
Just make the LesserThing interface and let BiggerThing implement it implicitly. e.g.
public interface ISomeEntityFilter
{
string FieldA { get; }
DateTime FieldB { get; }
}
public class SomeEntity : ISomeEntityFilter
{
public int Id { get; set; }
public string FieldA { get; set; }
public DateTime FieldB { get; set; }
// ... many others
}
Now, given
Expression<Func<ISomeEnityFilter, bool>> filter
coming from the caller, what you do is simply applying it and then casting back to the original type (the latter is needed because the first operation changes the generic type of the result from IQueryable<SomeEntity> to IQueryable<ISomeEntityFilter>, but you know that it sill actually holds SomeEntity elements):
var query = dbContext.Set<SomeEntity>()
.Where(filter)
.Cast<SomeEntity>();
And yes (you can easily verify that), the result is server (SQL) translatable EF Core query.
What you want to do requires working with the Expression tree to transform a lambda of the form pl => pl.prop == x to a new lambda p => p.prop == x.
This code works with either properties or fields, but assumes that the LesserThing will have only member names that also exist in the BigThing. Of course, you could also create a Dictionary and map the LesserThing member names to BigThing member names.
Since you can't infer generic type parameters from the return, you have to manually pass in the types.
var ans = dbContext.BigThing.Where(someQueryExpression.TestBigThing<BigThing,LesserThing>());
Since the type of someQueryExpression must be Expression<Func<LesserThing,bool>> it is only possible to access fields or properties of LesserThing or outside variables or constants.
public static class TestExt {
public static Expression<Func<TBig, bool>> TestBigThing<TBig, TLesser>(this Expression<Func<TLesser, bool>> pred) {
// (T p)
var newParm = Expression.Parameter(typeof(TBig), "p");
var oldMemberExprs = pred.Body.CollectMemberExpressions();
var newMemberExprs = oldMemberExprs.Select(m => Expression.PropertyOrField(newParm, m.Member.Name));
var newBody = pred.Body;
foreach (var me in oldMemberExprs.Zip(newMemberExprs))
newBody = newBody.Replace(me.First, me.Second);
// p => {newBody}
return Expression.Lambda<Func<TBig,bool>>(newBody, newParm);
}
/// <summary>
/// Collects all the MemberExpressions in an Expression
/// </summary>
/// <param name="e">The original Expression.</param>
/// <returns>List<MemberExpression></returns>
public static List<MemberExpression> CollectMemberExpressions<T>(this T e) where T : Expression {
new CollectMemberVisitor().Visit(e);
return CollectMemberVisitor.MemberExpressions;
}
/// <summary>
/// ExpressionVisitor to collect all MemberExpressions.
/// </summary>
public class CollectMemberVisitor : ExpressionVisitor {
public static List<MemberExpression> MemberExpressions;
public CollectMemberVisitor() {
MemberExpressions = new List<MemberExpression>();
}
protected override Expression VisitMember(MemberExpression node) {
MemberExpressions.Add(node);
return node;
}
}
}

Getting property name and distinct values of a type T from a List<T> with reflection

I have a class Product with a set of properties:
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public string Categories { get; set; }
}
From a component, I obtain a List<Product> and, for several reasons, I need to use Reflection to get the properties of Product and then get the Distinct values and their Count() for each property.
Is it possible to achieve my goal through reflection? If not is there any other way to do it? Thanks!
UPDATE
The problem is that I do not know in advance which properties I have to use and which properties are in the Product class. That's why I think reflection is the best option.
I can achieve the same result by using a Switch - Case construct where the switch compare the Property Name extarcted from the class and each Case corresponds to a specific Property Name. But the flexibility of this solution is not enough for my problem
So it sounds like you're asking for something slightly different than the rest of us previously thought. You're not looking for the number of distinct values, or you're looking for the number of duplicates of each distinct value, which is essentially a group-by with a count of each group.
private static Dictionary<string, Dictionary<object, int>>
getDistinctValues<T>(List<T> list)
{
var properties = typeof(T).GetProperties();
var result = properties
//The key of the first dictionary is the property name
.ToDictionary(prop => prop.Name,
//the value is another dictionary
prop => list.GroupBy(item => prop.GetValue(item, null))
//The key of the inner dictionary is the unique property value
//the value if the inner dictionary is the count of that group.
.ToDictionary(group => group.Key, group => group.Count()));
return result;
}
At one point I had broken this up into two methods, but I condensed it down a bit to the point where I don't think it's needed. If you have trouble wrapping your head around all of the levels of nesting of this query feel free to ask for further clarifications.
private static int DistinctCount<T>(IEnumerable<T> items, string property)
{
var propertyInfo = typeof(T).GetProperty(property);
return items.Select(x => propertyInfo.GetValue(x, null)).Distinct().Count();
}
Usage:
List<Product> prods = GetProductsFromSomeplace();
int distinctCountById = DistinctCount(prods, "Id");
int distinctCountByName = DistinctCount(prods, "Name");
int distinctCountByCategories = DistinctCount(prods, "Categories");
If you want to allow for a custom IEqualityComparer for the properties, you can have an overload:
private static int DistinctCount<TItems, TProperty>(IEnumerable<TItems> items,
string property,
IEqualityComparer<TProperty> propertyComparer)
{
var propertyInfo = typeof(TItems).GetProperty(property);
return items.Select(x => (TProperty)propertyInfo.GetValue(x, null))
.Distinct(propertyComparer).Count();
}
And use like so:
List<Product> prods = GetProductsFromSomeplace();
int distinctCountById = DistinctCount(prods, "Id", new MyCustomIdComparer());
Where MyCustomIdComparer implements IEqualityComparer<TProperty> (in this case IEC<int>)
I present a solution below - but ideally you should look at breaking creating an abstraction for this problem that allows an object returning an IEnumerable<T> to provide a list of 'filterable' properties of the T, along with the values that are to be used. That way whatever is returning the data from the data source can do this with full knowledge. It pushes more of the work back to your data source/service/whatever, but it makes your UI much simpler.
Since you don't know the properties - then you can do this (I'm assuming assuming an IEnumerable because I'm assuming a generic solution is out - since you say you need reflection). If you do have a typed expression (i.e. you actually have a List<Product>) then a generic solution would be better as it would remove the need to get the first item:
public Dictionary<string, IEnumerable<object>>
GetAllPropertyDistincts(IEnumerable unknownValues)
{
//need the first item for the type:
var first = unknownValues.Cast<object>().First(); //obviously must NOT be empty :)
var allDistinct = first.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(p => new
{
PropName = p.Name,
Distinct = unknownValues.Cast<object>().Select(
o => property.GetValue(o, null)
).Distinct()
}).ToDictionary(v => v.PropName, v => v.Distinct);
}
Now you have a dictionary, keyed by the property name of every distinct value for each property of every object in your untyped enumerable (well - assuming they're all of the same type or base). Note - there might be some issues with properties of certain types and the default IEqualityComparer that the Distinct extension method uses - because it's a generic method and at the moment it'll be using EqualityComparer<object>.Default - which won't necessarily work for some types.
To turn this into a generic solution you can just change the first four lines to:
public Dictionary<string, IEnumerable<object>>
GetAllPropertyDistincts<T>(IEnumerable<T> unknownValues)
{
var allDistinct = typeof(T)
With the .GetProperties(BindingFlags.Public | BindingFlags.Instance) line following, and then change the inner call unknownValues.Cast<object>().Select( to just unknownValues.Select(.
If the list is not typed with Product but indeed with an open generic parameter T and this parameter has no restriction (where T : Product) then casting can help
int count = list
.Cast<Product>()
.Select(p => p.Id)
.Distinct()
.Count();
Okay, so I got slightly carried out with my answer, but here it is... a fully fledged distinct value counter. This doesn't completely answer your question, but should be a good start towards counting a property on a given object. Using this, in conjunction to looping through all of the properties on an object, should do the trick :p
/// <summary>
/// A distinct value counter, using reflection
/// </summary>
public class DistinctValueCounter<TListItem>
{
/// <summary>
/// Gets or sets the associated list items
/// </summary>
private IEnumerable<TListItem> ListItems { get; set; }
/// <summary>
/// Constructs a new distinct value counter
/// </summary>
/// <param name="listItems">The list items to check</param>
public DistinctValueCounter(IEnumerable<TListItem> listItems)
{
this.ListItems = listItems;
}
/// <summary>
/// Gets the distinct values, and their counts
/// </summary>
/// <typeparam name="TProperty">The type of the property expected</typeparam>
/// <param name="propertyName">The property name</param>
/// <returns>A dictionary containing the distinct counts, and their count</returns>
public Dictionary<TProperty, int> GetDistinctCounts<TProperty>(string propertyName)
{
var result = new Dictionary<TProperty, int>();
// check if there are any list items
if (this.ListItems.Count() == 0)
{
return result;
}
// get the property info, and check it exists
var propertyInfo = this.GetPropertyInfo<TProperty>(this.ListItems.FirstOrDefault(), propertyName);
if (propertyInfo == null)
{
return result;
}
// get the values for the property, from the list of items
return ListItems.Select(item => (TProperty)propertyInfo.GetValue(item, null))
.GroupBy(value => value)
.ToDictionary(value => value.Key, value => value.Count());
}
/// <summary>
/// Gets the property information, for a list item, by its property name
/// </summary>
/// <typeparam name="TProperty">The expected property type</typeparam>
/// <param name="listItem">The list item</param>
/// <param name="propertyName">The property name</param>
/// <returns>The property information</returns>
private PropertyInfo GetPropertyInfo<TProperty>(TListItem listItem, string propertyName)
{
// if the list item is null, return null
if (listItem == null)
{
return null;
}
// get the property information, and check it exits
var propertyInfo = listItem.GetType().GetProperty(propertyName);
if (propertyInfo == null)
{
return null;
}
// return the property info, if it is a match
return propertyInfo.PropertyType == typeof(TProperty) ? propertyInfo : null;
}
}
Usage:
var counter = new DistinctValueCounter<Person>(people);
var resultOne = counter.GetDistinctCounts<string>("Name");
If I understand the goal, you should be able to just use LINQ:
List<Product> products = /* whatever */
var distinctIds = products.Select(p=>p.Id).Distinct();
var idCount = distinctIds.Count();
...

How do I get property names of a type using a lambda expression and anonymous type?

I am trying to use Expression Trees and anonymous types to achieve the following.
Let's say I have this class:
class Person
{
public string FirstName {get;set;}
public string MiddleName {get;set;}
public string LastName {get;set;}
public DateTime DateOfBirth {get;set;}
}
Now I want to be able to call the following:
string[] names = Foo<Person>(x=> new { x.LastName, x.DateOfBirth });
I want names to contain 2 items, "LastName" and "DateOfBirth".
I am trying to extend PetaPoco, in a compile time safe way rather than writing string sql, so that I can specify a list of properties/columns I want to include in the SQL, rather than it selecting everything. I have some pretty large entities and there are cases where I do not want to select all the columns for performance reasons.
Try this out for size:
public static string[] Foo<T, TResult>(Expression<Func<T, TResult>> func)
{
return typeof(TResult).GetProperties().Select(pi => pi.Name).ToArray();
}
As you are returning an anonymous type from your lamda, you are able loop over all the properties of this anonymous type and use the inferred names of the properties. However when using this the syntax would be more like:
Foo((Person x) => new { x.LastName, x.DateOfBirth });
This is because the second generic argument is an anoymous type.
I'm lazy so this code handles only public properties. But it should be a good base to get you started.
public static string[] Foo<T>(Expression<Func<T, object>> func)
{
var properties = func.Body.Type.GetProperties();
return typeof(T).GetProperties()
.Where(p => properties.Any(x => p.Name == x.Name))
.Select(p =>
{
var attr = (ColumnAttribute) p.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault();
return (attr != null ? attr.Name : p.Name);
}).ToArray();
}
The answers given here work when either only single property is selected, OR when multiple properties are selected. None of them work for both. The answer by Lukazoid only works for multiple properties, the rest for single property, as of writing my answer.
The code below considers both the case, that is, you can use it for selecting single property AND multiple properties. Please note that I haven't added any sanity checking here, so feel free to add your own.
string[] Foo<T>(Expression<Func<Person, T>> func)
{
if (func.Body is NewExpression)
{
// expression selects multiple properties,
// OR, single property but as an anonymous object
// extract property names right from the expression itself
return (func.Body as NewExpression).Members.Select(m => m.Name).ToArray();
// Or, simply using reflection, as shown by Lukazoid
// return typeof(T).GetProperties().Select(p => p.Name).ToArray();
}
else
{
// expression selects only a single property of Person,
// and not as an anonymous object.
return new string[] { (func.Body as MemberExpression).Member.Name };
}
}
Or more succinctly, using a ternary operator it all becomes just this:
string[] Foo<T>(Expression<Func<Person, T>> func)
{
return (func.Body as NewExpression) != null
? typeof(T).GetProperties().Select(p => p.Name).ToArray()
: new string[] { (func.Body as MemberExpression).Member.Name };
}
Download LinkPad file: LinkPad
See it online: Repl.it
Please feel free to point out anything that I may have missed.
A page of code is a thousand words, so here's how Microsoft does it in Prism:
///<summary>
/// Provides support for extracting property information based on a property expression.
///</summary>
public static class PropertySupport
{
/// <summary>
/// Extracts the property name from a property expression.
/// </summary>
/// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
/// <param name="propertyExpression">The property expression (e.g. p => p.PropertyName)</param>
/// <returns>The name of the property.</returns>
/// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when the expression is:<br/>
/// Not a <see cref="MemberExpression"/><br/>
/// The <see cref="MemberExpression"/> does not represent a property.<br/>
/// Or, the property is static.
/// </exception>
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException(Resources.PropertySupport_NotMemberAccessExpression_Exception, "propertyExpression");
}
var property = memberExpression.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException(Resources.PropertySupport_ExpressionNotProperty_Exception, "propertyExpression");
}
var getMethod = property.GetGetMethod(true);
if (getMethod.IsStatic)
{
throw new ArgumentException(Resources.PropertySupport_StaticExpression_Exception, "propertyExpression");
}
return memberExpression.Member.Name;
}
}
If you want to take attributes into account it's going to be slightly more complicated, but the general idea of accepting an Expression<Func<T>> and fishing out the name of the property being targeted is the same.
Update: As is, the method will accept only one parameter; I only provided it as a guideline. The idea can be generalized of course:
public static string[] ExtractPropertyNames<T>(
Expression<Func<T, object>> propertyExpression)
This method will accept an expression that takes a T and returns an anonymous type which you can then reflect upon. You could substitute a second type parameter for object but that doesn't really do anything here because the only thing you want to do is reflect on the type.
I guess you have to disassemble code for Html.LabelFor(LabelExtensions.LabelFor<TModel,TValue> from System.Web.Mvc assembly).
For example, look at ExpressionHelper.GetExpressionText
As for replacing member name with attribute member value - you'll have to use old fashioned reflection.

Is there a way of Implementing a Select Condition in LINQ DataContext

I am working in a MVC3 project, and i am using LINQ to SQL. I have a database schema that uses a field to indicate if the record is active or deleted (field is boolean named "Active").
Now suppose there are two table linked such as State, and City, where City references State.
Let's say i have a method that returns a list of states:
public ActionResult ListStates()
{
return View(_repository.ListStates());
}
Now, i have implemented the repository method to return all states, and i could implement it in the following way:
public class Repository
{
public IQueryable<State> ListStates()
{
return dataContext.States.Where(p => p.Active == true)
}
}
In the view i could be sure i'm using only active states. But to be sure i'm using only active cities i would need to filter it in view, which makes the view uglier, or implement a custom view model. Both cases are valid, but they require a lot of work.
I have seen there are methods in data context where we can implement certain operations before an object gets inserted/updated into database, as this examle:
public partial class DatabaseDataContext
{
partial void InsertState(State instance)
{
instance.Active = true;
this.ExecuteDynamicInsert(instance);
}
}
The above method gets executed whenever an insert of the State object is happening.
My question is, is there a way to implement a condition only in one place for an object, for example to return only active records whenever a select is performed?
If I understood correctly, you're trying to eliminate the need of specifying .Where(p => p.Active == true) on methods of your repositories and you want to define it only once.
I'm not sure whether you can achieve this without creating a data context wrapper, because for each query you have to combine two logical expressions, the expression that comes from repository and p => p.Active == true.
The most simplest solution would be as follows:
/// <summary>
/// A generic class that provides CRUD operations againts a certain database
/// </summary>
/// <typeparam name="Context">The Database context</typeparam>
/// <typeparam name="T">The table object</typeparam>
public class DataContextWrapper<Context> where Context : DataContext, new()
{
Context DataContext;
/// <summary>
/// The name of the connection string variable in web.config
/// </summary>
string ConnectionString
{
get
{
return "Connection String";
}
}
/// <summary>
/// Class constructor that instantiates a new DataContext object and associates the connection string
/// </summary>
public DataContextWrapper()
{
DataContext = new Context();
DataContext.Connection.ConnectionString = ConnectionString;
}
protected IEnumerable<T> GetItems<T>([Optional] Expression<Func<T, bool>> query) where T : class, new()
{
//get the entity type
Type entity = typeof(T);
//get all properties
PropertyInfo[] properties = entity.GetProperties();
Expression<Func<T, bool>> isRowActive = null;
//we are interested in entities that have Active property ==> to distinguish active rows
PropertyInfo property = entity.GetProperties().Where(prop => prop.Name == "Active").SingleOrDefault();
//if the entity has the property
if (property != null)
{
//Create a ParameterExpression from
//if the query is specified then we need to use a single ParameterExpression for the whole final expression
ParameterExpression para = (query == null) ? Expression.Parameter(entity, property.Name) : query.Parameters[0];
var len = Expression.PropertyOrField(para, property.Name);
var body = Expression.Equal(len, Expression.Constant(true));
isRowActive = Expression.Lambda<Func<T, bool>>(body, para);
}
if (query != null)
{
//combine two expressions
var combined = Expression.AndAlso(isRowActive.Body, query.Body);
var lambda = Expression.Lambda<Func<T, bool>>(combined, query.Parameters[0]);
return DataContext.GetTable<T>().Where(lambda);
}
else if (isRowActive != null)
{
return DataContext.GetTable<T>().Where(isRowActive);
}
else
{
return DataContext.GetTable<T>();
}
}
}
And then you can create your repositories like this:
/// <summary>
/// States Repository
/// </summary>
public class StatesRepository : DataContextWrapper<DEMODataContext>
{
/// <summary>
/// Get all active states
/// </summary>
/// <returns>All active states</returns>
public IEnumerable<State> GetStates()
{
return base.GetItems<State>();
}
/// <summary>
/// Get all active states
/// </summary>
/// <param name="pattern">State pattern</param>
/// <returns>All active states tha contain the given pattern</returns>
public IEnumerable<State> GetStates(string pattern)
{
return base.GetItems<State>(s=>s.Description.Contains(pattern));
}
}
The usage:
StatesRepository repo = new StatesRepository();
var activeStates = repo.GetStates();
and
var filtered = repo.GetStates("Al");
Hope this helps ;)
You are looking for the dynamic linq library:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I've used this before to insert a Where IsActive = true into all select statements before.

Retrieve only base class from Entity Framework

If I have three classes in entity framework.
class Base {}
class Left : Base {}
class Right : Base {}
and I call DBContext.Bases.ToList();
This returns all instances of Base fully typed into their associated inherited types, as some people have noticed, the performance of EF on large inheritance structures is not great to say the least. My actual query in my project is 600 lines long, just for returning one entity and takes 2 seconds to generate.
They query runs much faster if you tell it which type to return, as it does not have to join across the whole structure. e.g.
DBContext.Bases.OfType<Left>.ToList();
or
DBContext.Bases.OfType<Right>.ToList();
However I now want to ONLY return the base class. Unfortunalty doing
DBContext.Bases.OfType<Base>.ToList();
does the same as
DBContext.Bases.ToList();
It gets the WHOLE inheritance structure... Is there any way (without making a new type in EF) of ONLY returning the class Base when looking through the Base collection?
Sorry I cant log into my actual account...
Maybe I didnt make myself clear, I want to bring back all the objects (including Base, Left and Right) but I only want the Base class to be returned, even if in the database they are actual Left and Right classes.
OFTYPE was a good suggestion but it filters out all my entities because none are the actual Base type. But I want to return only the Base type values in the Base type object.
Any ideas?
The GetType() is not understood by Entity Framework, but the keyword is does work. As such you can build an Expression and apply it to your query. The code here should work for EF5+ to add an extension method that you can call as: query.OfOnlyType<Base, SubTypeWithDescendants>(). (Or with the same two Type arguments if you need to, my hierarchy is more complicated than that though)
public static IQueryable<ReturnType> OfOnlyType<ReturnType, QueryType>
(this IQueryable<QueryType> query)
where ReturnType : QueryType {
// Look just for immediate subclasses as that will be enough to remove
// any generations below
var subTypes = typeof(ReturnType).Assembly.GetTypes()
.Where(t => t.IsSubclassOf(typeof(ReturnType)));
if (subTypes.Count() == 0) { return query.OfType<ReturnType>(); }
// Start with a parameter of the type of the query
var parameter = Expression.Parameter(typeof(ReturnType));
// Build up an expression excluding all the sub-types
Expression removeAllSubTypes = null;
foreach (var subType in subTypes) {
// For each sub-type, add a clause to make sure that the parameter is
// not of this type
var removeThisSubType = Expression.Not(Expression
.TypeIs(parameter, subType));
// Merge with the previous expressions
if (removeAllSubTypes == null) {
removeAllSubTypes = removeThisSubType;
} else {
removeAllSubTypes = Expression
.AndAlso(removeAllSubTypes, removeThisSubType);
}
}
// Convert to a lambda (actually pass the parameter in)
var removeAllSubTypesLambda = Expression
.Lambda(removeAllSubTypes, parameter);
// Filter the query
return query
.OfType<ReturnType>()
.Where(removeAllSubTypesLambda as Expression<Func<ReturnType, bool>>);
}
I've only tested it on EF6.1 with a code-first model. It borrows heavily from Alex James' tip 35.
To answer the question that none of the above answers seem to take care of (that is, we are only filtering returned columns to only be the base type columns, but not filtering out the rows that have derived type information), there is a fairly straightforward way of doing this with anonymous types. See here for another stackoverflow question dealing with the specifics.
The idea is to do something like this:
db.BaseTypes.Select(o => new { Prop1 = o.Prop1, Prop2 = o.Prop2, ....})
.AsEnumerable()
.Select(a => new BaseType() { Prop1 = a.Prop1, Prop2 = a.Prop2, ...});
The Linq-to-Entities will return a list of anonymous objects, while the .AsEnumerable() returns you back to Linq-to-Objects and allows you to call new BaseType() with an object initializer list.
This has an unfortunate downside of being specific to the types. Somebody here at the office wants a generic one written, so I'll return soon and edit this answer with a fully generic version of this.
EDIT (tested, but not in production EntityFramework):
Thanks to this answer for the SelectDynamic code.
public static class QueryableExtensions {
/// <summary>
/// Constructs a query that only selects the columns that are actually in the type <typeparamref name="T"/> as public properties.
///
/// Useful for inherited types when you only want the base type information.
/// </summary>
/// <remarks>
/// This function materializes the query. You'll want to call the where clauses BEFORE this call (since it is an optimization).
/// </remarks>
/// <typeparam name="T">Entity type.</typeparam>
/// <param name="query">Source query.</param>
/// <returns>An IEnumerable of items of type <typeparamref name="T"/>.</returns>
public static IEnumerable<T> FilterColumnsByType<T>(this IQueryable<T> query) where T : new() {
Type type = typeof(T);
List<string> selectedProps = type.GetProperties().Select(p => p.Name).ToList();
Tuple<IQueryable, Type> anonObjectTypePair = query.SelectDynamicAndType(selectedProps);
IQueryable anonObjects = anonObjectTypePair.Item1;
Type anonType = anonObjectTypePair.Item2;
return anonObjects.Cast<object>().AsEnumerable().Select(ob => {
var ret = new T();
selectedProps.ForEach(p =>
type.GetProperty(p).SetValue(ret, anonType.GetField(p).GetValue(ob)));
return ret;
});
}
/// <summary>
/// Constructs a query that selects only the <paramref name="propNames"/> given and returns an <see cref="IQueryable"/> of dynamic objects with only the selected fields.
///
/// Also returns the type information of the dynamic objects.
/// </summary>
/// <param name="source">Source query.</param>
/// <param name="propNames">The list of properties names to select.</param>
/// <returns>A query of anonymous types defined by the supplied <paramref name="propNames"/> and the actual <see cref="Type"/> used to construct anonymous type.</returns>
public static Tuple<IQueryable, Type> SelectDynamicAndType(this IQueryable source, IEnumerable<string> propNames) {
Dictionary<string, PropertyInfo> sourceProperties = propNames.ToDictionary(name => name, name => source.ElementType.GetProperty(name));
Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);
ParameterExpression sourceItem = Expression.Parameter(source.ElementType, "t");
IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>();
Expression selector = Expression.Lambda(Expression.MemberInit(
Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);
return Tuple.Create(source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Select", new Type[] { source.ElementType, dynamicType },
Expression.Constant(source), selector)), dynamicType);
}
/// <summary>
/// Constructs a query that selects only the <paramref name="propNames"/> given and returns an <see cref="IQueryable{dynamic}"/> of dynamic objects with only the selected fields.
/// </summary>
/// <param name="source">Source query.</param>
/// <param name="propNames">The list of properties names to select.</param>
/// <returns>A query of anonymous types defined by the supplied <paramref name="propNames"/>.</returns>
public static IQueryable<dynamic> SelectDynamic(this IQueryable source, IEnumerable<string> propNames) {
return source.SelectDynamicAndType(propNames).Item1.Cast<dynamic>();
}
static class LinqRuntimeTypeBuilder {
private static AssemblyName assemblyName = new AssemblyName() { Name = "DynamicLinqTypes" };
private static ModuleBuilder moduleBuilder = null;
private static Dictionary<string, Type> builtTypes = new Dictionary<string, Type>();
static LinqRuntimeTypeBuilder() {
moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name);
}
private static string GetTypeKey(Dictionary<string, Type> fields) {
string key = string.Empty;
foreach (var field in fields.OrderBy(kvp => kvp.Key).ThenBy(kvp => kvp.Value.Name))
key += field.Key + ";" + field.Value.Name + ";";
return key;
}
private static Type GetDynamicType(Dictionary<string, Type> fields) {
if (null == fields)
throw new ArgumentNullException("fields");
if (0 == fields.Count)
throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition");
try {
Monitor.Enter(builtTypes);
string className = GetTypeKey(fields);
if (builtTypes.ContainsKey(className))
return builtTypes[className];
TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);
foreach (var field in fields)
typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public);
builtTypes[className] = typeBuilder.CreateType();
return builtTypes[className];
} catch (Exception ex) {
//log.Error(ex);
Console.WriteLine(ex);
} finally {
Monitor.Exit(builtTypes);
}
return null;
}
public static Type GetDynamicType(IEnumerable<PropertyInfo> fields) {
return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType));
}
}
}
Assuming you are able to use LINQ, could you use something along the lines of the following quick and dirty example?:
var result = from item in DBContext.Bases.ToList()
where (!item.GetType().IsSubclassOf(typeof(Base)))
select item;
Not sure about the performance differences, but i could imagine that this would be faster than loading all rows (when a lot of rows are in DB):
List<int> ids = DBContext.Rights.Select(x => x.Id).ToList();
ids.AddRange(DBContext.Lefts.Select(x => x.Id).ToList());
var bases = DBContext.Bases.Where(x => !ids.Contains(x.Id)).ToList();
You can use DbSet.SqlQuery:
DBContext.Bases.SqlQuery("select * from BaseTable").AsNoTracking().ToList();
Be aware that not using .AsNoTracking() will get you into hot water sooner or later (if there are derived types already loaded into the context you will get unique key violations/exceptions immediatelly).
I currently use the following LINQ extension, assuming sub-classes are located in the same assembly.
public static class MyLinqExtensions
{
public static IQueryable<T> OfTypeOnly<T>(this IQueryable<T> query)
{
Type type = typeof (T);
IEnumerable<Type> derivedTypes = Assembly
.GetAssembly(type)
.GetTypes()
.Where(t => t.IsSubclassOf(type));
return query.ExceptTypes(derivedTypes.ToArray());
}
public static IQueryable<T> ExceptTypes<T>(this IQueryable<T> query, params Type[] excludedTypes)
{
if (excludedTypes == null)
return query;
return excludedTypes.Aggregate(query,
(current, excludedType) => current.Where(entity => entity.GetType() != excludedType));
}
}
Usage:
var bases = DBContext.Bases.OfTypeOnly<Base>();

Categories

Resources