Procedimiento para consultar mediante LINQ to SharePoint
Última modificación: viernes, 08 de octubre de 2010
Hace referencia a: SharePoint Foundation 2010
En este artículo
Paso 1: Obtención de una referencia a un sitio web
Paso 2: Obtención de una referencia a una lista
Paso 3 (opcional): Desactivación del seguimiento de cambios en el objeto
Paso 4: Definición de la consulta LINQ
Paso 5: Enumeración de los resultados de consulta
Paso 6 (opcional): Combinación de resultados de varias listas y varios orígenes de datos
Paso 7 (opcional): Unión de resultados de varios orígenes de datos
En este tema se explica cómo solicitar listas de Microsoft SharePoint Foundation mediante el proveedor LINQ to SharePoint.
Paso 1: Obtención de una referencia a un sitio web
Toda codificación en el proveedor LINQ to SharePoint debe comenzar por la creación de un objeto DataContext. Este objeto representa un subconjunto de una base de datos de contenido, específicamente, las listas y los elementos de lista de un sitio web de SharePoint Foundation. La manera más simple de crear este objeto es pasar la dirección URL absoluta del sitio web que desea consultar, como una cadena literal, al constructor de clases, como se muestra a continuación.
DataContext teamSite = new DataContext("http://MarketingServer/SalesTeam");
Sin embargo, lo más habitual es que la solución deba ejecutarse en varios sitios de distintos conjuntos o granjas de servidores y no conozca la dirección URL completa cuando escriba el código. Si el código se ejecuta en cualquier situación en la que existe un contexto HTTP, como en un elemento web o en una página de aplicación personalizada, y está realizando consultas en el sitio web actual, puede usar el objeto SPContext para obtener la dirección URL, como se muestra en este ejemplo.
DataContext teamSite = new DataContext(SPContext.Current.Web.Url);
También puede usar el contexto para obtener, de manera indirecta, las direcciones URL de otros sitios web dentro de la colección de sitios, o incluso de las otras colecciones de sitios de la aplicación web. El siguiente código, por ejemplo, crea un DataContext para el sitio web de nivel superior de la colección de sitios más antigua de la aplicación web.
String rootOfOldestURL = this.Site.WebApplication.Sites[0].RootWeb.Url;
using (DataContext topSiteOfOldestSiteCollection = new DataContext(rootOfOldestURL))
{
}
Tenga en cuenta que debe eliminar el objeto DataContext porque está usando un objeto SPWeb distinto al que proporcionó SPContext.
Nota
Las referencias a un objeto SPWebApplication requieren una instrucción using (Imports en Visual Basic) para el espacio de nombres Microsoft.SharePoint.Administration.
Para conocer otras maneras de obtener referencias a sitios web dentro de una granja de servidores de SharePoint Foundation, vea Obtención de referencias a sitios, aplicaciones web y otros objetos clave.
Si no existe un contexto HTTP, como en una aplicación de consola, y no conoce el nombre del servidor cuando está codificando, puede usar el alias "localhost" para obtener una referencia al sitio web raíz, como se muestra en los siguientes ejemplos.
using (DataContext topLevelSite = new DataContext("https://localhost"))
{
}
using (DataContext mySite = new DataContext("https://localhost/sites/MySite"))
{
}
Dado que no existe un contexto HTTP, debe eliminar el objeto DataContext.
Puede derivar una clase de DataContext, en cuyo caso usaría el constructor de clases, como se muestra en el siguiente ejemplo.
ContosoTeamData teamSite = new ContosoTeamData(SPContext.Current.Web.Url);
Paso 2: Obtención de una referencia a una lista
Use el método GetList<T>(String) para obtener un objeto EntityList<TEntity>. Este objeto es una representación de IQueryable<T> de una lista. A continuación se muestra un ejemplo.
EntityList<Announcement> announcements = teamSite.GetList<Announcement>("Announcements")
Tenga en cuenta que el tipo de contenido de la lista lo debe representar una clase declarada explícitamente, en este caso "Announcement". La clase se debe decorar con un ContentTypeAttribute que especifique el nombre del tipo de contenido en el sitio web de SharePoint Foundation y el identificador del tipo de contenido. Generalmente, deberá saber qué listas va a consultar el código cuando lo esté escribiendo. Como mínimo, la clase que representa el tipo de contenido debe incluir una propiedad que represente una columna de la lista, y esa declaración de propiedad debe contener un ColumnAttribute que especifique al menos el nombre del campo y su tipo. En el siguiente ejemplo se muestran las declaraciones mínimas que se requieren para habilitar el código de llamada para que llame al método GetList<T>(String) (donde T es Announcement).
[ContentType(Name="Announcement", Id="0x0104")]
public partial class Announcement
{
[Column(Name = "Title", FieldType = "Text")]
public String Title { get; set; }
}
Pero una consulta solo puede hacer referencia a columnas representadas por propiedades en la clase de tipo de contenido, de modo que si solo se proporciona esta declaración mínima, el código de llamada solo podría hacer referencia al campo (columna) Título, como se muestra en este ejemplo.
var excitingAnnouncements = from announcement in announcements
where announcement.Title.EndsWith("!")
select announcement;
Nota
Como se muestra en este ejemplo, el hecho de que la clase se llame Announcement no significa que deba reflejar exactamente el tipo de contenido de SharePoint Foundation con el mismo nombre. Esto significa que no se requiere que tenga una propiedad por cada columna del tipo de contenido. La clase puede representar solo un subconjunto de columnas del tipo de contenido, y también puede tener miembros adicionales. Esta es, de hecho, una característica crucial del proveedor LINQ to SharePoint, porque los propietarios de sitios pueden agregar columnas a listas y, al hacerlo, se crea un nuevo tipo de contenido para la lista. Tampoco se requiere que la clase tenga el mismo nombre que el tipo de contenido de la lista. Puede tener cualquier nombre, siempre y cuando ese mismo nombre se use como el tipo de parámetro en la llamada al método GetList<T>(String). Sin embargo, el código es generalmente más legible si la clase tiene el mismo nombre que el tipo de contenido oficial de la lista. De manera predeterminada, la herramienta SPMetal realiza este procedimiento.
Los tipos de contenido pueden heredar de otros tipos de contenido y, para la llamada al método GetList<T>(String), puede usar como tipo de parámetro cualquier tipo de contenido que sea superior en el árbol de herencia. Por ejemplo, debido a que todos los tipos de contenido derivan del tipo de contenido básico Item, puede usar Item como tipo de parámetro, como se muestra en este ejemplo:
EntityList<Item> announcements = teamSite.GetList<Item>("Announcements")
El código fuente debe proporcionar una declaración de una clase Item y dicha clase debe declarar propiedades para cada una de las columnas del tipo de contenido al que hacen referencia las consultas.
Si usa el tipo de contenido Item, puede consultar listas sin conocer el nombre de la lista o su tipo de contenido derivado cuando codifica, como se muestra en este ejemplo.
DataContext topLevelSite = new DataContext("https://localhost");
SPSite siteCollection = new SPSite("https://localhost");
EntityList<Item> someList = topLevelSite.GetList<Item>(siteCollection.RootWeb.Lists[0].Title);
var first3Items = from item in someList
where item.Id <= 3
select item;
foreach (var item in first3Items)
{
Console.Writeline("{0} is one of the first 3 items in {1}", item.Title, someList.Title);
}
Sin embargo, en los escenarios más realistas de codificación útil de LINQ to Sharepoint se incluyen las columnas que son únicas en listas particulares, de modo que para que sea más práctico, debe conocer los nombres de las listas y sus tipos de contenido específicos derivados. Concretamente, cuando codifica debe suponer que ciertas listas están presentes en los sitios web en los que se va a ejecutar el código de consulta. Además, debido a que las consultas de una lista específica van a hacer referencia a columnas en particular, debe suponer que, para una lista específica, va a estar presente un cierto subconjunto de columnas en la lista. Esto implica que la solución de SharePoint Foundation sea de uno de estos dos tipos:
Se va a diseñar para consultar tipos de listas conocidas que se incluyen con SharePoint Foundation o con productos de funcionalidad mejorada, como Microsoft SharePoint Server.
Se va a desarrollar e instalar junto con un conjunto de una o más listas personalizadas como parte de un conjunto de características o una definición de sitio personalizada.
Se recomienda que use la herramienta SPMetal para generar las declaraciones de clase y propiedad necesarias.
Paso 3 (opcional): Desactivación del seguimiento de cambios en el objeto
Si el código sólo consulta listas, en lugar de agregar, eliminar o editar elementos de lista, puede desactivar el seguimiento de los cambios en el objeto. Al hacerlo, mejorará el rendimiento. Establezca la propiedad ObjectTrackingEnabled como false.
teamSite.ObjectTrackingEnabled = false;
Paso 4: Definición de la consulta LINQ
El valor de LINQ es que las consultas se escriben básicamente de la misma manera, independientemente del origen de datos o del proveedor LINQ. A excepción de pequeñas diferencias en cuanto a la obtención de una referencia al contexto de datos y al objeto IQueryable<T>, las consultas LINQ to SharePoint son las mismas que las consultas que usaría para LINQ to SQL o LINQ to XML. Para obtener más información, vea los temas sobre LINQ to SQL: Language Integrated Query de .NET para datos relacionales y LINQ to XML.
Sin embargo, no existen dos proveedores LINQ exactamente iguales. Las diferencias en los idiomas de consulta nativos de los orígenes de datos (a los que el proveedor traduce las consultas LINQ) a veces fuerzan algunas limitaciones en las posibilidades de consulta. Particularmente, existe un límite de uniones de listas, ya sean implícitas o explícitas, en consultas que usan el proveedor LINQ to SharePoint. Una consulta LINQ to SharePoint puede unir dos listas, ya sea de manera explícita o implícita, pero solo si una tiene una columna de tipo Búsqueda que busca una columna de la otra tabla. Si el campo de búsqueda solo permite un valor, esta relación entre las listas se debe representar en el código con un campo EntityRef<TEntity> en la clase que representa el tipo de contenido de la lista. Si el campo permite varios valores, la relación se debe representar con un campo EntitySet<TEntity> y una propiedad EntitySet<TEntity> que lo ajuste.
Sugerencia |
---|
La propiedad Log es un TextWriter que puede escribir la consulta CAML a la que se traduce la consulta LINQ. Tener la capacidad para ver la consulta CAML puede resultar útil en el momento de la depuración. Para hacerlo, asigne un objeto TextWriter a la propiedad Log. En el siguiente ejemplo, se asigna TextWriterOut a Log. Esto ocasiona que la consulta CAML aparezca en la consola cuando se ejecuta una consulta LINQ en una aplicación de consola, como se muestra a continuación. |
#if DEBUG
teamSite.Log = Console.Out;
#endif
Paso 5: Enumeración de los resultados de consulta
Al igual que con todos los proveedores LINQ, una consulta LINQ to SharePoint no se excluye hasta que se enumera. Normalmente esto ocurre en un bucle foreach. En un caso inusual, en el que se requiera una enumeración personalizada, como omitir cualquier otro elemento del resultado, puede usar los métodos IEnumerable y IEnumerator.
También puede asignar los resultados de una consulta a una variable IEnumerable<T>, como un objeto IList<T> o ICollection<T>, en lugar del tipo anónimo var (Dim en Visual Basic). Esto ocasiona que la consulta se ejecute inmediatamente, de modo que la variable se pueda rellenar. A continuación se muestra un ejemplo:
EntityList<Announcement> announcements = teamSite.GetList<Announcement>("Announcements")
IList<Announcement> excitingAnnouncements = from announcement in announcements
where announcement.Title.EndsWith("!")
select announcement;
Paso 6 (opcional): Combinación de resultados de varias listas y varios orígenes de datos
Puede combinar resultados de más de una lista en una única IList<T>, que luego se puede filtrar mediante LINQ to Objects. En el siguiente ejemplo se muestra cómo producir una IList<T> tanto de eventos corporativos como de equipo y cómo generar un informe de los eventos del día actual que tienen lugar en el auditorio.
DataContext corpSiteData = new DataContext("https://localhost/CorpSite");
DataContext markTeamData = new DataContext("https://localhost/Marketing");
EntityList<Event> allCorpEvents = corpSiteData.GetList<Event>("Calendar");
EntityList<Event> allMarkTeamEvents = markTeamData.GetList<Event>("Calendar");
List<Event> todaysCorpEvents = (from ev in allCorpEvents
where ev.StartDate = DateTime.Now
select ev).ToList();
List<Event> todaysTeamEvents = (from ev in allMarkTeamEvents
where ev.StartDate = DateTime.Now
select ev).ToList();
IEnumerable<Event> mergedEvents = todaysCorpEvents.Union(todaysTeamEvents);
var todaysAuditoriumEventTitles = from ev in mergedEvents
where ev.Location.Contains("Auditorium")
select new { ev.Title };
foreach (var eventTitle in todaysAuditoriumEventTitles)
{
Console.WriteLine(eventTitle.Title);
}
Los dos objetos IList<T> deben tener el mismo tipo de parámetro.
Paso 7 (opcional): Unión de resultados de varios orígenes de datos
La técnica que se usó en el paso 7 para combinar datos de varias listas de SharePoint Foundation se puede usar para combinar datos de listas de SharePoint Foundation con datos de otros orígenes, siempre que los registros del otro origen se puedan fundir en la clase que representa el tipo de contenido de SharePoint Foundation. Por ejemplo, suponga que el código usa LINQ to SQL para producir un objeto IList<T> (donde T es Client) llamado oldClients e imagine que usa LINQ to SharePoint para producir un objeto IList<T> (donde T es Patient) llamado activePatients. Si las propiedades y otros miembros de la clase Client son un subconjunto de los miembros de la clase Patient y los miembros correspondientes tienen las mismas firmas, entonces podría combinar los datos del origen SQL con los datos de la lista de SharePoint Foundation, como se muestra en este ejemplo.
foreach (Patient patient in activePatients)
{
oldClients.Add((Client)patient);
}
Una opción aún mejor para combinar datos de dos proveedores LINQ es usar la misma clase de tipo de elemento para ambos. Esto es posible porque puede poner las decoraciones de atributo que se necesiten para LINQ to SharePoint y las que se necesiten para otro proveedor LINQ en la misma declaración de clase. En el siguiente ejemplo se muestra la firma de una declaración de clase decorada con el ContentTypeAttribute de LINQ to SharePoint y el TableAttribute de LINQ to SQL.
[ContentType(Name="Item", Id="0x01" List="Customers")]
[Table(Name = "Customers")]
public partial class Customer : Item
{
}
Con estos atributos, no es necesario convertir los tipos para combinar datos que provengan de la tabla de SQL con datos de la lista de SharePoint Foundation.
Vea también
Referencia
IEnumerable
Conceptos
Identificadores de tipo de contenido
Consultas de LINQ no admitidas y consultas de dos fases
Obtención de referencias a sitios, aplicaciones web y otros objetos clave
Procedimiento para escribir en bases de datos de contenido mediante LINQ to SharePoint
Otros recursos
LINQ to SQL: Language-Integrated Query de .NET para datos relacionales