0

I need to return a set of MyClass based on a tuple value list. This tuple values are used to get the correct object from database. In order to avoid calling db many times, I'm trying to use a Union to build the query and then get all the data from db only once calling .ToList() method. In this case, if there are no results in the query I need to return a default value object. When I apply the DefaultIfEmpty method I get an error. The idea is if I receive a List of 15 tuples, I need to return 15 results, if there were no results, they should be populated with the built default Class value.

public ISet<MyClass> Post([FromBody] IList<Tuple<string, string>> codesToFilter)
{
    IEnumerable<MyClass> filteredObjectsByCodes = new List<MyClass>();

    foreach (Tuple<string, string> tupleElement in codesToFilter)
    {
        //Class built based on parameters to be returned if there are no records in db
        MyClass classDefaultValue = new MyClass(tupleElement.Item1,
            "A default property string",
            "Default text",
            tupleElement.Item2);

        var filteredObjects = (from entity in DatabaseContext.MyEntities
                               where (entity.Property1 == tupleElement.Item1 &&
                                   entity.Property4== tupleElement.Item2)
                               select new MyClass
                               (
                                   entity.Property1,
                                   entity.Property2,
                                   entity.Property3,
                                   entity.Property4
                               )
                              ).DefaultIfEmpty(classDefaultValue);

        filteredObjectsByCodes = filteredObjectsByCodes.Union(filteredObjects);
    }

    var filteredObjectsResult = new HashSet<MyClass>((filteredObjectsByCodes.ToList()));

    return filteredObjectsResult;
}

Any idea of how to accomplish this in an optimized way?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Alberto Montellano
  • 5,886
  • 7
  • 37
  • 53

2 Answers2

1

Perhaps you can remove DefaultIfEmpty and add the missing MyClasses later.

IEnumerable<MyClass> results  = filteredObjectsByCodes.ToList();

var missing = codesToFilter
    .Where(c => results.All(f => f.Property1 != c.Item1 && f.Property4 != c.Item2))
    .Select(c =>  new MyClass(tupleElement.Item1.. );

results = results.Union(missing);

return new HashSet<MyClass>(results);
Yann Olaf
  • 597
  • 3
  • 12
0

Call AsEnumerable before the call to DefaultIfEmpty. It's an operation that just doesn't make sense to perform on the DB end in the first place. Get the results from the DB, and if empty, let the application add the default item to the sequence.

To avoid performing the Union on the application side all you need to do is apply the AsEnumerable().DefaultIfEmpty(...) call after you've union-ed the various DB queries. There's no need for the DefaultIfEmpty to be performed before aggregating all of the sub-queries.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • thank you for your answer Servy, however, the default value should be built with each tuple value in the list, if I add the DefaultIfEmpty after union-ed the various DB queries , how do I get the respective tuple values in my default value? – Alberto Montellano Feb 06 '15 at 16:58