15

The given code

static public int Q()
{
    return Enumerable.Range(0, 100)
        .Select(i => i)
        .First();
}

emits the following warning:

warning : CodeContracts: requires unproven: Any(source)

If I remove .Select() clause it disappears.

But it's not clear to me what exactly I need to .Ensure so that the cccheck was satisfied.

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • in this code, why are you using Select method...This method is used when you want to transform source into new form – Viru Apr 14 '16 at 08:45
  • 1
    @Viru it's a minimum viable example crafted specifically to demonstrate the issue. In real code there are more LINQ methods chained for sure. – zerkms Apr 14 '16 at 08:46
  • 1
    @Szeki https://github.com/Microsoft/CodeContracts – zerkms Apr 14 '16 at 08:49
  • 1
    I expect CodeContracts cannot evaluate the result of the Select query, since this depends on the data present in the enumerable. Now, in your specific case it is obvious that the enumerable contains data through the way your create and query the enumeration. However, CodeContracts probably don't analyze any boundary conditions and treats the Select() as a method that can always potentially create an empty enumeration. – Wicher Visser Apr 14 '16 at 08:50
  • ok got it...I guess it says there is a chance of empty collection so use Any method check before you use First method – Viru Apr 14 '16 at 08:50
  • @WicherVisser yep, that's my guess as well, and I'm in desperate need of a solution that is different from "split the expression into separate steps and assert each one separately" :-) – zerkms Apr 14 '16 at 08:51
  • 1
    What if you use FirstorDefault()?? – Viru Apr 14 '16 at 08:57
  • -- a solution that is different from "split the expression" -- Well, if you craft defensive programming by hand here you have to split the expression; I do not see the problem for doing the same thing using codecontracts. – jlvaquero Apr 14 '16 at 08:57
  • @Viru then it's fine (and it's expected) – zerkms Apr 14 '16 at 08:58
  • 1
    Well, in your case you can just ignore the warning :) – Fabjan Apr 14 '16 at 09:00
  • @jlvaquero splitting up a Linq query could be an issue if Linq is used against the DB (or any other source), where the Linq expressions are used as filters to reduce the amount of data send across the wire. Ideally, linq queries should be joined as much as possible before a ToList() or any other method that materializes the query is executed. – Wicher Visser Apr 14 '16 at 09:00
  • @Wicher Visser I was assuming common sense spliting the query if is deferred i.e.: `var res = Enumerable.Range(0, 100).Select(i => i).Take(1); Contract.Assume(res.Any())//or res.Count() > 0; return res.First();` – jlvaquero Apr 14 '16 at 09:38

2 Answers2

1

Since this solves the problem and is still not that ugly as one might have thought initially, I'm posting it as an answer (if one has better ideas I'm open for your suggestions though):

static public int Q()
{
    var e = Enumerable.Range(0, 100)
        .Select(i => i);

    Contract.Assume(e.Any());
    return e.First();
}

So I should not have to split the whole expression, but the part that the static analyzer was afraid of, and for that part I could assure it that it's "all fine, trust me, I know what I'm doing".

A note:

for some reason neither

Contract.Assert(e.Count() > 0);

or

Contract.Assert(e.Any());

work.

Important: as other people mention, this might be not suitable for all cases, since the additional e.Any() call would materialize the collection, which may be undesirable in some cases (eg: when it's a LINQ from a 3rd party source).

zerkms
  • 249,484
  • 69
  • 436
  • 539
  • 1
    You should probably use `Contract.Assume(e.Any());` just for good form. – Matthew Watson Apr 14 '16 at 09:15
  • As @Wicher Visser said in question's comment; splitting `Select` and `First` will cause to; i.e. Entity Framework; not including TOP1 in the generated SQL Query and hurts performance. – jlvaquero Apr 14 '16 at 09:45
  • I think so. I think that as soon `e.Any()` is called the query is executed and stored in memory. In the best case, a query goes to database to get `Any()` result and `First()` query do the TOP1 but you are doing 2 queries then. Anyway it is unconfirmed for me. I am just guessing. – jlvaquero Apr 14 '16 at 09:52
  • @jlvaquero that's true. My point was that it's not splitting itself will cause something, but *another call* that will materialise the collection. – zerkms Apr 14 '16 at 09:57
1

Can you avoid the warning with this code?

var res = Enumerable.Range(0, 100).Select(i => i).Take(1); //execute one query with TOP1 and store in memory
Contract.Assume(res.Any()); //or res.Count() > 0 //query already in memory
return res.First(); //query already in memory
jlvaquero
  • 8,571
  • 1
  • 29
  • 45
  • Yep, that definitely is an improvement for my answer (for the cases when using `.Any()` may be not desired). – zerkms Apr 14 '16 at 10:29