Click here to Skip to main content
Click here to Skip to main content

Dynamically generate a LINQ query with a custom property

, 23 Mar 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
Dynamically generate LINQ query with a custom property.

Introduction

Last week I was playing with LINQ and it was fun till I tried to dynamically generate a query. It can be done, but not in the way I would like it to be.

Background

I made a Car class with two properties, Name and Year, and want to be able to filter my Cars List depending on the selected column in my grid which displays a List (by Name, or by Year).

While I tried to do this, I didn't like the options I found on Google (Reflection, Expressions, predicates, Dynamic LINQ library...) and thought it could be easier ...

The main problem for me seems to be that the property on which I do filtering must be set while I write my code, so I can't change the property till I change my code, or write several queries and do some on them....not good.

Then I found out that the only way (I know of) to get the name of my property is over Reflection...not nice again. So I thought why not make custom properties, properties that know their name as car.Name.PropertyName.

So I wrote this little struct:

public struct Property<T> 
{ 
  #region fields
    private string _name;
    private T _value;
   #endregion             
   #region Properties
         public string PropertyName
         {get { return _name; }set { _name = value; }}
         public dynamic PropertyValue
         {get { return _value; }set { _value = value; }
         }         
      #endregion
}

And my Car class would be like this:

public class Car
{
    
    private Property<string> _name;

    public Property<string> Name
    {
        get {_name.PropertyName = "Name";
            return _name;
        }
        set { 
            _name = value;
        }
    }

    private Property<int> _year;

        public Property<int> Year
    {
        get {_year.PropertyName = "Year";
            return _year;
        }
              set { _year = value;
          
        }
    }
}

This was OK, but the problem occurred in my buton_click on:

Car c1 = new Car(); c1.Name = "Audi";

Because c1.Name isn't a string, I add to my custom property struct an implicit operator overload like this:

public static implicit operator Property<T>(T temp )
{
    var local = new Property<T> {PropertyValue = temp};            
    return local;
}

But when I do this, I lose my original instance of Car because in this method, as you can see, I create a new Property<T>, and this new Property<T> doesn't have the PropertyName set.

That's why I have in my Car class, on every property, propertyName = "TheNameOfProperty" and then return the value.

The error was gone, but I must also be able to write something like this:

string carName = c1.Name

Also, c1.Name return Property<T> is not a string.

So I wrote a new implicit operator overload in my custom Property struct.

public static implicit operator T(Property<T> tt)
{
    return tt.PropertyValue;
}

Now everything works great.

But to use Custom Properties, I must be able to iterate through my collection of properties in my instance of Car (Name, Year....etc)

I will add a List in my Car class which will contain all the properties of the instance. I read somewhere that a HashSet collection is fast so I will use it for this purpose.

The best place for adding a property to my list is in the setter of the property after (_name=value). But there's a trick thing. When I set the value of my property:

car1.Name = "Audi"

The implicit operator Property<T> occurs and a new instance of Property is returned, so in the list of properties, we need to override the Add method so that it deletes the old instance, in this case, Name property, and replaces it with a new one.

/// <summary>
/// Custom made collection that need to be used in order custom property work correctly
/// it deletes old reference of property and add a new one
/// </summary>
public class ListOfProperties : System.Collections.Generic.HashSet<IProperty>
{
    public new bool Add(IProperty p)
    {
       int nesto = this.RemoveWhere(x => x.PropertyName.Equals(p.PropertyName));

        if (base.Add(p))
            return true;
        else
            return false;
    }
}

As you can see, we create two interfaces to make things easier, IProperty and IProperties.

public interface IProperty
{
    string PropertyName { get; set; }
    dynamic PropertyValue { get; set; }
}

public interface IProperties
{
    ListOfProperties Properties { get; }
}

So my custom property struct now looks like:

public struct  Property<T> : IProperty
{

    #region fields

    private string _name;
    private T _value;

    #endregion

    #region Properties

    public string PropertyName
    {
        get { return _name; }
        set { _name = value; }
    }
    public dynamic PropertyValue
    {
        get { return _value; }
        set { _value = value; }
    }

    #endregion 

    #region Operator Overloading


    public static implicit operator Property<T>(T temp )
    {
        var local = new Property<T> {PropertyValue = temp};
        return local;
    }
 

    public static implicit operator T(Property<T> tt)
    {
        return tt.PropertyValue;
    }

    #endregion
}

Using the code

My Car class now looks like this:

public class Car:IProperties
{
    #region Implementation of IProperties

    ListOfProperties list = new ListOfProperties();

    public ListOfProperties Properties
    {
        get { return list; }
    }

    #endregion

    private Property<string> _name;

    public Property<string> Name
    {
        get { _name.PropertyName = "Name";
            return _name;
        }
        set { 

            _name = value;
            Properties.Add(Name);
        }


    }

    private Property<int> _year;

    public Property<int> Year
    {
        get { _year.PropertyName = "Year";
            return _year;
        }
        set { _year = value;
            Properties.Add(Year);
        }
    }
}

And the template for those who have Resharper (others can skip this):

private Property<$type$> $field$;public Property<$type$> $property$
{
get { 
     $field$.PropertyName = "$property$";
     return $field$;
     }
set {
     $field$ = value;
     Properties.Add($property$);
     }
}

And finally, using our code in a form on button_click (note that "Name" can be variable, and the user must know the type of PropertyValue (for example, you must pass string value for "Name", in this case "Audi"; if you pass for example an integer, an error will occur since the type of propertyValue is dynamic).

var cars = new List<Car>();
var car1 = new Car(){Name="Audi",Year=2010};
var car2 = new Car() {Name = "Mercedes", Year = 2012}; 
cars.Add(car1);
cars.Add(car2);
var audi = (from c in cars
           let carProperties = c.Properties
           from carProperty in carProperties
           where carProperty.PropertyName == "Name" 
           && carProperty.PropertyValue == "Audi"
           select c).FirstOrDefault();

And we can also do this:

//Collection of all kinds of objects that implements IProperties
List<IProperties> AllKindsOfObjects = new List<IProperties>();

var car1 = new Car(){Name="Audi",Year=2010};

var car2 = new Car() {Name = "Mercedes", Year = 2012};

AllKindsOfObjects.Add(car1);
AllKindsOfObjects.Add(car2);


//some random object which too implements IProperties
var racun1 = new Racuni() {Ime = "Elvis", Prezime = "Begluk"};

AllKindsOfObjects.Add(racun1);

string temp = "Name";

var ListOfObjectsThatHavePropertyCalledName = from c in AllKindsOfObjects
           let objectProperties = c.Properties
           from objectProperty in objectProperties
           where objectProperty.PropertyName == temp 
           select c;

In the sample above, ListOfObjectsThatHavePropertyCalledName will have two objects, car1 and car2.

Testing speed

To test the query speed of my dynamically generated LINQ query with custom properties, I used a class with five custom properties and generated a List of 1,000,000 records in it. The initialization of objects and adding objects to the list took 6 seconds. And the query itself took only 70 milliseconds to find one object of many. My PC have 4 GB of DDR2 and a 3.2 Ghz CoreDuo CPU.

Issues

I think some things can be made better, for example it would be a lot nicer if I did not need to add properties n-times to my Properties collections, and if I could use T instead of dynamic and it really bugs me that I have to set PropertyName every time. But for now, this is it, and if I figure out a better and simpler way to do this, I will let you know Smile | :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Elvis Begluk
Other
Bosnia And Herzegovina Bosnia And Herzegovina
No Biography provided

Comments and Discussions

 
Questionabout PredicateParser class Pinmemberjoyhen12312314-Oct-14 22:16 
NewsDynamic Linq is already doing the job PinmemberPhilippe Bouteleux26-Mar-12 0:02 
GeneralRe: Dynamic Linq is already doing the job PinmemberElvis Begluk26-Mar-12 7:21 
SuggestionNot very re-usable... [modified] PinmemberAndreas Gieriet25-Mar-12 19:15 
GeneralRe: Not very re-usable... [modified] PinmemberElvis Begluk26-Mar-12 7:33 
GeneralRe: Not very re-usable... [modified] PinmemberAndreas Gieriet26-Mar-12 10:41 
GeneralRe: Not very re-usable... PinmemberElvis Begluk26-Mar-12 11:38 
GeneralRe: Not very re-usable... [modified] PinmemberAndreas Gieriet27-Mar-12 16:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 23 Mar 2012
Article Copyright 2012 by Elvis Begluk
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid