I'm new to LINQ and I'm doing a simple project to learn the features of the technology.
Currently I've got a static class that wraps an array of object (a kind of simple factory). Let's say it looks like the following:
public static class Factory
{
private static Item[] items = new Item[]
{
// items are created here
};
// ...
}
Now I can add some functions to my Factory that allow me to query the inner array, e.g.
public static Item GetByID(ItemID id)
{
var query =
from item in items
where item.ID == id
select item;
return query.First();
}
However, this requires me to modify the internals of the Factory class. Is there a way to write such queries from the 'outer world' instead ?
public class OtherClass
{
var result = from it in Factory select ...
}
?
Yes, you can. Just use linq on the Factory from the 'outer world':
public class OtherClass
{
public Item Get(ItemId id)
{
return Factory.Items.SingleOrDefault(i => i.ID == id);
}
}
Of course, to do this, you'd need to change the access modifier of the items array to be public.
There are so many options.
The easiest thing to do is just to expose a public property that allows just what you want to have allowed:
public static class Factory
{
private static Item[] items = new Item[]
{
// items are created here
};
public static IEnumerable<IReadableItem> Items{ get { return items; } }
// ...
}
The above code assumes that the Item class implements an IReadableItem interface that only has the methods and properties on it that you want to allow people to access. You could also clone the items list before returning it each time, if you're worried someone might re-cast the Items or try to modify it using reflection. Because the standard LINQ methods all work off of IEnumerable<>s, this would allow someone to effectively produce a LINQ query on your items, without exposing overmuch data.
List<string> bestItemNames = Factory.Items.Where(i => i.IsBest)
.Select(i => i.Name)
.ToList();
If you wanted to get really fancy, you could even implement your own LINQ provider. From a language perspective, LINQ expression syntax just maps to specific method names, so if you had a class that implemented a .Where() and a .Select() method, then you could implement that pretty much however you wanted, and people wouldn't know any different until they tried doing something that your methods didn't support.
One possibility is to implement IQueryable<T> for a non-static class:
public class Factory<T> : IQueryable<T>
{
protected T[] _items = new T[]{};
public Type ElementType
{
// or typeof(T)
get { return _items.AsQueryable().ElementType; }
}
public System.Linq.Expressions.Expression Expression
{
get { return _items.AsQueryable().Expression; }
}
public IQueryProvider Provider
{
get { return _items.AsQueryable().Provider; }
}
public IEnumerator<T> GetEnumerator()
{
return ( IEnumerator<T> )_items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
}
Derive non-generic class to populate array (if desired)
public class ItemFactory : Factory<Item>
{
public ItemFactory()
{
// items are created here
}
}
Create static instance
public static class Factories
{
private static ItemFactory _itemFactory = new ItemFactory();
public static ItemFactory ItemFactory { get { return _itemFactory; } }
}
Usage:
var specificItem = Factories.ItemFactory
.Where( item => item.ID == id )
.SingleOrDefault();
use an expression tree
public class OtherClass
{
public Item Get(ItemId id)
{
return Factory.Get(i => i.id == id);
}
}
and change the get method to
public Item Get(Expression<Func<Item,bool>> filter)
{
return items.SingleOrDefault(filter);
}
however, this approach makes little sense unless you are encapsulating some other logic in your factory class i.e. select only rows that are not soft deleted.
Related
Suppose I have classes set up like this:
public abstract class GenericCustomerInformation
{
//abstract methods declared here
}
public class Emails : GenericCustomerInformation
{
//some new stuff, and also overriding methods from GenericCustomerInformation
}
public class PhoneNumber : GenericCustomerInformation
{
//some new stuff, and also overriding methods from GenericCustomerInformation
}
//and more derivative classes for emails, addresses, etc ..
Then I have this function to return a specific list:
public List<GenericCustomerInformation> GetLists<T>()
{
if (typeof(T) == typeof(Alias))
{
return aliases.Cast<GenericCustomerInformation>().ToList();
}
if (typeof(T) == typeof(PhoneNumber))
{
return phoneNumbers.Cast<GenericCustomerInformation>().ToList();
}
// .. and the same for emails, addresses, etc ..
}
Now suppose I want to add to these lists using just one function:
public void AddToList<T>(T iGenericCustomerInformation)
{
GetLists<T>().Add((T)(object)iGenericCustomerInformation); //Doesn't work as intended. GetLists<T> seems to be returning lists as value, which is why any additions
}
The problem is that AddToList<T> doesn't work as intended. GetLists<T> seems to be returning lists as value, which is why any additions I do are not reflected in the primary list structure ...
So how to return the list as a reference, so that I can use that reference to do list additions through other functions ?
You're already defeating the point of generics by having all those typeof()s and if statements. That's not generic at all. I'd say just put the if statement in your AddToList() method and nix the generics.
public void AddToList(GenericCustomerInformation item)
{
Alias aliasItem = item as Alias;
if(aliasItem != null)
{
aliases.Add(aliasItem);
return;
}
PhoneNumber phoneNumberItem = item as PhoneNumber;
if(phoneNumberItem != null)
{
phoneNumbers.Add(phoneNumberItem);
}
}
Why not keep all your lists in a dictionary of lists ?
private Dictionary<Type, List<GenericCustomerInformation>> MyLists;
public List<GenericCustomerInformation> GetLists<T>()
{
return MyLists[typeof(T)];
}
public void AddToLists<T>(GenericCustomerInformation item)
{
GetLists<T>().Add(item);
}
I am new to c# and now bit confused with Generics and Entity Framework. I have two tables in database, which I retrieve in my code using Entity Framework. On my aspx page, I have a grid which should populate the data based on what table user selects.
In future there will be more tables. So I wanted to write a factory pattern to get the source list for datagrid. I can not make it to work because i am very confused.
Here is my code for BaseClass and two child classes.
static class Factory
{
public static ReportBase GetReport(string name)
{
switch (name)
{
case "Child1":
return new Child1();
case "Child2":
return new Child1();
default:
return null;
}
}
}
//Base Class
class ReportBase<T>
{
public List<T> _list;
public abstract void Load();
public abstract List<T> Filter(DateTime statrtDate, DateTime endDate);
}
//Child 1
class Child1 : ReportBase
{
public List<GetChild1> _list;
public Child1(){}
public override void Load()
{
//GetChild1 is the name of database table
var info = from p in Context.GetChild1 select p;
_list = info.ToList();
}
public List<GetChild1> Filter(DateTime startDate, DateTime endDate)
{
var filteredValues = from p in _list where p.UploadedDate <= startDate select p;
return filteredValues.ToList();
}
}
//Child 2
class Child2 : ReportBase
{
public List<GetChild2> _list;
public Child2() { }
public override void Load()
{
//GetChild2 is the name of database table
return (from p in Context.GetChild2 select p).ToList();
}
public List<GetChild2> Filter(DateTime startDate, DateTime endDate)
{
return (from p in _list where p.UploadedDate <= startDate select p).ToList();
}
}
Can Someone please correct the code accordingly? Do I have to use Generics here? I tried using it in BaseClass but it doesn't work properly, because I have to fix my child classes accordingly, for which I have no clue.
First of all, with generics you can write better code that is more readable and short.
I think it is better not to use a factory class for repositories in the end you need to know which type you are dealing with it in your code such as:
Child1 report = new Child1();
report.SomeBehaviorInChild1(); // this is not achievable with base class.
If you want to use this factory you can do the following:
ReportBase report = Factory.GetReport();
Secondly, it is a bad idea to let your list public because you don't want to those who are using your class to create a new list from their code such as:
report._list = new List();
You don't want this thing to happen to the objects in your class. So it is better to have your list private and depend only on the methods to return the data source of your report.
private List _list;
public List<T> GetDataSource()
{
return _list;
}
Thirdly, if you implement a generic reportbase you won't need to write the child classes unless they have something special that the base doesn't implement.
Fourthly, the current implementation of Filter method is soooo bad because what you are doing here is getting all the records from database then filtering them in memory. This implementation is a bad practice. A better approach is to use the IQueryable which is a deffered execution object i.e. the result won't be populated until you request it.
public List<GetChild1> FilterExample()
{
IQueryable<GetChild1> result = _context.GetChild1;
result = from p in result
where p.UploadDate < startDate;
select p;
//until this moment the query is still not send to the database.
result = result.OrderBy(p => p.UploadDate);
//when you call the ToList method the query will be executed on the database and the list of filtered and sorted items will be returned. Notice the sorting and filtering is done in the database which is faster than doing it in memory
List<GetChild1> populatedResult = result.ToList();
return populatedResult;
}
So this was a better approach to your problem. I think it is good for you to read a book called "More Effective C# 2008" it talks about queryable and linq in general.
Now if we apply this on your BaseReportClass we can get the following:
//Base Class
class ReportBase<T> where T: class
{
private DbContext _context;
public ReportBase(DbContext context)
{
_context = context;
}
//I think GetAll is a more suitable name for your method than load :D
public IQueryable<T> GetAll()
{
return _context.Set<T>();
}
public IQueryable<T> Filter(Func<T,bool> filterFunction)
{
var result = from p in GetAll()
where filterFunction(p)
select p;
return result;
}
}
Now to defining the additional behavior for the child classes.
//Child 1
class Child1 : ReportBase<GetChild1>
{
public ReportBase(DbContext context):base(context)
{
}
public List<GetChild1> FilterOnStartDate(DateTime startDate)
{
//I don't know if you are familiar with lambda expressions but if you are not you can research it on the internet.
//The parameter here is converted into a method that its parameter is "p" of type GetChild1
// and the body is "p.UploadDate < startDate".
// Note you can remove the type of the parameter because the compiler can predict it.
return Filter((GetChild1 p) => p.UploadDate < startDate).ToList();
}
}
This is the code that uses your classes:
Child1 report = new Child1(new GetChild1Entities());
report.FilterOnStartDate(DateTime.Now);
Hope this was useful.
Generics for defining how to display each entity type (or table) is the wrong approach.
Instead, using a pattern such as MVVM or MVC, where according to the type of the data (the model) you select a view.
MVVM
MVC
MVC for ASP.NET
I have a class which must implement the following property
public ICollection<IType> Items
{
get { return this.items;}
}
My question is how to implement this when the type of this.items is a List<MyType> where MyType implements IType. I need to ensure the following:
Unecessary enumeration of the list is avoided if possible
That the class can internally treat the elements of this.items as their concrete type
That external callers may add and remove elements to this collection
Thanks in advance.
How about Items being IEnumerable<IType>? IEnumerable is covariant so the code would just work with no changes. On the other hand, you could have another, dedicated method to add elements to the internal list.
class MainClass
{
public static void Main()
{
ShowMeHowToDoIt show = new ShowMeHowToDoIt();
show.Add( new TheType() );
foreach ( var item in show.Items )
{
Console.WriteLine( item );
}
}
}
public class ShowMeHowToDoIt
{
private List<TheType> items = new List<TheType>();
public void Add( TheType item ) { items.Add( item ); }
public IEnumerable<IType> Items
{
get { return items; }
}
}
public interface IType { }
public class TheType : IType { }
Like Paul mentioned, you can't have both #2 and #3. You'll have to pick one or the other, or expose the concrete type to external callers. But, for your actual requirement, your best bet is to store your collection as a List internally, and just use a method when you need to get a member by the concrete type. Something like this:
private List<IType> items = new List<IType>();
private TType GetItem<TType>(int index)
where TType : IType
{
return (TType)items[index];
}
public ICollection<IType> Items
{
get
{
return this.items;
}
}
As pointed out by #PaulPhillips in the comments to this question:
Requirements (2) and (3) are contradictory.
One approach is to change the type of Items to IEnumerable<IType> and have another property of ICollection<MyType>. This will mean some redesign but clearly I was going about this wrong anyway.
Thanks!
Either declare this.items as a List<IType> if you want to expose it as ICollection<IType> and thus allowing external callers to add ITypes that are not MyTypes.
Internally work like this on the items of the list
var myObj = this.items[i] as MyType;
if (myObj == null) {
work with this.items[i] and treat it as a IType
} else {
work with myObj which is a MyType
}
OR
declare the public property as
public ICollection<MyType> Items { get return this.items; } }
and thus allow external callers to add only items of type MyType.
I am sorry, but you cannot fulfill conditions (2) and (3) at the same time
UPDATE
Another option is to only allow external callers to get items of the list but not to add items, by using an indexer having only a getter.
public IType this[int i]
{
get { return this.items[i]; }
}
an external caller can then access items like this
var obj = new ClassImplementingThisStuff();
int i = 5;
IType x = obj[i];
Also add a count property
public int Count {
get { return this items.Count; }
}
This solution avoids unnecessary enumeration.
I think the points in the comments about this being possibly a bad design are valid, however you can still do something like this and get away with it:
interface IFruit
{
string Name { get; }
string SerialNumber { get; }
}
class Apple : IFruit
{
private string _serial = Guid.NewGuid().ToString();
public string Name {
get {
return "Apple";
}
}
public string SerialNumber {
get { return _serial; }
}
}
class AppleBasket : IEnumerable<IFruit>
{
private List<Apple> _items = new List<Apple>();
public void Add(Apple apple) {
_items.Add(apple);
}
public IEnumerator<IFruit> GetEnumerator() {
return _items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return _items.GetEnumerator();
}
}
/******************/
AppleBasket basket = new AppleBasket();
Apple apple1 = new Apple();
basket.Add(apple1);
Apple apple2 = new Apple();
basket.Add(apple2);
foreach (IFruit fruit in basket) {
Console.WriteLine(fruit.SerialNumber);
}
I would recommend you rethink your approach though.
I have found when using NHibernate and creating a one to many relationship on an object that when the many grows very large it can slow down dramatically. Now I do have methods in my repository for collecting a paged IList of that type, however I would prefer to have these methods on the model as well because that is often where other developers will look first to gather the list of child objects.
e.g.
RecipientList.Recipients will return every recipient in the list.
I would like to implement a way to add paging on all of my oen to many relationships in my models using preferably an interface but really anything that won't force a typed relationship onto the model. For example it would be nice to have the following interface:
public interface IPagedList<T> : IList<T>
{
int Count { get; }
IList<T> GetPagedList(int pageNo, int pageSize);
IList<T> GetAll();
}
Then being able to use it in code...
IList<Recipient> recipients = RecipientList.Recipients.GetPagedList(1, 400);
I have been trying to think of ways to do this without giving the model any awareness of the paging but I'm hitting my head against a brick wall at the moment.
Is there anyway I can implement the interface in a similar way that NHibernate does for IList and lazyloading currently? I don't have enough knowledge of NHibernate to know.
Is implementing this even a good idea? Your thoughts would be appreciated as being the only .NET developer in house I have no-one to bounce ideas off.
UPDATE
The post below has pointed me to the custom-collection attribute of NHibernate, which would work nicely. However I am unsure what the best way is to go around this, I have tried to inherit from PersistentGenericBag so that it has the same basic functionality of IList without much work, however I am unsure how to gather a list of objects based on the ISessionImplementor. I need to know how to either:
Get some sort of ICriteria detail for the current IList that I am to be populating
Get the mapping details for the particular property associated with the IList so I can create my own ICriteria.
However I am unsure if I can do either of the above?
Thanks
Ok I'm going to post this as an answer because it is doing mostly what I wanted. However I would like some feedback and also possibly the answer to my one caveat of the solution so far:
I've created an interface called IPagedList.
public interface IPagedList<T> : IList<T>, ICollection
{
IList<T> GetPagedList(int pageNo, int pageSize);
}
Then created a base class which it inherits from IPagedList:
public class PagedList<T> : IPagedList<T>
{
private List<T> _collection = new List<T>();
public IList<T> GetPagedList(int pageNo, int pageSize)
{
return _collection.Take(pageSize).Skip((pageNo - 1) * pageSize).ToList();
}
public int IndexOf(T item)
{
return _collection.IndexOf(item);
}
public void Insert(int index, T item)
{
_collection.Insert(index, item);
}
public void RemoveAt(int index)
{
_collection.RemoveAt(index);
}
public T this[int index]
{
get
{
return _collection[index];
}
set
{
_collection[index] = value;
}
}
public void Add(T item)
{
_collection.Add(item);
}
public void Clear()
{
_collection.Clear();
}
public bool Contains(T item)
{
return _collection.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_collection.CopyTo(array, arrayIndex);
}
int Count
{
get
{
return _collection.Count;
}
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
return _collection.Remove(item);
}
public IEnumerator<T> GetEnumerator()
{
return _collection.GetEnumerator();
}
int ICollection<T>.Count
{
get { return _collection.Count; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return _collection.GetEnumerator();
}
public void CopyTo(Array array, int index)
{
T[] arr = new T[array.Length];
for (int i = 0; i < array.Length ; i++)
{
arr[i] = (T)array.GetValue(i);
}
_collection.CopyTo(arr, index);
}
int ICollection.Count
{
get { return _collection.Count; }
}
// The IsSynchronized Boolean property returns True if the
// collection is designed to be thread safe; otherwise, it returns False.
public bool IsSynchronized
{
get
{
return false;
}
}
public object SyncRoot
{
get
{
return this;
}
}
}
I then create an IUserCollectionType for NHibernate to use as the custom collection type and NHPagedList which inherits from PersistentGenericBag, IPagedList as the actual collection itself. I created two seperate classes for them because it seemed like the use of IUserCollectionType had no impact on the actual collection to be used at all, so I kept the two pieces of logic seperate. Code below for both of the above:
public class PagedListFactory<T> : IUserCollectionType
{
public PagedListFactory()
{ }
#region IUserCollectionType Members
public bool Contains(object collection, object entity)
{
return ((IList<T>)collection).Contains((T)entity);
}
public IEnumerable GetElements(object collection)
{
return (IEnumerable)collection;
}
public object IndexOf(object collection, object entity)
{
return ((IList<T>)collection).IndexOf((T)entity);
}
public object Instantiate(int anticipatedSize)
{
return new PagedList<T>();
}
public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
{
return new NHPagedList<T>(session);
}
public object ReplaceElements(object original, object target, ICollectionPersister persister,
object owner, IDictionary copyCache, ISessionImplementor session)
{
IList<T> result = (IList<T>)target;
result.Clear();
foreach (object item in ((IEnumerable)original))
{
result.Add((T)item);
}
return result;
}
public IPersistentCollection Wrap(ISessionImplementor session, object collection)
{
return new NHPagedList<T>(session, (IList<T>)collection);
}
#endregion
}
NHPagedList next:
public class NHPagedList<T> : PersistentGenericBag<T>, IPagedList<T>
{
public NHPagedList(ISessionImplementor session) : base(session)
{
_sessionImplementor = session;
}
public NHPagedList(ISessionImplementor session, IList<T> collection)
: base(session, collection)
{
_sessionImplementor = session;
}
private ICollectionPersister _collectionPersister = null;
public NHPagedList<T> CollectionPersister(ICollectionPersister collectionPersister)
{
_collectionPersister = collectionPersister;
return this;
}
protected ISessionImplementor _sessionImplementor = null;
public virtual IList<T> GetPagedList(int pageNo, int pageSize)
{
if (!this.WasInitialized)
{
IQuery pagedList = _sessionImplementor
.GetSession()
.CreateFilter(this, "")
.SetMaxResults(pageSize)
.SetFirstResult((pageNo - 1) * pageSize);
return pagedList.List<T>();
}
return this
.Skip((pageNo - 1) * pageSize)
.Take(pageSize)
.ToList<T>();
}
public new int Count
{
get
{
if (!this.WasInitialized)
{
return Convert.ToInt32(_sessionImplementor.GetSession().CreateFilter(this, "select count(*)").List()[0].ToString());
}
return base.Count;
}
}
}
You will notice that it will check to see if the collection has been initialized or not so that we know when to check the database for a paged list or when to just use the current in memory objects.
Now you're ready to go, simply change your current IList references on your models to be IPagedList and then map NHibernate to the new custom collection, using fluent NHibernate is the below, and you are ready to go.
.CollectionType<PagedListFactory<Recipient>>()
This is the first itteration of this code so it will need some refactoring and modifications to get it perfect.
My only problem at the moment is that it won't get the paged items in the order that the mapping file suggests for the parent to child relationship. I have added an order-by attribute to the map and it just won't pay attention to it. Where as any other where clauses are in each query no problem. Does anyone have any idea why this might be happening and if there is anyway around it? I will be disappointed if I can't work away around this.
You should look into one of the LINQ providers for NHibernate. What your looking for is a way to delay-load the results for your query. The greatest power of LINQ is that it does exactly that...delay-loads the results of your queries. When you actually build a query, in reality its creating an expression tree that represents what you want to do, so that it can actually be done at a later date. By using a LINQ provider for NHibernate, you would then be able to do something like the following:
public abstract class Repository<T> where T: class
{
public abstract T GetByID(int id);
public abstract IQueryable<T> GetAll();
public abstract T Insert(T entity);
public abstract void Update(T entity);
public abstract void Delete(T entity);
}
public class RecipientRepository: Repository<Recipient>;
{
// ...
public override IQueryable<Recipient> GetAll()
{
using (ISession session = /* get session */)
{
// Gets a query that will return all Recipient entities if iterated
IQueryable<Recipient> query = session.Linq<Recipient>();
return query;
}
}
// ...
}
public class RecipientList
{
public IQueryable<Recipient> Recipients
{
RecipientRepository repository = new RecipientRepository();
return repository.GetAll(); // Returns a query, does not evaluate, so does not hit database
}
}
// Consuming RecipientList in some higher level service, you can now do:
public class RecipientService
{
public IList<Recipient> GetPagedList(int page, int size)
{
RecipientList list = // get instance of RecipientList
IQueryable<Recipient> query = list.Recipients.Skip(page*size).Take(size); // Get your page
IList<Recipient> listOfRecipients = query.ToList(); // <-- Evaluation happens here!
reutrn listOfRecipients;
}
}
With the above code (its not a great example, but it does demonstrate the general idea), you build up an expression representing what you want to do. Evaluation of that expression happens only once...and when evaluation happens, your database is queried with a specific query that will only return the specific subset of rows you actually requested. No need to load up all the records, then filter them down later on to the single page you requested...no waste. If an exception occurs before you evaluate, for whatever reason, you never even hit the database, increasing efficiency even more.
This power can go much farther than querying a single page of results. The extension methods .Skip() and .Take() are available on all IQueryable<T> and IEnumerable<T> objects, along with a whole bunch of others. In addition, you have .Where(), .Except(), .Join(), and many, many more. This gives you the power to, say, .GetAll(), then filter the possible results of that query with one or more calls to .Where(), finishing with a .Skip(...).Take(...), ending in a single evaluation at your .ToList() (or .ToArray()) call.
This would require that you change your domain somewhat, and start passing IQueryable<T> or IEnumerable<T> around in place of IList<T>, and only convert to an IList<T> at your higher-level, 'publicly facing' services.
If you are going to do something like that, I can not think of a way, you would be able to "write" to the paged collection for NH to persist. The paged collection will be read only.
If that is ok, then you could use an approach like this: http://www.acceptedeclectic.com/2007/12/generic-custom-nhibernate-collections.html
He's wrapping the PersistentGenericBag and is adding some ekstra methods, just like you describe. GetPagedList() could then be implemented with a criteria, that return an ReadOnlyCollection, as could Count - returning a long of course. The GetAll() method won't be neccessary, it will just be returning the collection it self, as far as I can see.
As for if it's a good idea, I do think it is, if you have a lot of collections, where this is an actual problem. If it's just one or two collctions, I would go with just having a method on the entity it self, that returned the collection in pages.
Inspired by the MVC storefront the latest project I'm working on is using extension methods on IQueryable to filter results.
I have this interface;
IPrimaryKey
{
int ID { get; }
}
and I have this extension method
public static IPrimaryKey GetByID(this IQueryable<IPrimaryKey> source, int id)
{
return source(obj => obj.ID == id);
}
Let's say I have a class, SimpleObj which implements IPrimaryKey. When I have an IQueryable of SimpleObj the GetByID method doesn't exist, unless I explicitally cast as an IQueryable of IPrimaryKey, which is less than ideal.
Am I missing something here?
It works, when done right. cfeduke's solution works. However, you don't have to make the IPrimaryKey interface generic, in fact, you don't have to change your original definition at all:
public static IPrimaryKey GetByID<T>(this IQueryable<T> source, int id) where T : IPrimaryKey
{
return source(obj => obj.ID == id);
}
Edit: Konrad's solution is better because its far simpler. The below solution works but is only required in situations similar to ObjectDataSource where a method of a class is retrieved through reflection without walking up the inheritance hierarchy. Obviously that's not happening here.
This is possible, I've had to implement a similar pattern when I designed a custom entity framework solution for working with ObjectDataSource:
public interface IPrimaryKey<T> where T : IPrimaryKey<T>
{
int Id { get; }
}
public static class IPrimaryKeyTExtension
{
public static IPrimaryKey<T> GetById<T>(this IQueryable<T> source, int id) where T : IPrimaryKey<T>
{
return source.Where(pk => pk.Id == id).SingleOrDefault();
}
}
public class Person : IPrimaryKey<Person>
{
public int Id { get; set; }
}
Snippet of use:
var people = new List<Person>
{
new Person { Id = 1 },
new Person { Id = 2 },
new Person { Id = 3 }
};
var personOne = people.AsQueryable().GetById(1);
This cannot work due to the fact that generics don't have the ability to follow inheritance patterns. ie. IQueryable<SimpleObj> is not in the inheritance tree of IQueryable<IPrimaryKey>