Extension Methods not working for an interface - c#

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>

Related

How to use LINQ to query a [key] property using generic types?

Let's say I have a class:
public class Customer
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
And now I want to create a generic Get() method that might query Customer or any one of several other classes that also have a [key] field defined.
public T Get<T>(int id)
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(i => i. ????? = id);
}
I'm not sure how to use Linq to generically specify the [key] field.
Thanks!
Hope this helps:
public interface IBase
{
int Id { get; }
}
public class Customer : IBase
{
public string Name { get; set; }
public int Id { get ; set ; }
}
public T Get<T>(int id) where T : IBase
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(i => i.Id == id);
}
Just implement the interface IBase in all other classes.
For what is worth I think using contracts is a better way to solve this. But in case you or someone else actually need to check for the attribute here's the answer:
public static T Get<T>(int id)
{
string json = DoSomething(); // <-- making it easy for this post
List<T> items = JsonConvert.DeserializeObject<List<T>>(json);
return items.FirstOrDefault(
item => (int)item.GetType()
.GetProperties()
.FirstOrDefault(
p => Attribute.IsDefined(p, typeof(KeyAttribute))
).GetValue(item) == id
);
}
As far a this part of your question:
I'm not sure how to use Linq to generically specify the [key] field.
The attribute is KeyAttribute you can know that by navigating to the definition (pressing F12 if you're using VS or checking the docs in case your editor doesn't support this feature.
Things to consider:
this is using Reflection reasonably heavily, so it will never have the best performance. That being said you can cache the result from GetProperties() somewhere for faster lookups.
It's hardcoding the cast to int but it appears that's what you're after.
If the collection is null it'll throw an exception.

Protobuf-net KeyedCollection serialization

I need to serialize/deserialize a KeyedCollection with protobuf-net, can I just serialize a list?
If so, what is the most efficient way to convert-back the List to the KeyedCollection?
Here follows a sample code that shows the case:
public class FamilySurrogate
{
public List<Person> PersonList { get; set; }
public FamilySurrogate(List<Person> personList)
{
PersonList = personList;
}
public static implicit operator Family(FamilySurrogate surrogate)
{
if (surrogate == null) return null;
var people = new PersonKeyedCollection();
foreach (var person in surrogate.PersonList) // Is there a most efficient way?
people.Add(person);
return new Family(people);
}
public static implicit operator FamilySurrogate(Family source)
{
return source == null ? null : new FamilySurrogate(source.People.ToList());
}
}
public class Person
{
public Person(string name, string surname)
{
Name = name;
Surname = surname;
}
public string Name { get; set; }
public string Surname { get; set; }
public string Fullname { get { return $"{Name} {Surname}"; } }
}
public class PersonKeyedCollection : System.Collections.ObjectModel.KeyedCollection<string, Person>
{
protected override string GetKeyForItem(Person item) { return item.Fullname; }
}
public class Family
{
public Family(PersonKeyedCollection people)
{
People = people;
}
public PersonKeyedCollection People { get; set; }
}
Solution?
.NET Platform Extensions 6 has an implementation of the KeyedCollection, KeyedByTypeCollection Class. This has a constructor which accepts an IEnumerable. The downside to this implementation is that the keys are the items, and it doesn't appear to allow you to change that. If you're already inheriting KeyedCollection, you may as well follow the implementation here and go by Microsoft's lead; they just iterate and call Add().
See also
Uses of KeyedByTypeCollection in .Net?
Can't seem to resolve KeyedByTypeCollection?
What are .NET Platform Extensions on learn.microsoft.com?
Linq with custom base collection
Collection Initializers
Previous thoughts
I'm also trying to tackle this issue from a Linq query perspective, possibly related posts:
Cannot implicitly convert type System.Collections.Generic.List back to Object after linq query
dotnet/runtime: Why is KeyedCollection abstract?
The core issue seems to be that KeyedCollectedion does not contain a constructor that takes any form of ICollection to initialize its data with. The base class of KeyedCollection, Collection, does however. The only option seems to be writing your own constructor for your KeyedCollection class that iterates over a collection and adds each element to the current instance.
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class VariableList<T> : KeyedCollection<string, T>
{
// KeyedCollection does not seem to support explicitly casting from an IEnumerable,
// so we're creating a constructor who's sole purpose is to build a new KeyedCollection.
public VariableList(IEnumerable<T> items)
{
foreach (T item in items)
Add(item);
}
// insert other code here
}
This seems really inefficient though, so I hope someone corrects me...
Edit: John Franco wrote a blogpost wherein they hack together a solution for genericly casting a List with covariants (in 2009!) This doesn't look like a very good way to do things.
Looking at System.Linq.Enumerable's implementation of ToList, Linq also iterates and Adds to the new collection.

Using LINQ with wrapped arrays

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.

Can generics improve this design?

The PartyRoleConstraints class in the model below (on the right) is the subject of this question.
The idea is that when a client tries to associate a Party with a RoleType, the RoleType sees if it has any Constraints that should prevent a given Party from being associated. A Party is a supertype for both Person and Organization.
Here is the totally generic interface I am after:
public interface IRoleConstraint<in T>
{
Func<T, bool> IsSatisfied { get; }
string UnsatisfiedDescription { get; }
bool CanAddRole(T instance);
}
A common constraint would be by Type. So if I have role type of "husband" then I want to make sure the Party instance is a Person. Here is some implementation and a test case proving I can do this:
public class RoleConstraint<T> : IRoleConstraint<T>
{
public RoleConstraint(Func<T, Boolean> isSatisfied, string unsatisfiedDescription) {
if (isSatisfied == null) throw new ArgumentNullException("isSatisfied");
if (unsatisfiedDescription == null) throw new ArgumentNullException("unsatisfiedDescription");
IsSatisfied = isSatisfied;
UnsatisfiedDescription = unsatisfiedDescription;
}
public Func<T, bool> IsSatisfied { get; protected set; }
public string UnsatisfiedDescription { get; protected set; }
public bool CanAddRole(T instance) { return IsSatisfied.Invoke(instance); }
}
public class PartyRoleConstraint : RoleConstraint<Party>
{
public PartyRoleConstraint(Func<Party, bool> isSatisfied, string unsatisfiedDescription) : base(isSatisfied, unsatisfiedDescription) { }
}
public class PartyRoleConstrainedToType<TRequired> : PartyRoleConstraint where TRequired : Party
{
private static readonly string _unsatisfiedDescription
= string.Format("This role requires a Party instance to be a {0}", typeof(TRequired).Name);
private static readonly Func<Party, bool> _isSatisfied = instance => instance.GetType().Equals(typeof(TRequired));
public PartyRoleConstrainedToType() : base(_isSatisfied, _unsatisfiedDescription) { }
}
[Test]
public void Constraints_IfTypeConstraint_and_InstanceDoesNotMatch_False()
{
var sony = new Organization("Sony Corporation");
var constraint = new PartyRoleConstrainedToType<Person>();
_husbandRoleType.AddConstraint(constraint);
Assert.That(_husbandRoleType.CanAddRole(sony), Is.False);
}
The problem I am hitting is if I want to set up a rule based on an attribute of a subtype of Party. For example, I want the gender of the husband to be Male. I can do this with a cast, as:
[Test]
public void Constraints_IfConstraintConditionIsNotMet_False()
{
_husbandRoleType.AddConstraint(new PartyRoleConstrainedToType<Person>());
Assert.That(_husbandRoleType.CanAddRole(_arthur), Is.True);
//**** here is the cast **** //
var mustBeMale = new PartyRoleConstraint(p => ((Person)p).Gender == Gender.Male, "the husband must be male.");
_husbandRoleType.AddConstraint(mustBeMale);
Assert.That(_husbandRoleType.CanAddRole(_arthur), Is.False);
_arthur.Gender = Gender.Male;
Assert.That(_husbandRoleType.CanAddRole(_arthur), Is.True);
}
The question (finally!) is: can I use generics to avoid that cast, and if so, how?
Yes, you can get rid of the cast, but you will have to specify "Person" somewhere. It's hard to propose a "best place" to do so, without knowing more about your requirements and constraints. One option would be something like:
var mustBeMale = PartyRoleConstraint.For<Person>( p => p.Gender == ...);
Another option would be to tweek PartyRoleConstrainedToType to allow further restrictions. And example might look like this:
var combinedConstraint = new PartyRoleConstrainedToType<Person>().MustSatisfy(p => p.Gender == ...);
As said before: You will have to specify Person somewhere, but there a different options to create a nice syntax. What nice means, depends on your requirements and use cases.

c# Linq to SQL and Generics

Im quite new to generics and as a learning exercise Im trying to create a simple method that picks an entity from an entity set (table) with a specified ID.
public T GetEntity<T>(int id) where T : class
{
return db.GetTable<T>().SingleOrDefault(o => o.id == id);
}
The above obviously wont work because o in o.id is unknown and thus cannot access the property id - can someone help with how to achieve this?
If all of your entities contain an id property then you can define an interface
public interface IEntity
{
int id { get; }
}
implement it on all your classes and change your method to
public T GetEntity<T>(int id) where T : IEntity
{
return db.GetTable<T>().SingleOrDefault(o => o.id == id);
}
Such interface can be useful in all the places where you want to do something for all your entities, ie. Delete by id. You can add more properties to your interface, ie. timestamp.
If you cannot change your entity classes (for implementing a common interface), you could use a predicate function and pass it into the method
public T GetEntity<T>(int id, Func<T, int, bool> predicate) where T : class
{
return db.GetTable<T>().SingleOrDefault(o => predicate(o,id));
}
Another solution, not recommended for performance reasons, but i shall post it anyway.
public T GetEntity<T>(int id) where T : class
{
return db.GetTable<T>().AsEnumerable().SingleOrDefault(o => (int)o.GetType().GetProperty("Id").GetValue(o,null) == id);
}
You need to set a constrain on your T so the compiler can be sure there is a id property. Once way is to use interfaces, ie.
public interface IUnique
{
int id { get; set; }
}
Then you can say
public T GetEntity<T>(int id) where T : class, IUnique
{
return db.GetTable<T>().SingleOrDefault(o => o.id == id);
}

Categories