SharePoint 2010: Limitaciones en campos proyectados al hacer Joins!

Como sabéis, una de las novedades que incorpora SharePoint 2010 es la posibilidad de hacer Joins en las consultas que hagamos a listas que están relacionadas mediante campos de lookup con otras listas. Este soporte es posible gracias a la actualización del esquema CAML subyacente, de forma que podemos incorporar Joins a nuestras consultas ya sea en CAML o utilizando LINQ To SharePoint (el proveedor se encarga de generar el código correspondinte). Así por ejemplo:

  • En el caso de usar un objeto SPQuery, podemos definir un Join en CAML de la siguiente forma (en este caso tenemos una lista Projects que está relacionada mediante un campo de lookup con la lista Employees…el ejemplo está tomado del Training Kit de desarrollo de SharePoint 2010):
   1: using (SPSite siteCollection = new SPSite("http://demo2010a:100"))

   2: {

   3:     SPList list = siteCollection.RootWeb.Lists["Projects"];

   4:  

   5:     SPQuery query = new SPQuery();

   6:     query.Joins =

   7:         @"

   8:         <Join Type='LEFT' ListAlias='Primary_x0020_Contact'><!--List Name: Employees-->

   9:             <Eq>

  10:                 <FieldRef Name='Primary_x0020_Contact' RefType='ID' />

  11:                 <FieldRef List='Primary_x0020_Contact' Name='ID' />

  12:             </Eq>

  13:         </Join>

  14:         ";

  15:     query.ProjectedFields =

  16:         @"

  17:         <Field Name='Primary_x0020_ContactTitle' Type='Lookup' List='Primary_x0020_Contact' ShowField='Title' />

  18:         <Field Name='Primary_x0020_ContactJobTitle' Type='Lookup' List='Primary_x0020_Contact' ShowField='JobTitle' />

  19:         ";

  20:     query.ViewFields =

  21:         @"

  22:         <FieldRef Name='Title' />

  23:         <FieldRef Name='Primary_x0020_ContactTitle' />

  24:         <FieldRef Name='Primary_x0020_ContactJobTitle' />

  25:         ";

  26:     query.Query =

  27:         @"

  28:         <Where>

  29:             <Eq>

  30:                 <FieldRef Name='Primary_x0020_ContactTitle' /><Value Type='Lookup'>Contact1</Value>

  31:             </Eq>

  32:         </Where>

  33:         ";

  34:     Console.WriteLine("CAML Query sent");

  35:     Console.WriteLine(query.ViewXml.ToString());

  36:     Console.ReadLine();

  37:     Console.WriteLine("Query results");

  38:     SPListItemCollection results = list.GetItems(query);

  39:     foreach (SPListItem item in results)

  40:     {

  41:         Console.WriteLine("{0} - {1} - {2}",

  42:             item["Title"].ToString(),

  43:             item["Primary_x0020_ContactTitle"].ToString(),

  44:             item["Primary_x0020_ContactJobTitle"].ToString());

  45:     }

  46: }

  • Como veis, el objeto SPQuery incorpora dos nuevas propiedades:
    • Joins, que permite definir el join o joins en la consulta.
    • ProjectedFields, que permite incorporar en los resultados devueltos campos proyectados, es decir, campos que están en la lista relacionada con la lista principal. En este caso estamos devolviendo dos campos de la lista Employees.
  • En el caso de usar LINQ To SharePoint, el uso de Joins es mucho más intuitivo. Por ejemplo:
   1: try

   2: {

   3:     SPLINQProxySiteDataContext ctx =

   4:         new SPLINQProxySiteDataContext(SPContext.Current.Web.Url);

   5:     var productsList = from p in ctx.Productos

   6:                        select new

   7:                        {

   8:                            Producto = p.Título,

   9:                            Descripcion = p.Descripción,

  10:                            Fabricante = p.Fabricante.Título

  11:                        };

  12:     ctx.Log = swWriter;

  13:     grdProductos.DataSource = productsList;

  14:     grdProductos.DataBind();

  15:     txtConsultaCAML.Text = "Consulta: " +

  16:         swWriter.ToString();

  17: }

  18: catch (Exception ex)

  19: {

  20:     txtError.Text = ex.Message;

  21: }

  • En  este caso, tras definir una instancia del objeto DataContext correspondiente, estamos haciendo una consulta a la lista Productos que está relacionada con la lista Empresas mediante el campo de lookup Fabricante. En el select realizado estamos consultando dos campos de la lista Productos y uno de la lista Empresas por lo que el proveedor de LINQ To SharePoint generará por nosotros el correspondiente Join (lo podemos ver con ctx.log=swWriter):
   1: <View>

   2:     <Query>

   3:         <Where>

   4:             <BeginsWith>

   5:                 <FieldRef Name="ContentTypeId" />

   6:                     <Value Type="ContentTypeId">0x0100</Value>

   7:             </BeginsWith>

   8:         </Where>

   9:     </Query>

  10:     <ViewFields>

  11:         <FieldRef Name="Title" />

  12:         <FieldRef Name="Descripci_x00f3_n" />

  13:         <FieldRef Name="FabricanteTitle" />

  14:     </ViewFields>

  15:     <ProjectedFields>

  16:         <Field Name="FabricanteTitle" Type="Lookup" List="Fabricante" ShowField="Title" />

  17:         </ProjectedFields>

  18:     <Joins>

  19:         <Join Type="LEFT" ListAlias="Fabricante">

  20:         <!--List Name: Empresas-->

  21:             <Eq>

  22:                 <FieldRef Name="Fabricante" RefType="ID" />

  23:                 <FieldRef List="Fabricante" Name="ID" />

  24:             </Eq>

  25:         </Join>

  26:     </Joins>

  27:     <RowLimit Paged="TRUE">2147483647</RowLimit>

  28: </View>

  • Finalmente, y después de tanto “rollo”, la respuesta a las preguntas que dan título al post:
    • ¿Cuántos Joins podemos tener? La respuesta es que el umbral tope definido (por aplicación web) por defecto es de 8 como podéis consultar en este enlace:http://technet.microsoft.com/en-us/library/cc262787.aspx. Este es el umbral que se fija en la administración central y que se puede sobreescribir ya que no es un valor límite, aunque si el máximo recomendado sin penalizar el rendimiento. Si se intenta realizar una consulta que implique superar este número de Joins por defecto, la consulta se bloqueará y se generará la correspondiente excepción. En este thread de los foros de MSDN podéis encontrar un caso en el que se pudo configurar el uso de 13 Joins antes de obtener la excepción: http://social.msdn.microsoft.com/Forums/en-US/sharepoint2010general/thread/f48f36f8-108f-4ac8-a5d6-c347b7adb5ce. En lo que a realizar consultas simples, sin Joins, SharePoint devolverá en este caso hasta un máximo de 8 campos de lookup.
    • ¿Se pueden hacer Joins con cualquier tipo de campo? La respuesta es que no. Por ejemplo, no se permite hacer un Join con campos que no tienen sentido como los de texto enriquecido.