Retrieve only base class from Entity Framework - c#

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>();

Related

How to write Repository method for .ThenInclude in EF Core 2

I'm trying to write a repository method for Entity Framework Core 2.0 that can handle returning child collections of properties using .ThenInclude, but I'm having trouble with the second expression. Here is a working method for .Include, which will return child properties (you supply a list of lambdas) of your entity.
public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = _context.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query.Where(predicate).FirstOrDefault();
}
Now here is my attempt at writing a method that will take a Tuple of two Expressions and feed those into a .Include(a => a.someChild).ThenInclude(b => b.aChildOfSomeChild) chain. This isn't a perfect solution because it only handles one child of a child, but it's a start.
public T GetSingle(Expression<Func<T, bool>> predicate, params Tuple<Expression<Func<T, object>>, Expression<Func<T, object>>>[] includeProperties)
{
IQueryable<T> query = _context.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty.Item1).ThenInclude(includeProperty.Item2);
}
return query.Where(predicate).FirstOrDefault();
}
Intellisense returns an error saying "The type cannot be inferred from the usage, try specifying the type explicitly". I have a feeling it's because the expression in Item2 needs to be classified as somehow related to Item1, because it needs to know about the child relationship it has.
Any ideas or better techniques for writing a method like this?
I found this repository method online and it does exactly what I wanted. Yared's answer was good, but not all the way there.
/// <summary>
/// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method default no-tracking query.
/// </summary>
/// <param name="selector">The selector for projection.</param>
/// <param name="predicate">A function to test each element for a condition.</param>
/// <param name="orderBy">A function to order elements.</param>
/// <param name="include">A function to include navigation properties</param>
/// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param>
/// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns>
/// <remarks>This method default no-tracking query.</remarks>
public TResult GetFirstOrDefault<TResult>(Expression<Func<TEntity, TResult>> selector,
Expression<Func<TEntity, bool>> predicate = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
bool disableTracking = true)
{
IQueryable<TEntity> query = _dbSet;
if (disableTracking)
{
query = query.AsNoTracking();
}
if (include != null)
{
query = include(query);
}
if (predicate != null)
{
query = query.Where(predicate);
}
if (orderBy != null)
{
return orderBy(query).Select(selector).FirstOrDefault();
}
else
{
return query.Select(selector).FirstOrDefault();
}
}
Usage:
var affiliate = await affiliateRepository.GetFirstOrDefaultAsync(
predicate: b => b.Id == id,
include: source => source
.Include(a => a.Branches)
.ThenInclude(a => a.Emails)
.Include(a => a.Branches)
.ThenInclude(a => a.Phones));
I had the same issue since EF Core doesn't support lazy loading but i tried to get workaround in the following way:
First create an attribute class to mark our desired navigation properties from other properties of a given class.
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
public class NavigationPropertyAttribute : Attribute
{
public NavigationPropertyAttribute()
{
}
}
Extension methods to filter out navigation properties and apply Include/ThenInclude using string based Eager loading.
public static class DbContextHelper
{
public static Func<IQueryable<T>, IQueryable<T>> GetNavigations<T>() where T : BaseEntity
{
var type = typeof(T);
var navigationProperties = new List<string>();
//get navigation properties
GetNavigationProperties(type, type, string.Empty, navigationProperties);
Func<IQueryable<T>, IQueryable<T>> includes = ( query => {
return navigationProperties.Aggregate(query, (current, inc) => current.Include(inc));
});
return includes;
}
private static void GetNavigationProperties(Type baseType, Type type, string parentPropertyName, IList<string> accumulator)
{
//get navigation properties
var properties = type.GetProperties();
var navigationPropertyInfoList = properties.Where(prop => prop.IsDefined(typeof(NavigationPropertyAttribute)));
foreach (PropertyInfo prop in navigationPropertyInfoList)
{
var propertyType = prop.PropertyType;
var elementType = propertyType.GetTypeInfo().IsGenericType ? propertyType.GetGenericArguments()[0] : propertyType;
//Prepare navigation property in {parentPropertyName}.{propertyName} format and push into accumulator
var properyName = string.Format("{0}{1}{2}", parentPropertyName, string.IsNullOrEmpty(parentPropertyName) ? string.Empty : ".", prop.Name);
accumulator.Add(properyName);
//Skip recursion of propert has JsonIgnore attribute or current property type is the same as baseType
var isJsonIgnored = prop.IsDefined(typeof(JsonIgnoreAttribute));
if(!isJsonIgnored && elementType != baseType){
GetNavigationProperties(baseType, elementType, properyName, accumulator);
}
}
}
}
Sample POCO classes implementing NavigationPropertyAttribute
public class A : BaseEntity{
public string Prop{ get; set; }
}
public class B : BaseEntity{
[NavigationProperty]
public virtual A A{ get; set; }
}
public class C : BaseEntity{
[NavigationProperty]
public virtual B B{ get; set; }
}
Usage in Repository
public async Task<T> GetAsync(Expression<Func<T, bool>> predicate)
{
Func<IQueryable<T>, IQueryable<T>> includes = DbContextHelper.GetNavigations<T>();
IQueryable<T> query = _context.Set<T>();
if (includes != null)
{
query = includes(query);
}
var entity = await query.FirstOrDefaultAsync(predicate);
return entity;
}
Json result for sample class C would be:
{
"B" : {
"A" : {
"Prop" : "SOME_VALUE"
}
}
}
Back in EF6 we could write something like this:
query.Include(t => t.Navigation1, t => t.Navigation2.Select(x => x.Child1));
And it was perfect and simple. We could expose it in an repository without dragging references from the EF assembly to other projects.
This was removed from EF Core, but since EF6 is open-source, the method that transforms the lambda expressions in paths can easily be extracted to use in EF Core so you can get the exact same behavior.
Here's the complete extension method.
/// <summary>
/// Provides extension methods to the <see cref="Expression" /> class.
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Converts the property accessor lambda expression to a textual representation of it's path. <br />
/// The textual representation consists of the properties that the expression access flattened and separated by a dot character (".").
/// </summary>
/// <param name="expression">The property selector expression.</param>
/// <returns>The extracted textual representation of the expression's path.</returns>
public static string AsPath(this LambdaExpression expression)
{
if (expression == null)
return null;
TryParsePath(expression.Body, out var path);
return path;
}
/// <summary>
/// Recursively parses an expression tree representing a property accessor to extract a textual representation of it's path. <br />
/// The textual representation consists of the properties accessed by the expression tree flattened and separated by a dot character (".").
/// </summary>
/// <param name="expression">The expression tree to parse.</param>
/// <param name="path">The extracted textual representation of the expression's path.</param>
/// <returns>True if the parse operation succeeds; otherwise, false.</returns>
private static bool TryParsePath(Expression expression, out string path)
{
var noConvertExp = RemoveConvertOperations(expression);
path = null;
switch (noConvertExp)
{
case MemberExpression memberExpression:
{
var currentPart = memberExpression.Member.Name;
if (!TryParsePath(memberExpression.Expression, out var parentPart))
return false;
path = string.IsNullOrEmpty(parentPart) ? currentPart : string.Concat(parentPart, ".", currentPart);
break;
}
case MethodCallExpression callExpression:
switch (callExpression.Method.Name)
{
case nameof(Queryable.Select) when callExpression.Arguments.Count == 2:
{
if (!TryParsePath(callExpression.Arguments[0], out var parentPart))
return false;
if (string.IsNullOrEmpty(parentPart))
return false;
if (!(callExpression.Arguments[1] is LambdaExpression subExpression))
return false;
if (!TryParsePath(subExpression.Body, out var currentPart))
return false;
if (string.IsNullOrEmpty(parentPart))
return false;
path = string.Concat(parentPart, ".", currentPart);
return true;
}
case nameof(Queryable.Where):
throw new NotSupportedException("Filtering an Include expression is not supported");
case nameof(Queryable.OrderBy):
case nameof(Queryable.OrderByDescending):
throw new NotSupportedException("Ordering an Include expression is not supported");
default:
return false;
}
}
return true;
}
/// <summary>
/// Removes all casts or conversion operations from the nodes of the provided <see cref="Expression" />.
/// Used to prevent type boxing when manipulating expression trees.
/// </summary>
/// <param name="expression">The expression to remove the conversion operations.</param>
/// <returns>The expression without conversion or cast operations.</returns>
private static Expression RemoveConvertOperations(Expression expression)
{
while (expression.NodeType == ExpressionType.Convert || expression.NodeType == ExpressionType.ConvertChecked)
expression = ((UnaryExpression)expression).Operand;
return expression;
}
}
Then you can use it like this (put it in an QueryableExtensions class or something like that):
/// <summary>
/// Specifies related entities to include in the query result.
/// </summary>
/// <typeparam name="T">The type of entity being queried.</typeparam>
/// <param name="source">The source <see cref="IQueryable{T}" /> on which to call Include.</param>
/// <param name="paths">The lambda expressions representing the paths to include.</param>
/// <returns>A new <see cref="IQueryable{T}" /> with the defined query path.</returns>
internal static IQueryable<T> Include<T>(this IQueryable<T> source, params Expression<Func<T, object>>[] paths)
{
if (paths != null)
source = paths.Aggregate(source, (current, include) => current.Include(include.AsPath()));
return source;
}
And then in your repository you call it normally like you would do in EF6:
query.Include(t => t.Navigation1, t => t.Navigation2.Select(x => x.Child1));
References:
How to pass lambda 'include' with multiple levels in Entity Framework Core?
https://github.com/aspnet/EntityFramework6
When I need .ThenInclude I add my dbcontext class as dependency injection and write my query directly from dbcontext reference. I don't know if it is good or bad practice.

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.

Nested Lambda for use with Fluent Interface

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.

Categories

Resources