Procedimiento para escribir en bases de datos de contenido mediante LINQ to SharePoint

Última modificación: viernes, 24 de junio de 2011

Hace referencia a: SharePoint Foundation 2010

En este artículo
Pasos 1 y 2: Obtención de referencias a sitios web y listas
Paso 3: Comprobación de que el seguimiento de cambios en el objeto está habilitado
Paso 4: Adición de código básico para agregar, eliminar, reciclar o actualizar un elemento de lista
Paso 5: Adición de infraestructura para administrar conflictos de concurrencia

En este tema se explica cómo codificar en el proveedor LINQ to SharePoint para agregar o eliminar elementos de lista de listas de Microsoft SharePoint Foundation y cómo cambiar los valores de campos específicos en elementos de lista.

Pasos 1 y 2: Obtención de referencias a sitios web y listas

Para ver una explicación de cómo obtener una referencia en el código al sitio web y a la lista cuyos datos desea cambiar, vea los pasos 1 y 2 del tema Procedimiento para consultar mediante LINQ to SharePoint.

Paso 3: Comprobación de que el seguimiento de cambios en el objeto está habilitado

La propiedad ObjectTrackingEnableddebe estar en el valor predeterminado true antes de que pueda usar LINQ to SharePoint para realizar cambios en la base de datos. Si el código establece esta propiedad en false y ha consultado el objeto DataContext con este valor marcado, entonces no se puede restablecer la propiedad como true. Por consiguiente, si el código consulta el objeto DataContext y, a continuación, realiza cambios en la base de datos de contenido, tiene dos opciones:

  • Evite que el código establezca la propiedad ObjectTrackingEnabled como false.

  • Cree un nuevo objeto DataContext para el mismo sitio web en el código, y colóquelo después de las consultas, pero antes de que el código escriba en la base de datos. Deje la propiedad ObjectTrackingEnabled del objeto nuevo DataContext en su valor predeterminado true y use el nuevo objeto para escribir en la base de datos de contenido.

Paso 4: Adición de código básico para agregar, eliminar, reciclar o actualizar un elemento de lista

El código básico para escribir en la base de datos de contenido es simple. Debe comenzar por crear una referencia al sitio web y la lista, y terminar con una llamada a SubmitChanges(). Entre ambos se encuentra el código en el que realiza los cambios que desea mediante uno de los métodos *OnSubmit de la clase EntitySet<TEntity> o escribe en campos de lista mediante sintaxis de establecimiento de propiedades común. En todos los ejemplos siguientes, teamSite es un objeto DataContext que representa un sitio web, y TeamMembers es un objeto EntitySet<TEntity> que representa una lista Integrantes del grupo.

Nota importanteImportante

El objeto que representa el elemento de lista que va a agregar, eliminar, reciclar o actualizar debe tener las propiedades EntityState, Id y Version. Si la clase que representa el tipo de contenido se generó mediante SPMetal, el objeto tendrá esas propiedades. Los diferentes métodos *OnSubmit asignan valores a la propiedad EntityState. Las propiedades Id y Version las establece el tiempo de ejecución de SharePoint Foundation. El código no debería escribir en ninguna de estas tres propiedades.

Adición de un elemento de lista

Para agregar un elemento a una lista, cree un objeto del tipo de contenido de la lista y, a continuación, páselo al método InsertOnSubmit(TEntity).

Nota importanteImportante

Todas las propiedades del objeto de elemento de lista que representen campos obligatorios del tipo de contenido deben tener un valor antes de insertar el elemento en la lista. (Estas mismas propiedades se declaran con una decoración [ColumnAttribute] cuya propiedad Required está presente y se establece como true.) Por ejemplo, todos los tipos de contenido heredan del tipo de contenido básico Element de SharePoint Foundation, que tiene un campo obligatorio Título. Puede asignar valores a dichas propiedades entre la llamada de InsertOnSubmit(TEntity) y la de SubmitChanges(), pero es recomendable que inicialice las propiedades obligatorias lo antes posible. Si no existe un constructor de clases que inicialice todas las propiedades obligatorias, puede usar un inicializador de objeto, como se muestra en el ejemplo más adelante.

En el siguiente ejemplo se muestra cómo agregar un elemento a una lista y, a continuación, guardar los cambios en la base de datos.

// Create the new list item.
TeamMember bob = new TeamMember() { Title="Bob Smith" };

// Set the item to be inserted.
teamSite.TeamMembers.InsertOnSubmit(bob);

// Write changes to the content database.
teamSite.SubmitChanges();

También puede usar un método InsertAllOnSubmit(IEnumerable<TEntity>) para insertar varios elementos.

Eliminación y reciclaje de un elemento de lista

En el siguiente ejemplo se muestra cómo usar el método DeleteOnSubmit(TEntity) para eliminar un elemento de una lista.

// Set the item to be deleted.
foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    if (teamMember.Title = "Bob Smith")
    {
        teamSite.TeamMembers.DeleteOnSubmit(teamMember);
    }
}

// Write changes to the content database.
teamSite.SubmitChanges();

Si desea colocar un elemento en la papelera de reciclaje del usuario, en lugar de eliminarlo totalmente, use el método RecycleOnSubmit(TEntity). También puede usar los métodos DeleteAllOnSubmit(IEnumerable<TEntity>) y RecycleAllOnSubmit(IEnumerable<TEntity>) para eliminar varios elementos al mismo tiempo.

Cambio de un campo en un elemento de lista

Para cambiar un valor de un campo en un elemento de lista, simplemente escriba en la propiedad que representa el campo, como se muestra en este ejemplo:

// Set the property to a new value.
foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

// Write changes to the content database.
teamSite.SubmitChanges();

Varios cambios con una sola llamada de SubmitChanges

No existe un límite en el número de cambios que el código puede realizar con una llamada del método SubmitChanges(). Para un mejor rendimiento, el código debe hacer la menor cantidad posible de llamadas. El siguiente código muestra un ejemplo de varios tipos de cambios escritos en la base de contenidos con una sola llamada de SubmitChanges():

// ‘sally’ is a TeamMember object.
teamSite.TeamMembers.RecycleOnSubmit(sally);

// ‘leftCompany’ is an IList of TeamMember objects
teamSite.TeamMembers.DeleteAllOnSubmit(leftCompany);

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

// Write changes to the content database.
teamSite.SubmitChanges();

No es necesario que todos los cambios que envíe sean para la misma lista. Los cambios a una o todas las listas de una página web se pueden enviar con una sola llamada de SubmitChanges().

Paso 5: Adición de infraestructura para administrar conflictos de concurrencia

Si cambió algún valor de campo, antes de realizar cambios en la base de datos de contenido mediante la llamada del método SubmitChanges(), el sistema de seguimiento de cambios en el objeto comprueba si otro usuario cambió alguno de los elementos de lista afectados después de que el proceso del usuario actual los recuperara de la base de datos. (Para obtener más información sobre este sistema, vea Seguimiento de cambios en objetos y concurrencia optimista.) Si hay un conflicto de concurrencia, SubmitChanges() genera una ChangeConflictException. Además, se genera un MemberChangeConflict que representa información acerca de la discrepancia. Si surgen otras discrepancias entre el valor de cliente actual para un campo y el valor del campo en la base de datos, cada uno de ellos se representa también con un objeto MemberChangeConflict. Todas las discrepancias para un elemento de lista específico se agregan a la propiedad MemberConflicts de un objeto ObjectChangeConflict. En función de la sobrecarga de SubmitChanges() a la que se haya llamado y los parámetros que se le hayan pasado, podría haber más de un objeto ObjectChangeConflict. Todos se agregan a la propiedad ChangeConflicts del objeto DataContext.

El código debe capturar la excepción y resolver todas las discrepancias antes de llamar otra vez a SubmitChanges(). En algunos casos, la mejor manera de manejar los problemas es preguntar al usuario para que decida cómo resolver cada discrepancia que surja. Puede rellenar la UI que se muestra al usuario con los datos de los objetos ObjectChangeConflict de la propiedad ChangeConflicts, y de sus objetos secundarios MemberChangeConflict; en particular las propiedades OriginalValue, DatabaseValue y CurrentValue.

SugerenciaSugerencia

Considere la posibilidad de distinguir claramente en la UI las discrepancias que representan conflictos de concurrencia reales; por un lado las discrepancias entre las propiedades OriginalValue y DatabaseValue, y por el otro, las discrepancias que simplemente representan cambios que se hubieran escrito en la base de datos de contenido automáticamente si no hubiera habido conflictos de concurrencia, es decir, discrepancias entre DatabaseValue y CurrentValue donde OriginalValue = DatabaseValue.

Implemente las elecciones del usuario con llamadas a una combinación de los siguientes métodos:

En otros casos, es posible que cuando esté codificando sepa cuál es la mejor manera para resolver discrepancias, según el propósito de la aplicación y el tipo de cambios que realiza en las bases de datos de contenido. En estas situaciones, podría ser mejor que llame a una combinación de los métodos de la lista anterior sin preguntar al usuario. Debe tener en cuenta cuatro puntos principales al construir la lógica de resolución:

  • La versión del valor de un campo que se debe mantener en la base de datos de contenido, el valor original, el valor actual en la base de datos, el valor en el proceso de la aplicación (el valor de cliente) o un cuarto valor. Para obtener una orientación, vea RefreshMode y los temas de referencia para los métodos de la lista anterior. Además, aunque los artículos de la siguiente tabla son sobre el proveedor LINQ to SQL, la lógica que describen se aplica también a LINQ to SharePoint.

    Procedimiento para resolver conflictos de concurrencia mediante la combinación con valores de base de datos (LINQ to SQL)

    Procedimiento para resolver conflictos de concurrencia mediante la retención de valores de base de datos (LINQ to SQL)

    Procedimiento para resolver conflictos de concurrencia mediante la invalidación de valores de base de datos (LINQ to SQL)

  • ¿Qué desea hacer cuando el usuario actual ha enviado un cambio a un elemento de lista y otro usuario lo eliminó por completo de la lista? Para obtener más información sobre las opciones que tiene, vea Resolve(RefreshMode, Boolean) y ResolveAll(RefreshMode, Boolean).

  • ¿A qué nivel necesita aplicar las decisiones sobre los dos escenarios anteriores? Imagine que, por ejemplo, desea aplicar las siguientes reglas a todas las discrepancias de todos los elementos de lista de todas las listas.

    • Ignore los elementos de lista que otro usuario eliminó.

    • Conserve todos los cambios realizados por el proceso de la aplicación y también los de otros procesos de usuario, teniendo preferencia por los suyos si entran en conflicto con los de otro usuario.

    Puede aplicar esta lógica con una simple llamada a ChangeConflictCollection.ResolveAll().

    Pero si necesita conservar todos los cambios en los elementos de algunas listas, y cancelar los cambios hechos por su propio proceso en elementos de ciertas listas, el código debe iterar los miembros de la propiedad ChangeConflicts, llamando a diferentes sobrecargas de ObjectChangeConflict.Resolve() y pasando distintos parámetros para distintos elementos ObjectChangeConflict.

    Para algunos tipos de contenido, es posible que deba aplicar diferentes reglas para diferentes campos. En esa situación, el código debe iterar los miembros de la propiedad MemberConflicts de algunos objetos ObjectChangeConflict y llamar a diferentes sobrecargas de MemberChangeConflict.Resolve(), y pasar diferentes parámetros para diferentes campos.

  • ¿Cuándo desea emitir una excepción ChangeConflictException y no realizar más cambios? Esto es parte de la lógica de resolución, aunque debe implementar su decisión con la llamada a SubmitChanges(). Tiene dos opciones: puede hacer que la excepción se emita inmediatamente cuando se detecta un conflicto de concurrencia o puede esperar a que se escriban todos los cambios pendientes. En este último caso, se genera la excepción (y los cambios en los campos para los que hay un conflicto se cancelan) si se detecta uno o más conflictos de concurrencia. Una ventaja de esta opción es que la propiedad MemberConflicts contiene una lista completa de todos los conflictos de concurrencia (y otras discrepancias) de todo el conjunto de cambios que se enviaron. Por consiguiente, la lógica de resolución puede controlar todo el conjunto. Debe determinar qué opción elegir mediante la elección de la sobrecarga de SubmitChanges() a la que llamará y los parámetros que le pasará.

En el siguiente código se muestra la manera más fácil de resolver las discrepancias.

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

try 
{
    teamSite.SubmitChanges();
}
catch (ChangeConflictException e) 
{
    teamSite.ChangeConflicts.ResolveAll();
    teamSite.SubmitChanges();
}

La primera llamada de SubmitChanges() es la sobrecarga sin parámetros. Genera la excepción en cuanto se detecta el primer conflicto de concurrencia. Por lo tanto, la llamada de ChangeConflictCollection.ResolveAll() solo resuelve todas las discrepancias registradas hasta ese punto. Si hay otro conflicto de concurrencia incluido entre los cambios que se enviaron, ocasiona la segunda llamada de SubmitChanges() para generar la excepción.

En el siguiente código se muestra un ejemplo más complejo de resolución de discrepancias.

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

try 
{
    teamSite.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e) 
{
    foreach (ObjectChangeConflict changedListItem in teamSite.ChangeConflicts)
    {
        // If another user has changed properties of a non-manager,
        // leave that other user’s changes, except for the TopTask field.
        if (((TeamMember)changedListItem.Object).IsManager = false)
        {        
             foreach (MemberChangeConflict changedField in changedListItem.MemberConflicts)
            {
                if (changedField.Member.Name == "TopTask")
                {
                    changedField.Resolve(RefreshMode.KeepCurrentValues);
                }
                else
                {
                    changedField.Resolve(RefreshMode.OverwriteCurrentValues);
                }
            }
        }
        // But if another user has changed properties of a manager, let this
        // process’s changes override the other user’s changes.
        else
        {
            changedListItem.Resolve(RefreshMode.KeepCurrentValues);
        }    
    }

    teamSite.SubmitChanges();
} // end catch