4

I have a generic Repo class that exposes a database LINQ provider:

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get()
    {
        return _queryable;
    }
}

(IPersisted is a simple marker interface for persisted objects).

Now... I would like to find an elegant way to inject a default LINQ expression for certain derived types of IPersisted. For example, for the following IPersisted implementation:

class Deletable : IPersisted
{
    bool IsDeleted { get; set; }
}

I want the IQueryable<T> Get() method to return _queryable.Where(q => !q.IsDeleted), only when T is of type Deletable.

I thought about creating some type of dictionary map IDictionary<Type, Expression>, and doing a lookup on typeof(T) inside the Get method but I'm not sure that I'll be able to strongly type the expressions in this case.

How can I inject a "default" LINQ expression into the Get method, based on the type of T? I'd like to have an extensible, object oriented way of mapping types to default expressions.

Zaid Masud
  • 13,225
  • 9
  • 67
  • 88
  • I doubt you'll make anything perfectly strongly-typed, since in the C# type system you can't express a constraint "a mapping from `T` to something generic featuring `T`, for arbitrary values of `T`" – millimoose Sep 26 '13 at 13:27
  • Sounds like your `Repo` should be derived from a base class rather than an interface. – Khan Sep 26 '13 at 13:32
  • 1
    What about intorducing IDeletable interface. Then you could check if T is oftype IDeletable and return your query the way you want it? – epitka Sep 26 '13 at 13:33
  • @Zaid, what is the type of expression here? Is it not going to be `Func` always? – nawfal Sep 26 '13 at 13:34
  • @nawfal yes we can assume it will always be `Func` – Zaid Masud Sep 26 '13 at 13:43
  • @epitka it's fine to have the mapping at an interface level. But you'd still need to do a lookup and I'm not sure how to do that. Can you share a code sample? – Zaid Masud Sep 26 '13 at 13:45
  • @Khan the `Repo` is not derived from anything at all in this code sample. In fact, it only specifies a [generic constraint](http://msdn.microsoft.com/en-us/library/d5x73970.aspx) of `IPersisted` – Zaid Masud Sep 26 '13 at 13:49
  • something like typeof(T).IsAssignableFrom(typeof(IDeletable)) ? – epitka Sep 26 '13 at 13:56

2 Answers2

3

Since each IPersisted type is going to have its own expression, I would make that a constraint on the part of IPersisted. So now you have some kind of polymorphism.

Something like?

interface IPersisted<T> where T: IPersisted<T>
{
    Expression<Func<T, bool>> Predicate { get; }
}

class Repo<T> where T : IPersisted<T>, new()
{
    public IQueryable<T> Get()
    {
        var dummy = new T();
        return _queryable.Where(dummy.Predicate);
    }
}

class Deletable : IPersisted<Deletable>
{
    public Deletable()
    {

    }

    public Expression<Func<Deletable, bool>> Predicate
    {
        get { return x => !x.IsDeleted; }
    }

    bool IsDeleted { get; set; }
}

I think what you need here is some kind of static polymorphism, but since C# doesnt offer that, you might need create a dummy instance for yourself, just to get the expression.


If you can't have a default constructor, then you can rely on FormatterServices.GetUninitializedObject(t). You can adjust your Get method like this:

public IQueryable<T> Get()
{
    var dummy = (T)FormatterServices.GetUninitializedObject(typeof(T));
    return _queryable.Where(dummy.Predicate);
}

Two things out of possibly many things to note about FormatterServices.GetUninitializedObject:

  1. It will not initialize anything or run the constructor, but that shouldn't be a problem for us.

  2. It's relatively slower. Shouldn't be a big deal since you can cache the instances :) Something like:

    class Repo<T> where T : IPersisted<T>
    {
        //caching mechanism: this is run only once per instance; you can make it 
        //static if this shud be run only once the entire lifetime of application
        readonly T dummy = (T)FormatterServices.GetUninitializedObject(typeof(T));
    
        public IQueryable<T> Get()
        {
            return _queryable.Where(dummy.Predicate);
        }
    }
    

If the exact expression is not important, then you can get rid of the object instantiation. Something like:

interface IPersisted<T> where T: IPersisted<T>
{
    Func<T, bool> Predicate { get; }
}

class Repo<T> where T : IPersisted<T>
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(x => x.Predicate(x));
    }
}

class Deletable : IPersisted<Deletable>
{
    public Func<Deletable, bool> Predicate
    {
        get { return x => !x.IsDeleted; }
    }
}

If preserving the original definition of IPersisted is important, then you can make it non-generic. Not sure if that would make it any less strongly-typed.

interface IPersisted
{
    Expression<Func<object, bool>> Predicate { get; }
}

class Repo<T> where T : IPersisted
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(dummy.Predicate);
    }
}

class Deletable : IPersisted
{
    public Expression<Func<object, bool>> Predicate
    {
        get { return x => !((Deletable)x).IsDeleted; }
    }
}

The above approach can be made more strongly typed by going for a method in IPersisted but need not be good enough a constraint:

interface IPersisted
{
    Expression<Func<T, bool>> GetPredicate<T>() where T : IPersisted;
}

class Repo<T> where T : IPersisted
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(dummy.GetPredicate<T>());
    }
}

class Deletable : IPersisted
{
    Expression<Func<T, bool>> IPersisted.GetPredicate<T>() //made it explicit
    {
        return x => ((Deletable)(object)x).IsDeleted;
    }
}

Note: Make the Predicate implementation explicit if it doesn't make sense outside the Repo<T> class.

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • This is useful. The only problem is that I have to introduce an empty constructor constraint, and I've created most of my `IPersisted` implementations without empty constructors in order to force initialization of required fields. Static polymorphism would be wonderful for this case. – Zaid Masud Sep 26 '13 at 14:16
  • @ZaidMasud another way of tackling is to bring in some kind of hacks, is performance important? – nawfal Sep 26 '13 at 14:18
  • Well I suppose performance is always important :) But what kind of hacks did you have in mind? Are you thinking of something like using `Activator` with default constructor types? – Zaid Masud Sep 26 '13 at 14:21
  • @ZaidMasud, even for activator, you need a default constructor (and public). I will edit my answer :) – nawfal Sep 26 '13 at 14:21
  • 1
    `FormatterServices.GetUninitializedObject`... good one. Learned something new. Will try it out and if things work out I'll accept the answer. Thanks. – Zaid Masud Sep 26 '13 at 15:01
  • @ZaidMasud Pls catch the edit. I made a terrible mistake by going after a dictionary mechanism which is all unwanted. A static variable per generic type itself acts a cache. More here: http://stackoverflow.com/questions/686630/static-generic-class-as-dictionary – nawfal Sep 27 '13 at 13:55
0

Not sure if we're 100% on the same page but this get method example will take a linq Func which you can use as a where clause to filter your query.

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get(Func<T, bool> filter)
    {
        return _queryable.Where(filter);
    }
}

Edit: Not sure if this will translate well to sql but to make sure i have the right idea of what you want (and maybe spark a light) : Have you considered something like this?

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get()
    {
        if (typeof (T).IsAssignableFrom(typeof (IDeletable)))
        {
            return _queryable.Where(o => ((IDeletable) o).Deleted = false).AsQueryable();
        }
        return _queryable;
    }
}
Kristof
  • 3,267
  • 1
  • 20
  • 30
  • If I was to go this route the usage would become something like `Get(q => !q.IsDeleted)` and I'd rather just keep it as is and say `Get().Where(q => !q.IsDeleted)`. The point here is for the `Get` method to automatically inject the expression based on the type, without the consumer getting involved. – Zaid Masud Sep 26 '13 at 13:47
  • Regarding your edit... yes this is exactly the type of thing I need, but in a more extensible manner as I want to have these expressions for multiple interfaces in addition to `IDeletable`. I'd rather not do it using a long list of `if` statements if I can avoid it. – Zaid Masud Sep 26 '13 at 14:05
  • 1
    Have you considered a strategy pattern where you hold a list of say IFilters. Each filter has 2 methods : bool DoesFilterApplyForType(Type) and another method to actually apply the filter if the first method returned true. You could inject the list of IFilters using dependency injection if you want. – Kristof Sep 26 '13 at 14:10