Generic method using Linq and inheritance to bind to object - c#

I'm struggling to get my head around how to do the following:
I have several methods that return different strongly-typed IEnumerable objects.
These strongly-typed class share a common base class that exposes properties I want to access in a Linq selector.
However I can't seem to get this working. If I just pass the base type in the method then I get errors when binding the IEnumerable because the properties available in the derived class are not available.
If I try to pass the type then because the Linq expression does not know the type I can't access the properties that I need in my Linq expression.
I need to somehow tell the Linq expression that my IEnumerable of type is derived from my base class.
Below is an example of what I'm trying to do:
private IEnumerable<MyStronglyTypedResultSet> GetReportDetails()
{
// this returns the IEnumerable of the derived type
}
public class MyBaseClass
{
public Guid UserId {get; set;}
public string OfficeName {get; set;}
}
public class MyStronglyTypedResultSet : MyBaseClass
{
public string FullName {get; set;}
public int Age {get; set;}
}
public void MyProblemMethod<T>(IEnumerable<T> allData, string officeToFind)
{
// How do I tell Linq that my <T> type is derived from 'MyBaseClass' so I can access the 'OfficeName' property?
IEnumerable<T> myData = allData.Where(c => c.OfficeName .ToLower().Equals(officeToFind.ToLower()));
MyUsefulObject.DataSource= myData; // This needs to have access to the properties in 'MyStronglyTypedResultSet'
MyUsefulObject.DataaBind();
}

You can use the OfType extension method.
public void MyProblemMethod<T>(IEnumerable<T> allData, string officeToFind)
{
// How do I tell Linq that my <T> type is derived from 'MyBaseClass' so I can access the 'OfficeName' property?
IEnumerable<T> myData = allData.OfType<MyBaseClass>.Where(c => c.OfficeName .ToLower().Equals(officeToFind.ToLower()));
MyUsefulObject.DataSource= myData;
MyUsefulObject.DataaBind();
}

Change your method like below
public void MyProblemMethod<T>(IEnumerable<T> allData, string officeToFind) where T : MyBaseClass
{
// How do I tell Linq that my <T> type is derived from 'MyBaseClass' so I can access the 'OfficeName' property?
IEnumerable<T> myData = allData.Where(c => c.OfficeName .ToLower().Equals(officeToFind.ToLower()));
MyUsefulObject.DataSource= myData; // This needs to have access to the properties in 'MyStronglyTypedResultSet'
MyUsefulObject.DataaBind();
}

Related

Using a Query Object In A C# Expression to Return A different type

I currently have a data object for the sake of argument I'll call it foo...
public class Foo
{
public int IndexedKey { get; set;}
public string NonIndexedData {get; set;}
}
I have a generic repository that I want to query this object with, however due to permissions to the database I'm not allowed to do a full table scan. I've therefore been tasked with creating safe query objects.
public class FooQuery
{
public int IndexedKey
}
The generic repository allows an arbitrary predicate and it currently has an implementaation similar to the following...
public class FooRepo : IGenericRepo<Foo>
{
private ICollection<Foo> _allFooRecords; //Imagine this is populated
public ICollection<Foo> GetWhere(Expression<Func<Foo, bool>> criteria)
{
return _allFooRecords.Where(criteria.Compile())
}
}
I want to be able to do the following...
public class FooRepo : IGenericRepo<Foo, FooQuery>
{
private ICollection<Foo> _allFooRecords; //Imagine this is populated
public ICollection<Foo> GetWhere(Expression<Func<FooQuery, bool>> criteria)
{
return _allFooRecords.Where(criteria.Compile())
}
}
The above won't compile. I know that FooQuery's properties definitely contain the right fields that match what is the indexed property of the Foo class, but I can no longer user the criteria.Compile because it will return a function that is incompatible with searching a Foo collection. Is there a way to make this work with the above signature, and what would I need to change about my implementation to get this to work correctly.
Many Thanks
your requirement is: Automatically translate view model expression Expression<Func<FooQuery, bool>> to real model expression Expression<Func<Foo, bool>>. FooQuery is actually your ViewModel, but Foo is real Model.
Automapper could do the magic.
//wrap the translate in base class, so you don't have to do it in each repo
public class BaseRepo<TEntity, TViewMode> : IGenericRepo<TEntity, TViewMode>
{
....
public IWhatever<TEntity> Where(Expression<Func<TViewMode, bool>> vmExpression)
{
var expression = Mapper.Map<Expression<Func<TEntity, bool>>>(vmExpression);
return whateverDataOrQuery.Where(expression);
}
}
Full official document is here:
Expression Translation: https://github.com/AutoMapper/AutoMapper/blob/master/docs/Expression-Translation-(UseAsDataSource).md

Get attribute on or name of derived class's property

We have a BaseClass and a set of derived POCO classes (see DerivedClassA). They map to an existing key-value store in the database that we cannot change at this point.
Each property on the derived class will map to a key in the store. The keys that the properties represent are string values that are either the property name or (if present) the custom attribute MyAttribute.Key as demonstrated on PropertyC below. The common use case for the attribute is if the key begins with an integer which is invalid for a C# property name (and we cannot change the keys).
public class BaseClass
{
public int BaseClass Id { get; set; } = 0;
}
public class DerivedClassA : BaseClass
{
public int PropertyA { get; set; }
public int PropertyB { get; set; }
[MyAttribute(Key = "404Property")]
public int PropertyC { get; set; }
}
In code, we need to get the key values as strings. After some wrestling and digging from other SO Answers (I do not claim any level of expertise with generics), we came up with the GetKey() method in the derived BaseClass<T> below. Note that GetCustomAttributeValue<T>() is a custom helper method that returns the value of an attribute (there may be a better way to do that, but that's out of scope for this question).
public class BaseClass<T> : BaseClass where T : BaseClass<T>
{
public string GetKey(Expression<Func<T, object>> property)
{
var memberInfo = GetMemberInfo(property);
return GetAttributeKey(memberInfo) ?? memberInfo?.Name;
}
private static MemberInfo GetMemberInfo(Expression<Func<T, object>> property) =>
(property.Body as MemberExpression ?? ((UnaryExpression)property.Body).Operand as MemberExpression)?.Member;
private static string GetAttributeKey(MemberInfo member) =>
member.GetCustomAttributeValue<string>(typeof(MyAttribute), "Key");
}
This solution seems to work if we derive the classes from the new BaseClass<T>
public class DerivedClassA : BaseClass<T> {
...
}
The GetKey() is now called as follows:
var derivedClassA = new DerivedClassA();
var propertyCKey = derivedClassA.GetKey(p => p.PropertyC);
We have a requirement that BaseClass needs to stay around, however, and we do not want the complexity of both the non-generic and the generic versions of BaseClass.
When I try to move GetKey() into the non-generic BaseClass, it no longer has the T type of the derived class which it needs to lookup the set of properties on the derived class. I do not want to add duplicate GetKey()s in each derived class.
Question:
Is there a way to move the GetKey() method (possibly re-writing it) into BaseClass rather than introducing the new BaseClass<T> only for supporting GetKey()?
Additional Background:
We are trying to wrap object-oriented/strong typing around a data store that is just a table that looks like:
| PreferenceId | UserId | PreferenceString |
Each derived class represents a different PreferenceId. Each PreferenceString is just a string of key/values "serialized" in a way custom to that PreferenceId (there is no rhyme/reason that can be shared across all PreferenceIds). This should all be redesigned at some point, but we are trying to wrap the current store in some kind of strong typing as a step in the transition.
As for me, all this structure in general seems to be crazy and overly complex.
Consider rewriting the whole approach instead of changing the GetKey method.
In general, GetKey in your base class breaks single-responsibility principle.
If I had to do this, I would just extract this functionality into a static class:
public static class CustomKeysHelper
{
public static string GetKey<T>(Expression<Func<T, object>> property) where T : BaseClass
{
var memberInfo = GetMemberInfo(property);
return GetAttributeKey(memberInfo) ?? memberInfo?.Name;
}
private static MemberInfo GetMemberInfo<T>(Expression<Func<T, object>> property) =>
(property.Body as MemberExpression ?? ((UnaryExpression)property.Body).Operand as MemberExpression)?.Member;
private static string GetAttributeKey<T>(MemberInfo member) =>
member.GetCustomAttributeValue<string>(typeof(MyAttribute), "Key");
}
// Usage:
string cKey = CustomKeysHelper.GetKey<DerivedClassA>(dca => dca.PropertyC);
Yes, it makes every GetKey call look longer, but it separates this logic and makes your intention clear.
Just in case you have instance of object BaseClass and want to extract property name from instance, but not type, then you can an extension method:
public static class CustomKeysHelper
{
// ... see above
public static string GetKey<T>(this T obj, Expression<Func<T, object>> property) where T : BaseClass
{
return GetKey<T>(property);
}
}
// Now, you can do this:
DerivedClassA derAInstance = ...;
derAInstance.GetKey(dca => dca.PropertyC);

Expression<TDelegate> on EF Include Statement

Hello so I am trying to make an some a bit more dynamic, that said i would like to be able to pass in an expression that will include the entities that I am trying to include. when i am trying to do this i keep getting an error that says:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
I have googled this error and saw that they were doing what i can get to work:
context.Contacts.Include(contact=>contact.PhoneNumber)
what I am trying to do is this:
Func<IEntity, IEntity> func = (contact) => ((Contact)contact).PhoneNumber;
Expression<Func<IEntity, IEntity>> expression = (contact)=> func(contact);
context.Contacts.Include(expression);
can someone please expain what I am doing wrong and why?
public interface IEntity
{
int Id { get; set; }
}
public class Contact:IEntity
{
public int Id {get;set;}
public string FirstName { get; set; }
public string LastName { get; set; }
public int PhoneNumberId { get; set; }
public PhoneNumber PhoneNumber { get; set; }
}
public class PhoneNumber:IEntity
{
public int Id { get; set; }
public string Number { get; set; }
}
UPDATE:
I have a repository class that looks at the type of class and uses reflection to get the correct DbSet and returns a IQueryable.
public IQueryable Get(IEntity t)
{
var setMethod = typeof(DbContext)
.GetMethod(nameof(DbContext.Set))
.MakeGenericMethod(t.getType());
var query = (IQueryable)setMethod.Invoke(db, null);
var results = query...
}
I have a table control that goes and gets the correct data using the typeRepository. So I am trying to be able to include entities to that table.
When you don't have a generic type argument, but simple Type parameter, you'd better use the non generic DbContext and IQueryable services provided by EF.
First, you don't need reflection - DbContext provides non generic Set method with Type argument:
public virtual DbSet Set(Type entityType)
As for Include, you can simply use the non generic Include extension method with string argument:
public static IQueryable Include(this IQueryable source, string path)
So the method in question can be implemented like this:
IQueryable query = db.Set(t.GetType());
if (t is Contact)
query = query.Include(nameof(Contact.PhoneNumber));
Not the best OOP practices, but works for the chosen design.
I believe your issue relates to the delegate type you are assigning to your Func. IE: Func<IEntity, IEntity> func
In which case IEntity does not have a navigation property defined of type IEntity
Try assigning the concrete type for your implementation
Func<Contact, PhoneNumber> func

assign linq result to an object variable and then get value from that object Variable

I have a situation that needs to assign a LINQ result to a Button.Tag property.
and when that button clicks, iterate throughout that LINQ result placed in the Button.Tag
HINT : LINQ result is type of List<anonymousType>. for some reason, i don't what to return List<KnownType>
any idea?
EDIT : As you all suggested, i reconsider problem and decided to create a specific class type and put DataTableRowId in the class instead of whole DataTableRow thing.
therefore anonymous Type Like new {Class1=c1, Class2=c2, DataTableRow3=dr3} changed to
class of type:
public class CustomClass
{
public Class1 c1 { get; set; }
public Class c2 { get; set; }
public int DataTableRow3Id dr3 { get; set; }
}
You can not access anonymous types this way, You can make a custom class and create the result of linq to that custom type. Assign this object to tag and later type cast it back to your custom type.

C# - Passing the object type in a method parameter

I'm trying to pass a object type to a method. I'm doing this for my CRUDRepository that's inherited by others repositories, but i can't figure out how to know with type i'm handling.
For example:
public PageOf<Entity> GetPageOfEntity(int pageNumber, int pageSize)
{
// Here i need to work with the entity type to make calls to database
}
The Entity is a object that's inherited by other entities (user, products, etc) and this method is returning a paged result. I'm doing this to avoid creating a GetPageOf method for each entity that i've.
What's the proper way to pass to the method the object type that i want to deal in paged results?
Edit:
Here's a example of the Entity
public abstract class Entity
{
public virtual long Id { get; set; }
public virtual DateTime Created { get; set; }
}
And the user object:
public class User : Entity
{
public string FirstName { get; set; }
}
As i'm trying to write a single class to handle some crud operations, i need to know in the method what object i'm using (but i'm trying to now create one method for each object type)
Thanks
Make the method generic:
public PageOf<TEntity> GetPageOfEntity<TEntity>(int pageNumber, int pageSize)
where TEntity : Entity
{
Type entityType = typeof(TEntity);
...
}
I am not sure, I understand your question correctly, but can't you just use a generic parameter?
public PageOf<Entity> GetPageOfEntity<TEntity>(int pageNumber, int pageSize)
where TEntity : Entity
{
// Here i need to work with the entity type to make calls to database
}

Categories