Find item from generic list - c#

I have a problem in fetching the record from a generic list. I have created a common function from where i want to get the records from any type of class. Below is sample code:-
public void Test<T>(List<T> rEntity) where T : class
{
object id = 1;
var result = rEntity.Where(x => x.id == id);
}
Please suggest. Thanks in advance.

With method like that a usual question for compiler is 'what is T' ?
If it's just a class it could be anything even a StringBuilder as Jon has mentioned and there is no guarantee that it has a property 'Id'. So it won't even compile the way it is right now.
To make it work we have two options :
A) Change the method and let compiler know what type to expect
B) Use reflection and use run-time operations (better avoid this when possible but may come handy when working with 3rd party libraries).
A - Interface solution :
public interface IMyInterface
{
int Id {get; set;}
}
public void Test<T>(List<T> rEntity) where T : IMyInterface
{
object id = 1;
var result = rEntity.Where(x => x.id == id);
}
B - Reflection solution:
public void Test<T>(List<T> rEntity)
{
var idProp = typeof(T).GetProperty("Id");
if(idProp != null)
{
object id = 1;
var result = rEntity.Where(x => idProp.GetValue(x).Equals(id));
}
}

You most define a basic class that have id property and your T most be inherit from that basic class.
public class BaseClass{
public object ID;
}
and you can change your function like this:
public void Test<T>(List<T> rEntity) where T : BaseClass
{
object id = 1;
var result = rEntity.Where(x => x.id == id);
}

Related

Get and Set values of properties generic type

I am actually struggling with generics :)
How can I access properties of a type passed as generic into a method? Do I need to use reflection in any case?
For example the following class
public class someClassA()
{
public int attributeA1;
public string attributeA2;
{
public class someClassB()
{
public int attributeB1;
public string attributeB2;
{
// Then a generic method
private TItem GetSomeData<TItem>(Guid Id) where TItem: new()
{
TItem instanceOfClass = new TItem();
// doing some data stuff
// ....
// finally group the result for better iteration
var groupedById = result.ToList().GroupBy(x =>
(x["SomeId"]))
.Select(grp => grp.ToList())
.ToList();
foreach (var item in groupedById)
{
// how can I access and set the properties of TItem -> instanceOfClass?
instanceOfClass.... = item.property1;
instanceOfClass.... = item.property2;
....
}
return instanceOfClass;
}
The above method is called like this for example
var data = GetSomeData<someClassA>(someId)
Any hint or advice is higly appreciated!
I have already tried with reflection but I am unsure wether this is usefull or not.

Generic method gives error

I have tried to write a generic method for the below mentioned code snippet.But it gives error on the OrderBy clause ? Could you tell me why ?
var cache = RedisConnectorHelper.Connection.GetDatabase();
var values = JsonConvert.DeserializeObject<List<StateListDto>>(cache.StringGet(AppConsts.States));
if (values != null) return new ListResultOutput<StateListDto>(values.OrderBy(o => o.Name).ToList());
Generic method :
public ListResultOutput<T> GetCache<T>(string cacheKey)
{
var cache = RedisConnectorHelper.Connection.GetDatabase();
var values = JsonConvert.DeserializeObject<List<T>>(cache.StringGet(cacheKey));
return values != null ? new ListResultOutput<T>(values.ToList().OrderBy(o=>o.Name)) : null;
}
call :
var values = GetCache<StateListDto>(AppConsts.States);
StateListDto.cs
public class StateListDto
{
public string Code { get; set; }
public string Name { get; set; }
}
It gives this error: (click to see the full size image)
If you are expecting to use this for more than just StateListDto I would suggest creating an interface or base class that does have the property called Name then you can guarantee it exists.
Something like:
public interface IDto
{
string Name { get; }
}
and then you can change your method to:
public ListResultOutput<T> GetCache<T>(string cacheKey) where T: IDto
{
var cache = RedisConnectorHelper.Connection.GetDatabase();
var values = JsonConvert.DeserializeObject<List<T>>(cache.StringGet(cacheKey));
return values != null ? new ListResultOutput<T>(values.ToList().OrderBy(o=>o.Name)) : null;
}
You can send the way you want to order by as a parameter like this:
public ListResultOutput<T> GetCache<T>(string cacheKey, Func<T,object> selector)
{
var cache = RedisConnectorHelper.Connection.GetDatabase();
var values = JsonConvert.DeserializeObject<List<T>>(cache.StringGet(cacheKey));
return values != null ? new ListResultOutput<T>(values.OrderBy(selector).ToList()) : null;
}
call :
GetCache<StateListDto>("yourKey", i=>i.Name);
In this way you don't force your class to implement anything - and you can choose to order by other parameter in your code
But all are having Name property.
Then create a common interface for them, something like this:
public interface INamed
{
string Name { get; }
}
And all your models with that property can implement that interface:
public class StateListDto : INamed
Then you can use that interface as a type constraint on the generic method:
public ListResultOutput<T> GetCache<T>(string cacheKey) where T: INamed
That way the compiler can guarantee that the type of T will have a Name property.
Note that a base class, concrete or abstract, can also be used to accomplish this. Though personally I prefer to use interfaces over inheritance unless there's a specific reason to use inheritance.

Dynamically creating objects in LINQ expression that inherit from abstract base class

I have an abstract class:
public abstract class Entity<T> where T : Tweet
{
protected string _name;
public abstract string Name { get; set; }
public abstract List<Tweet> tweets { get; set; }
}
Which is sub-classed by several different classes (their names are Person, Place and Organisation). I store lots of instances of the Entity class in a List<T> instance.
I have a separate method that is used to combine different List<Entity<Tweet>> instances into one another, joining them on Entity.Name and merging their containing List<Tweet>:
private static List<Entity<Tweet>> joinLists(List<Entity<Tweet>> list1, List<Entity<Tweet>> list2)
{
List<Entity<Tweet>> joined;
joined = list1.Union(list2)
.GroupBy(o => o.Name)
.Select(o => new Entity<Tweet> // this is wrong as class is abstract but at this point I don't know what concrete class o is?
{
Name = o.Key,
tweets =
o.SelectMany(x => x.tweets).ToList()
}).ToList();
return joined;
}
Unfortunately as Entity is an abstract class, I cannot create an instance of it, so how can I dynamically determine the actual concrete class of the o instance in the LINQ expression. I have a separate method that does this based on a string representation of an object's class name, but I would like to know how this would be achieved with LINQ (method below is my implementation for use elsewhere in my application):
private static Entity<Tweet> createEntity(string className, Tweet tweet, string value)
{
Entity<Tweet> entity = null;
Type t = Type.GetType("FinalUniProject.NERModels." + className);
entity = (Entity<Tweet>)Activator.CreateInstance(t);
entity.Name = value;
// Allow for inverted index by adding tweet to NamedEntist List<TweetModel>
if (entity.tweets == null) entity.tweets = new List<Tweet>();
entity.tweets.Add(tweet);
return entity;
}
Line of code below do not give any compile error for sure. Haven't checked all aspects. U sure this wont work ?
joined = list1.Union(list2)
.GroupBy(o => o.Name)
.Select(o =>
{
Entity<Tweet> entity = null;
string className = o.First().GetType().Name;
Type t = Type.GetType("FinalUniProject.NERModels." + className);
entity = (Entity<Tweet>)Activator.CreateInstance(t);
entity.Name = o.Key;
// Allow for inverted index by adding tweet to NamedEntist List<TweetModel>
entity.tweets = o.ToList().SelectMany(x => x.tweets).ToList();
return entity;
}).ToList();

Property Type as Generic parameter

I'm trying to figure out how I can make a Generics call take a variable for the Type. In the call below it take a type "DAL.Account" and works fine.
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
I want to change that so that I can pass a variable in place of the "DAL.Account". Something like this but I know that won't work as you can't pass property as a Type.
ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray())
Below is the shell pieces of code I think explains what I'm trying to do. Generics is not my strong suit so I'm looking for some help. Is there anyway that I can make this happen?
//Stores a "Type" that indicates what Object is a Criteria for.
public class AccountCriteria : IGeneratedCriteria
{
...
public Type EntityType
{
get {return typeof(DAL.Account);}
}
}
//I have added a function to the DataContext called "GetTable"
// And then used it as an example in a Console App to test its functionality.
public class ADRPDataContext : NHibernateDataContext
{
...
public CodeSmith.Data.NHibernate.ITable<T> GetTable<T>() where T : EntityBase
{
var tb = new CodeSmith.Data.NHibernate.Table<T>(this);
return tb;
}
}
// console application that uses DataContext.GetTable
class Program
{
static void Main(string[] args)
{
using (var ctx = new ADRPDataContext())
{
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
}
}
}
//ExistsCommand class that uses the EntityType property of the Critera to generate the data.
public class ExistsCommand
{
private IGeneratedCriteria Criteria { get; set; }
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
//This was my first attempt but doesn't work becuase you can't pass a property in for a Type.
//But I can figure out how to write this so that it will work.
Result = ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
You are looking to instantiate a generic type. Some info can be found here
This is a simple example demonstrating how to instantiate a List with a capacity of 3. Here is a method that you can call to create a generic when you don't know the type:
public static Object CreateGenericListOfType(Type typeGenericWillBe)
{
//alternative to the followin:
//List<String> myList = new List<String>(3);
//build parameters for the generic's constructor (obviously this code wouldn't work if you had different constructors for each potential type)
object[] constructorArgs = new Object[1];
constructorArgs[0] = 3;
//instantiate the generic. Same as calling the one line example (commented out) above. Results in a List<String> with 3 list items
Type genericListType = typeof(List<>);
Type[] typeArgs = { typeGenericWillBe };
Type myNewGeneric = genericListType.MakeGenericType(typeArgs);
object GenericOfType = Activator.CreateInstance(myNewGeneric, constructorArgs);
return GenericOfType;
}
And here is some sample code that will show you the example method works:
List<String> Strings = (List<String>)InstantiateGenericTypeWithReflection.CreateGenericListOfType(typeof(String));
//demonstrate the object is actually a List<String> and we can do stuff like use linq extensions (isn't a good use of linq but serves as example)
Strings.Add("frist");
Strings.Add("2nd");
Strings.Add("tird");
Console.WriteLine("item index 2 value: " + Strings.Where(strings => strings == "2").First());
In your example, replace your GetTable<Criteria.EntityType>() with CreateGenericTableOfType(Criteria.EntityType). This will return a generic table of whatever type you pass in. You will of course need to implement the method properly (handle constructor args, change List to Table etc).
I think you need to change the way you're doing this slightly, and instead use generics instead of the EntityType property. Perhaps something along the lines of the following:
// Create an abstract class to be used as the base for classes that are supported by
// ExistsCommand and any other classes where you need a similar pattern
public abstract class ExtendedCriteria<T> : IGeneratedCriteria
{
public ExistsCommand GetExistsCommand()
{
return new ExistsCommand<T>(this);
}
}
// Make the non-generic ExistsCommand abstract
public abstract class ExistsCommand
{
protected abstract void DataPortal_Execute();
}
// Create a generic sub-class of ExistsCommand with the type parameter used in the GetTable call
// where you were previously trying to use the EntityType property
public class ExistsCommand<T> : ExistsCommand
{
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
Result = ctx.GetTable<T>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
// Derive the AccountCriteria from ExtendedCriteria<T> with T the entity type
public class AccountCriteria : ExtendedCriteria<DAL.Account>
{
...
}

How to instantiate PrivateType of inner private class

I was trying to setup a unit test for a private inner class, but had very little success:
namespace Stats.Model
{
public class DailyStat
{
private class DailyStatKey // The one to test
{
private DateTime date;
public DateTime Date
{
get { return date; }
set { date = value.Date; }
}
public StatType Type { get; set; }
public override int GetHashCode()
{
return Date.Year * 1000000 +
Date.Month * 10000 +
Date.Day * 100 +
(int)Type;
}
public override bool Equals(object obj)
{
DailyStatKey otherKey = obj as DailyStatKey;
if (otherKey == null)
return false;
return (this.Date == otherKey.Date && this.StatType == otherKey.StatType);
}
}
}
}
I tried this code:
PrivateType statKeyType = new PrivateType("Stats.Model", "Stats.Model.DailyStat.DailyStatKey");
as well as
PrivateType statKeyType = new PrivateType("Stats.Model", "DailyStat.DailyStatKey");
To no avail.
The assembly's name is "Stats.Model", and to me the type name looks correct too, but I just get an exception: "System.TypeLoadException: Could not load type"
So what am I doing wrong ?
PrivateType, to the best of my knowledge, is reflection based, and I'd guess it's pretty much intended for this scenario, as you cannot have a private class directly beneath a namespace.
EDIT:
Added full implementation of DailyStatKey. What I want to test is the uniqueness of my GetHashCode method. As you can see I try to fit a date + type into a single int.
Found a solution myself:
var parentType = typeof(DailyStat);
var keyType = parentType.GetNestedType("DailyKeyStat", BindingFlags.NonPublic);
//edited to use GetNestedType instead of just NestedType
var privateKeyInstance = new PrivateObject(Activator.CreateInstance(keyType, true));
privateKeyInstance.SetProperty("Date", DateTime.Now);
privateKeyInstance.SetProperty("Type", StatType.Foo);
var hashCode = (int)privateKeyInstance.Invoke("GetHashCode", null);
You can also use PrivateType directly as well:
PrivateType statKeyType = new PrivateType("Stats.Model", "Stats.Model.DailyStat+DailyStatKey");
Nested classes have a string format that's different from their namespace (which is Stats.Model.DailyStat.DailyStatKey) so the usage isn't obvious.
Since it is private the only class that can create the instance is DailyStat itself. Unless you make it non private reflection (activator) would be your only choice if you want to create the class although that would not be a good idea as you wont be able to use it directly unless you are able to cast it to a public enough type or interface
EDIT:
Since you are trying to do this for unit testing then effectively you shouldnt test this class as it is private. You would only be able to test it through any public interface of DailyStat.
You can code a public "GetDailyStatKey" method on parent class.
public class DailyStat
{
private class DailyStatKey // The one to test
{
}
public DailyStatKey GetDailyStatKey()
{
return new DailyStatKey();
}
}
Now you can write:
DailyStat v = new DailyStat();
var x = v.GetDailyStatKey();

Categories

Resources