Search code examples
c#linquniondefaultifempty

Use DefaultIfEmpty to get default class values when using Union


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?


Solution

  • 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);