0
votes

Why doesn't my linq query work?

I suspect it may have something to do with lazy loading. It seems to work in linqpad.

public IList<PaymentDto> GetDocumentPayments(int documentId, bool? allowRepeatPayments, byte[] paymentStatuses, int[] paymentMethods)
    {
        using (var ctx = ObjectContextManager<MyDataContext>.GetManager("MyDataContext"))
        {
            var payments = new List<PaymentDto>();

            var ps = new List<byte>();        
            if (paymentStatuses != null)
            {
                ps = paymentStatuses.ToList();
            }

            var pm = new List<int>();
            if (paymentMethods != null)
            {
                pm = paymentMethods.ToList();
            }

            IQueryable<Payment> data = 
                   from payment in ctx.ObjectContext.Documents.OfType<Payment>()
                   where
                       ps.Contains(payment.Status) && 
                       pm.Contains(payment.Method) &&
                       payment.DocumentId == documentId &&
                       (allowRepeatPayments == null || payment.AllowRepeatPayments == allowRepeatPayments)
                   orderby payment.Id
                   select payment;

            foreach (var p in data) // Fails here
            {
                payments.Add(ReadData(p));
            }

            return payments;
        }
    }

Throws error: A CollectionType is required. Parameter name: collectionType.

2
Throws an error on what? What's the full exception? What line is it referring to? What's the ReadData() method doing? etc. - Jeff Mercado
See "Fails here" comment in the code. ReadData is just a property copier. :) - Kev

2 Answers

1
votes

Constructs like (allowRepeatPayments == null || payment.AllowRepeatPayments == allowRepeatPayments) can do funny thing to a query. Try what happens when you do:

if (allowRepeatPayments.HasValue)
{
    data = data.Where(p => p.AllowRepeatPayments == allowRepeatPayments);
}

You can do the same for paymentStatuses and paymentMethods.

It may solve your problem, but if not, it is an improvement anyway, because the condition is only added when it is necessary and the SQL is not cluttered when it isn't.

0
votes

The exception is thrown by internal methods of the framework while translating LINQ query to SQL, with the same kind of constructs for null checks as the one you are using (for example

.Where(data => x & y & (values == null || values.Contains(data.value)));)

I just experienced the same exception on a server running the earliest version of .Net 4 RTM (4.0.30319.1). It comes from

   exception : System.ArgumentException: A CollectionType is required.
   Parameter name: collectionType

   at System.Data.Common.CommandTrees.ExpressionBuilder.Internal.ArgumentValidation.ValidateNewEmptyCollection(TypeUsage collectionType, DbExpressionList& validElements)
   at System.Data.Objects.ELinq.ExpressionConverter.NewArrayInitTranslator.TypedTranslate(ExpressionConverter parent, NewArrayExpression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) ...

The error is very rare on the internet, and it doesn't happen with same conditions and newer versions, so it seems to have been fixed on more recent versions of .Net.

The suggestion of Gert Arnold will probably also allow to avoid the error, but the form above is oftenly used (like is SQL counterpart) and it's short and useful.

So for those who still encounter this error and don't understand why it works on some machine and sometimes not, I suggest to check their .Net FW version.