-
Notifications
You must be signed in to change notification settings - Fork 10
Home
Additional Resources
- Camlex Online http://camlex.ru.net
- Blog http://sadomovalex.blogspot.com
I. Camlex architecture and public interface Generally Camlex – is translator from lambda expression into CAML:
It receives expression like
x => (int)x["ID"] == 1
on input and translate it to valid CAML query. The exact signature of lambda expressions depends on method call of Camlex public interface. The most important interface in Camlex.NET assembly is IQuery and its implementation – Query class:
public interface IQuery
{
IQuery Where(Expression<Func<SPListItem, bool>> expr);
IQuery WhereAll(IEnumerable<Expression<Func<SPListItem, bool>>> expressions);
IQuery WhereAny(IEnumerable<Expression<Func<SPListItem, bool>>> expressions);
IQuery OrderBy(Expression<Func<SPListItem, object>> expr);
IQuery OrderBy(Expression<Func<SPListItem, object[]()>> expr);
IQuery OrderBy(IEnumerable<Expression<Func<SPListItem, object>>> expressions);
IQuery GroupBy(Expression<Func<SPListItem, object>> expr);
IQuery GroupBy(Expression<Func<SPListItem, object[]()>> expr, bool? collapse, int? groupLimit);
IQuery GroupBy(Expression<Func<SPListItem, object>> expr, bool? collapse, int? groupLimit);
IQuery GroupBy(Expression<Func<SPListItem, object>> expr, int? groupLimit);
IQuery GroupBy(Expression<Func<SPListItem, object>> expr, bool? collapse);
XElement[]() ToCaml(bool includeQueryTag);
string ToString();
string ToString(bool includeQueryTag);
}
As you can see most methods returns itself – so fluent interfaces calls are available with Camlex:
var caml =
Camlex.Query()
.Where(x => x["Status"] != null)
.GroupBy(x => x["CreatedBy"]).ToString();
Also notice that it has ToCaml() method which returns valid xml in array of XElement objects. It means that it can be simply customizable – for example you can use Linq to Xml or classes from System.Xml.Linq namespace in order to modify resulting query.
II. Lambda expressions syntax Internally Camlex uses expression trees in order to parse lambda expressions and translate it to CAML. There are 2 types of syntaxes allowed in Camlex (here and below “x” – is a parameter for lambda expression with SPListItem data type defined in Microsoft.Sharepoint.dll):
x => (int)x["ID"] == 1
I.e. you write integer rvalue and cast lvalue to int
x => x["ID"] == (DataTypes.Integer)"1"
In opposite with native syntax you write integer value in string representation and cast it to DataTypes.Integer class (helper class defined in Camlex.NET.dll) Two examples above are equivalent for Camlex. We introduced 2 types of syntaxes in order to be able to write queries with not only native .Net types (int, string, bool, DateTime), but also with Sharepoint data types (see http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfieldtype.aspx). You can easy mix these syntaxes and composite them via logical operations (&&, ||) in the same expression. The following schema shows how Camlex translates lambda expression with native into CAML query: String based syntax is very similar except Camlex uses rvalue cast to DataTypes.Integer in order to determine type of value and checks that string representation contains valid value for this type. What happens if Camlex cannot parse lambda expression? As described above there are only 2 types of syntaxes (and their combination via logical && and || operations) are valid for Camlex. If provided expression has different signature – then CamlexNET.NonSupportedExpressionException will be thrown.
III. Supported operations The list of operations available in CAML query can be found on http://msdn.microsoft.com/en-us/library/ms467521.aspx. Camlex supports all of them. The following table shows mapping from C# operations into CAML with examples:
CAML | C# | Example |
---|---|---|
And | && | Camlex.Query().Where(x => (string) x["Status"] == "Completed" && (int) x["ID"] == 1) |
Or | || | Camlex.Query().Where(x => (string)x["Status"] != "Completed" || x["Status"] == null) |
BeginsWith | String StartsWith() | Camlex.Query().Where(x => ((string)x["Title"]).StartsWith("Camlex")) |
Contains | String.Contains() | Camlex.Query().Where(x => ((string)x["Title "]).Contains("Camlex")) |
Eq | == | Camlex.Query().Where(x => (string)x["Title"] == "Camlex") |
Neq | != | Camlex.Query().Where(x => (string)x["Status"] != "Completed") |
Geq | >= | Camlex.Query().Where(x => (int)x["ID"] >= 1) |
Gt | > | Camlex.Query().Where(x => (int)x["ID"] > 1) |
Leq | <= | Camlex.Query().Where(x => (int)x["ID"] <= 1) |
Lt | < | Camlex.Query().Where(x => (int)x["ID"] < 1) |
DateRangesOverlap | Camlex.DateRangesOverlap() | Camlex.Query().Where(x => Camlex.DateRangesOverlap(x["StartField"], x["StopField"], x["RecurrenceID"], (DataTypes.DateTime)Camlex.Month)) |
IsNotNull | != null | Camlex.Query().Where(x => x["Status"] != null) |
IsNull | == null | Camlex.Query().Where(x => x["Title"] == null) |
OrderBy | IQuery.OrderBy() | Camlex.Query().Where(x => (string)x["Status"] == "Completed").OrderBy(x => x["Modified"] as Camlex.Desc) |
GroupBy | IQuery.GroupBy | Camlex.Query().Where(x => (string)x["Status"] == "Completed").GroupBy(x => new[]() { x["Modified"], x["Editor"] }, true, 10) |
IV. Non-constant expressions and runtime evaluation You can specify non-constant expressions both in indexer and in rvalue:
x => (Type)x[expr1] == expr2
expr1 and expr2 are evaluated in runtime. If expr1 or expr2 is method call and it throws exception – then exception will be thrown when query will be evaluated. I.e. Camlex doesn’t catch exceptions when it evaluates expression in runtime. Notice also that expr1 (indexer’s parameter) should have string type. If expr1 cannot be casted to string then Camlex throws InvalidFieldNameForFieldRefException:
public static void Scenario()
{
Func<object> expr1 = () => null;
Func<object> expr2 = () => 1;
var caml =
Camlex.Query()
.Where(x => (string)x[(string)expr1()]((string)expr1()) == expr2()).ToString();
}
Another interesting example – is when expr2 is null and operation is == or !=. In this case Camlex will translate and instead of and respectively:
public static void Scenario()
{
Func<object> expr1 = () => "Title";
Func<object> expr2 = () => null;
var caml =
Camlex.Query()
.Where(x => (string)x[(string)expr1()]((string)expr1()) == expr2()).ToString();
Console.WriteLine(caml);
}
Will be translated into :
<Where>
<IsNull>
<FieldRef Name="Title" />
</IsNull>
</Where>
While not-null rvalue:
public static void Scenario()
{
Func<object> expr1 = () => "Title";
Func<object> expr2 = () => "Camlex";
var caml =
Camlex.Query()
.Where(x => (string)x[(string)expr1()]((string)expr1()) == expr2()).ToString();
Console.WriteLine(caml);
}
Will be translated into :
<Where>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Camlex</Value>
</Eq>
</Where>
V. Dynamic filtering conditions This feature is added in 2.0 version. Often developers need to build CAML query based on predefined set of values. E.g. search items which have Ids contained in array {1, 2, 3} (analog of IN operator in SQL), or retrieve all items which contains one of the substrings (or all of them) in their Title { “hello”, “world” }. For this use case in version 2.0 were added 2 additional methods in IQuery interface WhereAll and WhereAny (by analogy with All and Any methods in Linq). The main difference is that WhereAll and WhereAny methods receive list of lambda expressions (or more accurate – IEnumerable) in contrast to Where method which receives single lambda expression.
By analogy with Linq WhereAll method constructs CAML query for retrieving those items which satisfy all conditions specified in argument. I.e. WhereAll method contructs CAML query using logical join. WhereAny method returns CAML query which can be used to retrieve items which satisfy at least one of specified conditions – it uses logical join. The general idea is to create list of lambda expressions and pass this list into Camlex. It will composite expressions using And or Or joins – so it will have one big expression as result and pass it in old Where method from 1.0 version.
Notice that you can create various expressions – i.e. not necessary to create the same expression for each value. You can even provide different argument names in expressions – Camlex hasn't restrictions to have the same argument names for all provided expressions. For example we need to select item which have ID = 1 and which Title = “Hello world”:
<Where>
<And>
<Eq>
<FieldRef Name="ID" />
<Value Type="Integer">1</Value>
</Eq>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Hello world</Value>
</Eq>
</And>
</Where>
It can be done using the following code:
var expressions = new List<Expression<Func<SPListItem, bool>>>();
expressions.Add(x => (int)x["ID"] == 1);
expressions.Add(y => (string)y["Title"] == "Hello world");
string caml = Camlex.Query().WhereAll(expressions).ToString();
I.e. Camlex may successfully translate queries with different arguments names (“x” and “y” in example above).
One of the best MUST HAVE library for every SharePoint developer ever. Guys, keep it up! by avishnyakov on Aug 20, 2013 at 5:36 AM
This package is absolutely a helpful solution of buildup CAML markup, i was once create my own package for CAML markup, but then abandoned when I found this project since v1.x, this project is just become much better. You will never know how troublesome when there is no results returned and you may have to bothered with the CAML markup syntax. It is truly a life saver and rescue a lot of developer day and nights. A must have library for SharePoint lover / developer. by z9ekaMen1 on Oct 11, 2016 at 5:45 AM
very useful tool! by patriksergil on Mar 12, 2015 at 12:17 PM