Question

I am trying to make a dynamic linq query that will check for values based on a string.

First of all, here's the query:

objQry = from o in m_Db.OBJECTS.Where(whereConditions)
                      select o;

if(!objQry.Any())
{
    return null;
}

The whereConditions variable is a string I build and pass as parameter to find out the values I need. Here's examples of valid string:

OBJ_NAME == \"Sword\" and OBJ_OWNER == \"Stan\" This will return any item whose name is "Sword" and owner is "Stan;

OBJ_COLOR == \"Blue\" OR OBJ_COLOR == \"Red\" This will return any item which color is either blue or red.

Up to there, I'm fine, but now I have a problem: I need to check a decimal field. So I've tried this string:

OBJ_NUMBER == 1

But the query returns null even if there are objects which OBJ_NUMBER value is 1. It's a decimal. How can I indicate the query that they need to check for a decimal value?

**** EDIT ****

I have tried to "modify" the value passed so that it looks like this:

"CARD_NUMBER == Convert.ToDecimal(1)"

And now I have a different kind of error telling me this:

LINQ to Entities does not recognize the method 'System.Decimal ToDecimal(Int32)' method, and this method cannot be translated into a store expression.

Any clues anyone? I'm still looking for a way to do this. Thanks!

EDIT 2

You can get an example of how my code is shaped by looking at this question.

Let's come back at this problem. I want to check decimal values. Let's say that OBJ_NUMBER is a decimal field.

Using Dynamic Linq, I tried to read the decimal field. Say that I want to get each object which number is 1.27. The whereConditions field would then be shaped like this:

OBJ_NUMBER == 1.27

But then I would get an Invalid real literal '1.27' error. I don't know why.

So I have tried Gert Arnold's solution and done this instead:

decimal bDecimal = decimal.Parce(valueToParse);

param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = bDecimal };

valuesToUse.Add("CARD_NUMBER == @cardNumber");

listParams.Add(param);

But I ended up having 2 problems:

  1. The first problem is that my whereConditions string is shaped this way:

    CARD_NUMBER == @cardNumber

    But I get the following error:

    No property or field 'cardNumber' exists in type 'CARD'

    Leading me to believe that it cannot make the link between the object parameter and the string used to do the query.

  2. As you can see, I have a list of Params. This is because I cannot know for sure how many parameters the user will chose. So each time the user enters a new search field, I have to create a new ObjectParameter and store it in a list. Here's how I try to do the thing after:

    ObjectParameter[] arrayParameters = listParams.ToArray(); // Convert the list to an array

And then, when I try to make the query:

cardQry = from c in mDb.CARD.Where(whereConditions, arrayParameters)
                               select c;

But to no avail.

RESULTS

Based on the answered question below, I have developped something "awful", yet functional.

First of all, I ignore every decimal fields because I could never reach them with dynamic linq. Instead, I do this:

var valuesToParse = keyValuePair.Value.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);

// Here I parse the value and, if that's the case, the symbol.

decimal baseValue = decimal.Parse(valuesToParse[0]); 

if (valuesToParse.Count() > 1)
{
    string baseMethod = valuesToParse[1];

    if (baseMethod == ">" || baseMethod == ">=")
    {
        if (baseMethod == ">=")
        {
            baseValue--;
        }

        // The list is actually like this: Dictionary<string, object> list = new Dictionary<string, object>();
        list.Add("low", baseValue);

        // I kind of activate a tag telling me that the user is looking for a higher value.
        cardHigher = true;
    }
    else
    {
        if (baseMethod == "<=")
        {
            baseValue++;
        }


        list.Add("low", baseValue);

        cardLower = true;
    }
}
else
{
    //lowParam = new ObjectParameter("dec", typeof(decimal)) { Value = baseValue };

    list.Add("low", baseValue);
}

cardNumberActivated = true;

At the end, when I get the list of objects, I do this:

if (list.Count > 0)
{

    (example)
    if (cardNumberActivated)
    {
        if (cardHigher)
        {
            q = mDb.CARD.Where("CARD_NUMBER >= @0", list["low"]).ToList();
        }
        else if (cardLower)
        {
            q = mDb.CARD.Where("CARD_NUMBER <= @0", list["low"]).ToList();
        }
        else
        {
            q = mDb.CARD.Where("CARD_NUMBER == @0", list["low"]).ToList();
        }
    }
}

// Here we get the orinalData with the basic filters.
listToReturn.AddRange(cardQry);

if (q != null)
{
    //listToReturn.AddRange(q);
    for (int i = 0; i < listToReturn.Count; i++)
    {
        var priceList1 = listToReturn[i];
        if (!q.Any(_item => _item.CARD_NUMBER == priceList1.CARD_NUMBER))
        {
            listToReturn.RemoveAt(i);
            i--;
        }
    }
}

And it works. This is not an elegant way to make it work, but I can validate the fields the way I wanted, and for this, I am thankful at last.

Was it helpful?

Solution

You should not build a query string with inline predicate values. Use parameters in stead. Then will also be able to specify the type:

var whereConditions= "it.CARD_NUMBER = @cardNumber";
var param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = 1 };
objQry = from o in m_Db.OBJECTS.Where(whereConditions, param);

Edit

I don't know what doesn't work in your code. Here's just a random piece of working code derived from one of my own projects:

var param1 = new ObjectParameter("dec", typeof(decimal)) { Value = 90000m };
var param2 = new ObjectParameter("int", typeof(int)) { Value = 90000 };
var q = ValueHolders.Where("it.DecimalValue >= @dec OR it.IntegerValue > @int",
                    param1, param2).ToList();

Note that param1, param2 could also be an array of ObjectParameter.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top