dynamic linq to entities to comply with DRY

82 views Asked by At

Is there a way to do the below dynamically so I don't have to repeat myself?

var result = from c in _Entities.Cars 
where c.Colour == "White"
select c;

var result = from m in _Entities.Bikes 
where m.Colour == "White"
select m;

etc...

as in:

var entities = new List<string> {"Cars", "Bikes"};
foreach (var e in entities)
{
    var result = from m in e //pseudo code
    where m.Colour == "White"
    select m;
}
4

There are 4 answers

12
LInsoDeTeh On BEST ANSWER

If Cars and Bikes implement a common interface, say IVehicle, which has the Colour property, you could define a function:

public IEnumerable<IVehicle> GetWhiteVehicles(IEnumerable<IVehicle> vehicles) {
  return vehicles.Where(p => p.Colour == "White");
}

and then

var result = GetWhiteVehicles(_Entities.Cars);
var result = GetWhiteVehicles(_Entities.Bikes);

If Cars and Bikes are generated classes from Entity Framework, you can nevertheless make them implement an interface by adding a new source file with a partial definition of the classes with the same name:

public partial class Bikes : IVehicle { }
public partial class Cars : IVehicle { }
6
MakePeaceGreatAgain On

This should work:

var entities = new List<List<dynamic>> {_Entities.Cars.ToList<dynamic>(), _Entitites.Bikes.ToList<dynamic>()};
foreach (var l in entities)
{
    var result = from m in l
        where m.Colour == "White"
        select m;
}

Thus you avoid any changes to your business-modell. However hence the dynamic-type is only resolved at runtime there is no way to ensure that the property Colour really exists on that type.

1
puzzler On

Add an interface that contains color (IColorful). Set your cars and bikes to implement that interface. Make and call a function:

List<IColorful> FindByColor(IEnumerable<IColorful> list, string color)
{
    List<IColorful> result = list.Where(item => item.Color == color).ToList();
    return result;
}

Call it like so:

var entities = FindByColor(_Entities.Cars, color);
entities.AddRange(FindByColor(_Entities.Bikes, color))
0
Jon Hanna On

First either define a base class they both derive from, or an interface they both implement. I'm going to assume from here in they implement a IColoured interface with a Colour property. (Though I'll note that it's common to use en-US in member names, so colours are represented by a Color property).

So, say your Cars class was created by EF as:

namespace SomeNamespace
{
  public partial class Cars
  {
    /*…*/
    public string Colour { get; set; }
    /*…*/
  }
}

Then you can have a file with:

namespace SomeNamespace
{
  public partial class Cars : IColoured
  {
  }
}

Because the two partial pieces of code are combined in compiling the class definition your code just needs to indicate the interface is implemented, the fact that the matching property is in another file doesn't matter.

Then:

public static IQueryable<T> FilterByColour<T>(this IQueryable<T> source, string colour)
  where T : IColoured
{
  return source.Where(p => p.Colour == colour);
}

Now you can use _Entities.Cars.FilterByColour("white"), _Entities.Bikes.FilterByColour("blue") etc. If filtering by "white" was a particularly common case then either:

Important: Never define such a method as taking an IEnumerable<> if it you don't have a good reason to (such as using something that isn't compatible with EF), use IQueryable<> so it is still being processed by EF and can be filtered on the database, rather than having everything retrieved from the database and filtered in your application.

public static IQueryable<T> FilterWhite<T>(this IQueryable<T> source)
  where T : IColoured
{
  return source.FilterByColour("white");
}

Or if you had no other use for FilterByColour then:

public static IQueryable<T> FilterWhite<T>(this IQueryable<T> source)
  where T : IColoured
{
  return source.Where(p => p.Colour == "white");
}

Would work.