Si8
Si8

Reputation: 9225

How to use a loop to create a CAML query

How I would like the CAML Query to look like:

<Where>
  <Or>
     <Eq>
        <FieldRef Name='ename_090' />
        <Value Type='Text'>Person 1</Value>
     </Eq>
     <Or>
        <Eq>
           <FieldRef Name='ename_090' />
           <Value Type='Text'>Person 1</Value>
        </Eq>
        <Or>
           <Eq>
              <FieldRef Name='ename_090' />
              <Value Type='Text'>Person 3</Value>
           </Eq>
           <Or>
              <Eq>
                 <FieldRef Name='ename_090' />
                 <Value Type='Text'>Person 4</Value>
              </Eq>
              <Or>
                 <Eq>
                    <FieldRef Name='ename_090' />
                    <Value Type='Text'>Person 5</Value>
                 </Eq>
                 <Or>
                    <Eq>
                       <FieldRef Name='ename_090' />
                       <Value Type='Text'>Person 6</Value>
                    </Eq>
                    <Eq>
                       <FieldRef Name='ename_090' />
                       <Value Type='Text'>Person 7</Value>
                    </Eq>
                 </Or>
              </Or>
           </Or>
        </Or>
     </Or>
  </Or>
</Where>

What I tried so far:

string sC = "<Where>";
int i = myList.Count - 1;
for (int iL = 0; iL < myList.Count; iL++) //for each person add an OR and the person and then another opening OR?
{
    while (i >= 0)
    {
        sC += "<Or>";
    }
    sC += "<Eq><FieldRef Name='ename_090' /><Value Type='Text'>" + myList[iL] + "</Value></Eq>";
    while (i >= 0)
    {
        sC += "<Or>";
    }
    i--;
}
if (myList.Count > 1)
{
      sC += "</Or>";
}
sC += "</Where>";

I am just stuck at how can I use a for loop to achieve the above.

Upvotes: 1

Views: 1431

Answers (2)

M.kazem Akhgary
M.kazem Akhgary

Reputation: 19149

Here is how to do this with recursion.

List<string> myList = new List<string>
{
    "Person 1",
    "Person 2",
    "Person 3"
};

Func<string,string> Value = x => string.Format("<Value Type = 'Text'>{0}</Value>",x); // Generate Value

Func<string, string> Eq = x => string.Format("<Eq><FieldRef Name = 'ename_090'/>{0}</Eq>", x); // Generate Eq.

Func<string, int, string> Or = null;
Or = (x, i) => string.Format("<Or>{0}{1}</Or>", x,
        i < myList.Count - 2
            ? Or(Eq(Value(myList[i++])), i)
            : i == myList.Count - 2 ? Or(Eq(Value(myList[i++])) + Eq(Value(myList[i])), i) : ""); // Or with recursion call

string sC;
if(myList.Count == 2) // a bit different when length is 2.
{
    sC = string.Format("<Where>{0}</Where>", Or(Eq(Value(myList[0])) + Eq(Value(myList[1])), 2));
}
else
{
    sC = string.Format("<Where>{0}</Where>", Or(Eq(Value(myList[0])), 1)); // start generating
}

You have three methods. Value which takes Persons names. ex: myList[0],myList[1], etc...

<Value Type='Text'>Person 0</Value>

Eq obviously formats string. So when you do Eq(Value(myList[0]) you will get one Eq which for example becomes

<Eq>
    <FieldRef Name='ename_090' />
    <Value Type='Text'>Person 0</Value>
</Eq>

Then you have Or. but it also have a call to itself which is called recursion.

So when you do Or(Eq(Value(myList[0])) string becomes

<Or>
    <Eq>
        <FieldRef Name='ename_090' />
        <Value Type='Text'>Person 1</Value>
    </Eq>
    {1}
<Or>

The part {1} will call to itself to continue generating until the last element of list.

Update: As suggested by Servy Aggregate is usefull here.

Func<string, string> Value = x => string.Format("<Value Type = 'Text'>{0}</Value>", x); // Generate Value

Func<string, string> Eq = x => string.Format("<Eq><FieldRef Name = 'ename_090'/>{0}</Eq>", x); // Generate Eq.

Func<string, string> Or = x => string.Format("<Or>{0}{{0}}</Or>", x);

int i = 0;
string sC = myList.Skip(1).Aggregate(Or(Eq(Value(myList[0]))), (seed, cur) =>
{
    if (i++ < myList.Count - 2)
    {
        return string.Format(seed, Or(Eq(Value(cur))));
    }

    return string.Format(seed, Eq(Value(cur)));
});

sC = string.Format("<Where>{0}</Where>", sC);

Upvotes: 2

Servy
Servy

Reputation: 203822

The closing Or tag inside of your for loop is <Or> instead of </Or>.

First write a method to construct the Eq element for an item in your list:

public static string TextEquals(string column, string value)
{
    return string.Format(
        "<Eq><FieldRef Name='{0}' /><Value Type='Text'>{1}</Value></Eq>",
        column, value);
}

Next write a method to wrap two elements in an Or element:

public static string WrapInOr(string first, string second)
{
    return string.Format("<Or>{0}{1}</Or>", first, second);
}

Now that we have this we can take the items in your list, create equals elements for each of them, and then aggregate them all using Or elements:

var caml = myList.Select(person => TextEquals("ename_090", person))
    .Aggregate((a,b) => WrapInOr(a, b));

Outputs:

<Or>
    <Or>
        <Or>
            <Or>
                <Or>
                    <Eq>
                        <FieldRef Name="enam_090" />
                        <Value Type="Text">Person 1</Value>
                    </Eq>
                    <Eq>
                        <FieldRef Name="enam_090" />
                        <Value Type="Text">Person 2</Value>
                    </Eq>
                </Or>
                <Eq>
                    <FieldRef Name="enam_090" />
                    <Value Type="Text">Person 3</Value>
                </Eq>
            </Or>
            <Eq>
                <FieldRef Name="enam_090" />
                <Value Type="Text">Person 4</Value>
            </Eq>
        </Or>
        <Eq>
            <FieldRef Name="enam_090" />
            <Value Type="Text">Person 5</Value>
        </Eq>
    </Or>
    <Eq>
        <FieldRef Name="enam_090" />
        <Value Type="Text">Person 6</Value>
    </Eq>
</Or>

Upvotes: 4

Related Questions