Saturday, February 4, 2012

Camlex.Net 3.1: support of dynamic ViewFields

On this week new version 3.1 of Camlex.Net was released. In this release we added support of the dynamic ViewFields. This feature was requested in one of the discussions on the codeplex Camlex site. I.e. the following new methods were added to the IQueryEx interface:

   1: public interface IQueryEx : IQuery
   2: {
   3:     ...
   4:     string ViewFields(IEnumerable<string> titles);
   5:     string ViewFields(IEnumerable<string> titles, bool includeViewFieldsTag);
   6:     string ViewFields(IEnumerable<Guid> ids);
   7:     string ViewFields(IEnumerable<Guid> ids, bool includeViewFieldsTag);
   8: }

I.e. now you can create list of field names or field ids dynamically and pass it into the Camlex. It will create CAML for ViewFields based on this list. For example the following code:

   1: var items = new [] { "Title", "FileRef" };
   2:  
   3: string caml = Camlex.Query().ViewFields(items);

will produce the following CAML:

   1: <FieldRef Name=\"Title\" />
   2: <FieldRef Name=\"FileRef\" />

I would like to share also some interesting (from my point of view :) ) implementation details. You can easily skip the rest of the post if you are not interested in it. Before version 3.1 there were the following methods (they still exist of course):

   1: public interface IQueryEx : IQuery
   2: {
   3:     string ViewFields(Expression<Func<SPListItem, object>> expr);
   4:     string ViewFields(Expression<Func<SPListItem, object>> expr, bool includeViewFieldsTag);
   5:     string ViewFields(Expression<Func<SPListItem, object[]>> expr);
   6:     string ViewFields(Expression<Func<SPListItem, object[]>> expr, bool includeViewFieldsTag);
   7: }

With these methods we were able to create ViewFields like this:

   1: string caml =
   2:     Camlex.Query().ViewFields(x => new [] { x["Title"], x["FileRef"] });

When add new functionality I wanted to reuse these existing methods in order to avoid creating of extra code. In order to do this the following question should be solved: having array of strings or guids (e.g. { “1”, “2”, “3” }) how to create lambda expression “x => new [] { x[“1”], x[“2”], x[“3”] }”? The solution is quite elegant:

   1: public string ViewFields(IEnumerable<string> titles, bool includeViewFieldsTag)
   2: {
   3:     ...
   4:     return this.ViewFields(this.createExpressionFromArray(titles), includeViewFieldsTag);
   5: }
   6:  
   7: public string ViewFields(IEnumerable<Guid> ids, bool includeViewFieldsTag)
   8: {
   9:     ...
  10:     return this.ViewFields(this.createExpressionFromArray(ids), includeViewFieldsTag);
  11: }
  12:  
  13: private Expression<Func<SPListItem, object[]>>
  14: createExpressionFromArray<T>(IEnumerable<T> items)
  15: {
  16:     return Expression.Lambda<Func<SPListItem, object[]>>(
  17:         Expression.NewArrayInit(typeof(object),
  18:         (IEnumerable<Expression>)items.Select(
  19:             t => Expression.Call(Expression.Parameter(typeof(SPListItem), "x"),
  20:                 typeof(SPListItem).GetMethod("get_Item", new[] { typeof(T) }),
  21:                 new[] { Expression.Constant(t) })).ToArray()),
  22:         Expression.Parameter(typeof(SPListItem), "x"));
  23: }

I.e. we iterate through all items in the list and for each item create expression – access to indexer x[item], and add them to the NewArrayInit expression. Note on the ToArray() call – without it lazy LINQ expression won’t be populated and array initialization will fail.

We are very interested in your feedback. Feel free to contact us via Camlex site on the codeplex.

No comments:

Post a Comment