Domanda

Im using Linq to return IDs from 4 cascading dropdown menus.

The user may have selected 1 or more values from either 1 or all of the menus. From the users selections, Im then quering the text columns of a DataTable for the representative IDs.

If the user selects from the 4th (and lowest level) dropdown, then they would have selected from all those above.

If the user selects "X" from menu 1, "Y" from menu 2, and nothing in the others i would expect to see results1 with say 10 rows where "X" exists in [Col_1], which would then be queried down to say 5 rows in results2 where "Y" exists in [Col_2].

EDIT The Code (in a basic form)

var results1 = 
    from a in dt.AsEnumerable()
    where stringArray1.Contains([Col1])
    select a;


var results2 = 
    from a in results1
    where stringArray2.Contains([Col2])
    select a;

var results3 = 
    from a in results2
    where stringArray3.Contains([Col3])
    select a;

var results4 = 
    from a in results3
    where stringArray4.Contains([Col4])
    select a;

var results = results4 ?? results3 ?? results2 ?? results1;

results4 depends on the results from results3, results3 on results2, and results2 on results1.

It may be that results4 - 2 are empty , and therefore I was wondering if I could coalesce these into a final variable, eg:

var results = results4 ?? results3 ?? results2 ?? results1;

This is throwing the error:

Operator '??' cannot be applied to operands of type 'System.Collections.Generic.IEnumerable<AnonymousType#4>'

Does anyone have any cunning ideas how to resolve this? Im trying not to write very long winded code, handling every possible outcome.

Thank you all in advance (hopefully the question makes sense!)

CM

È stato utile?

Soluzione

So based on the queries you're using, none of the results will ever be null. Some of them may be empty, but they won't be null. This means that the ?? operator won't ever do anything useful.

How do you want this to behave? Do you want to say, "if this result is empty, use this other one?" For that use the method I've written below.

public static IEnumerable<T> Coalesce<T>(IEnumerable<T> first, IEnumerable<T> second)
{
  if(first == null || !first.Any())
  {
    return second;
  }
  else
  {
    return first;
  }
}

This could then be used as:

Coalesce(results4, Coalesce(results3, Coalesce(results2, results1)));

The result of this is, "return result4, unless it's empty. If it's empty return results3. If results3 is empty return results2, if results2 is empty return results1."

You could make it an extension method if you think you'd use it enough. It would make it prettier, but would also clutter up intellisense for all of the other IEnumerables so it may not be worth it.

Altri suggerimenti

If you didn't want to write long winded code you could do

public static IEnumerable<T> SomeOrAll<T>(
    this IEnumerable<T> source, 
    Predicate<T> where)
{
    var results = source.Where(where)
    return results.Any() ? results : source;
}

var results = dt.AsEnumerable()
    .SomeOrAll(a => a.stringArray4.Contains([Col4]))
    .SomeOrAll(a => a.stringArray3.Contains([Col3]))
    .SomeOrAll(a => a.stringArray2.Contains([Col2]))
    .SomeOrAll(a => a.stringArray1.Contains([Col1]));

but, I suspect, if you step back a bit, you could do something else. If people needed this extension often it would already exist (perhaps.)

I'd rewrite the query to solve the problem, rather than dealing with it after the fact.

public List<string> FilterResults(string first, string second, string third, string fourth)
{
    var query = from a in dt.AsEnumerable()
                let firsts = first != null ? a.Where( x => x.stringArray1.Contains( first ) ) : null,
                let seconds = second != null ? firsts.Where( x => x.stringArray2.Contains( second ) ) : null,
                let thirds = third != null ? seconds.Where( x => x.stringArray3.Contains( third ) ) : null,
                let fourths = fourth != null ? thirds.Where( x => x.stringArray4.Contains( fourth ) ) : null
                select fourths ?? thirds ?? seconds ?? firsts;
    return query.ToList();
}

I didn't run this though VS but the general idea should come across.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top