1

I have functions like this one below that loads a database table using LinqToSql and returns a list of the entries converted into my custom model class:

    public List<AreaModel> LoadListOfAreaModels(Boolean includeDeleted = false)
    {
        using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
        {
            IQueryable<Areas> filtered = includeDeleted
                ? dc.Areas.Where((c) => !c.DataRowDeleted)
                : dc.Areas;
            return filtered.Select((c) => AreaModel.ModelFactoryFromLinq(c)).ToList();
        }
    }

Now I have quite a few tables and all have to get loaded and converted in the same way.

Can I somehow avoid typing this function a dozen times and just changing the AreaModel and Areas to e.g. TownModel and Towns by using a generic function?

I already tried this, but failed because I could not find a way to call a static class method if I only have the generic type parameter of it.

Further info: All XxxModel classes have the common superclass ModelBase (which does not contain the static factory method I need to call though!), all Linq table classes (like Areas or Towns) implement the common interface ILinqClass.

Any ideas on how to implement this the most elegant way?


Inspired by knittl's answer, I came up with this solution:

    public List<TModel> LoadListOfModels<TLinq, TModel>(
        Func<TLinq, bool> filter,
        Func<TLinq, TModel> modelFactory
        )
        where TLinq : class, ILinqClass
        where TModel : ModelBase
    {
        using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
        {
            return dc.GetTable<TLinq>()
                .Where(filter)
                .Select(modelFactory)
                .ToList();
        }
    }
4
  • Is DataRowDeleted defined in ModelBase? Commented Apr 28, 2016 at 12:48
  • @Lee Yes, DataRowDeleted it's part of an interface ModelBase implements Commented Apr 28, 2016 at 12:49
  • The problem as you mention is that AreaModel is a static class. Why don't you try to make you AreaModel, TownModel a non static class? Then they can implement an interface, something like IModelFactory with a method ModelFactoryFromLinq and then you can pass an implementation to this interface to your LoadList... method. Commented Apr 28, 2016 at 12:51
  • @JordiRuiz AreaModel is not static, and I could not manage to put ModelFactoryFromLinq() to an interface because of the different parameter and return types. Commented Apr 28, 2016 at 12:54

3 Answers 3

2

You should be able to pass delegates/Funcs:

public List<TModel> LoadListOfModels<TDbModel, TModel>(
  Func<LinqToSqlDataContext, TDbModel> modelSelector,
  Func<TDbModel, bool> isDeleted,
  Func<TDbModel, TModel> modelFactory,
  bool includeDeleted = false)
{
    using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
    {
        var models = modelSelector(dc);
        var filtered = includeDeleted
            ? models.Where(c => !isDeleted(c))
            : models;

        return filtered.Select(modelFactory).ToList();
    }
}

For Area call as follows:

 LoadListOfModels(
   dc => dc.Areas,
   c => c.DataRowDeleted,
   Area.ModelFactoryFromLinq,
   includeDeleted);

For Town:

 LoadListOfModels(
   dc => dc.Towns,
   c => c.DataRowDeleted,
   Town.ModelFactoryFromLinq,
   includeDeleted);

You might even be able to get rid of the includeDeleted parameter. Simply pass a filter func Func<TDbModel, bool> filter.

return modelsSelector(dc)
  .Where(filter)
  .Select(modelFactory)
  .ToList();

Call:

LoadListOfModels(dc => dc.Areas, c => true, Area.ModelFactoryFromLinq);
LoadListOfModels(dc => dc.Areas, c => !c.DataRowDeleted, Area.ModelFactoryFromLinq);
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, I managed to get rid of the modelSelector parameter as well, see my edited question for my final approach.
1

Try this approach:

public abstract class BaseClass<TModel> where TModel : class
{
    public bool DataRowDeleted { get; set; }
    public abstract TModel ModelFactoryFromLinq();
}

public class Area : BaseClass<AreaModel>
{
    public override AreaModel ModelFactoryFromLinq()
    {
        return new AreaModel();
    }
}

public static List<TModel> LoadListOfAreaModels<TContext, TModel>(bool includeDeleted = false)
    where TContext : BaseClass<TModel>, new()
    where TModel : class
{
    using (var dc = new LinqToSqlDataContext())
    {
        IQueryable<TContext> filtered = includeDeleted
            ? dc.GetTable<TContext>().Where((c) => !c.DataRowDeleted)
            : dc.GetTable<TContext>();
        return filtered.Select((c) => c.ModelFactoryFromLinq()).ToList();
    }
}

IMPLEMENTATION:

List<AreaModel> result = SomeClassName.LoadListOfAreaModels<Area, AreaModel>();

Comments

0

If DataRowDeleted is defined in ModelBase you should be able to do:

public static IQueryable<TModel> LoadListOfQuery<T, TModel>(IQueryable<T> source, Expression<Func<T, TModel>> selector, bool includeDeleted = false) where T : ModelBase {
    IQueryable<T> filtered = includeDeleted ? source : source.Where(s => !s.DataRowDeleted);
    return filtered.Select(selector);
}

Then add a function to evaluate a query given a context:

public static List<T> ExecList(Func<LinqToSqlDataContext, IQueryable<T>> qf)
{
    using(var c = new LinqToSqlDataContext())
    {
         var q = qf(c);
         return q.ToList();
    }
}

Then you can do:

public List<AreaModel> LoadListOfAreaModels(bool includeDeleted = false)
{
    ExecList(c => LoadListOfQuery(c.Areas, a => AreaModel.ModelFactoryFromLinq(a), includeDeleted);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.