Condividi tramite


How to: Write to Lists Using LINQ to SharePoint

Applies to: SharePoint Foundation 2010

This topic explains how to code against the LINQ to SharePoint provider to add or delete list items from Microsoft SharePoint Foundation lists and to change the values of particular fields in list items.

Steps 1 and 2: Getting References to Websites and Lists

For an explanation of how to get a reference in your code to the website and list whose data you want to change, see steps 1 and 2 of the topic How to: Query Using LINQ to SharePoint.

Step 3: Verify that Object Change Tracking is Enabled

The ObjectTrackingEnabled property must be at its default value of true before you can use LINQ to SharePoint to make changes to the database. If your code sets this property to false and has queried the DataContext object with this value in place, the property cannot be reset to true. Accordingly, if your code queries the DataContext object and then makes changes to the content database, you have two options:

  • Refrain from having your code set the ObjectTrackingEnabled property to false.

  • Create a new DataContext object for the same website in your code, and locate it after the queries but before your code writes to the database. Leave the ObjectTrackingEnabled property of the new DataContext object at its default value of true, and use the new object to write to the content database.

Step 4: Add Basic Code for Adding, Deleting, Recycling, or Updating a List Item

The basic code to write to the content databases is simple. You begin by creating a reference to the website and list, and you finish with a call to SubmitChanges(). In between is the code where you make the changes you want to make by using one of the *OnSubmit methods of the EntitySet<TEntity> class, or you write to list fields by using ordinary property-setting syntax. In all examples that follow, teamSite is a DataContext object that represents a website, and TeamMembers is an EntitySet<TEntity> object that represents a Team Members list.

Important

The object that represents the list item you are adding, deleting, recycling, or updating must have the EntityState property, the Id property, and the Version property. It has these properties if the class that represents the content type is generated by SPMetal. The various *OnSubmit methods assign values to the EntityState property. The Id property and the Version property are set by the SharePoint Foundation runtime. Your code should not write to any of these three properties.

Adding a List Item

To add an item to a list, create an object of the content type of the list, and then pass it to the InsertOnSubmit(TEntity) method.

Important

Any properties on the list item object that represent required fields of the content type must have a value before the item is inserted in the list. (These same properties are declared with a [ColumnAttribute] decoration whose Required property is present and set to true.) For example, all content types inherit from the basic Item content type of SharePoint Foundation, and this content type has a required Title field. You can assign values to such properties between the call of InsertOnSubmit(TEntity) and the call of SubmitChanges(); but it is a good practice to initialize required properties as soon as possible. If there is no class constructor that initializes all the required properties, you can use an object initializer, as shown in the following example.

This example shows how to add an item to a list and then save the changes to the database.

// 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();

You can also use the InsertAllOnSubmit(IEnumerable<TEntity>) method to insert multiple items.

Deleting and Recycling a List Item

The following example shows how to use the DeleteOnSubmit(TEntity) method to remove an item from a list.

// 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();

If you want to put an item in the user’s Recycle Bin rather than delete it entirely, use the RecycleOnSubmit(TEntity) method. You can also use the DeleteAllOnSubmit(IEnumerable<TEntity>) method and the RecycleAllOnSubmit(IEnumerable<TEntity>) method to remove multiple items at the same time.

Changing a Field in a List Item

To change a value of a field on a list item, write to the property that represents the field, as shown in this example.

// 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();

Multiple Changes with a Single Call of SubmitChanges

There is no limit to the number of changes your code can make with a call of the SubmitChanges() method. For best performance, your code should make as few calls of it as possible. The following code shows an example of several kinds of changes being written to the content database with a single call of 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();

The changes you submit do not all have to be to the same list. Changes to all lists on a website can be submitted with a single call of SubmitChanges().

Step 5: Add Infrastructure for Managing Concurrency Conflicts

If you have changed any field values, before any changes are committed to the content database by a call of the SubmitChanges() method, the object change-tracking system checks whether some other user has changed any of the affected list items after the current user process retrieved them from the database. (For more information about this system, see Object Change Tracking and Optimistic Concurrency.) If there is a concurrency conflict, SubmitChanges() throws a ChangeConflictException. In addition, a MemberChangeConflict object is generated that represents information about the discrepancy. If there are other discrepancies between the current client value for a field and the field’s value in the database, each of them is also represented by a MemberChangeConflict object. All of the discrepancies for a specified list item are added to the MemberConflicts property of an ObjectChangeConflict object. Depending on which overload of SubmitChanges() was called and what parameters were passed to it, there may be more than one ObjectChangeConflict object. All of them are added to the ChangeConflicts property of the DataContext object.

Your code must catch the exception and resolve all discrepancies before calling SubmitChanges() again. In some situations, the best way of handling the issues is to prompt the user to decide how to resolve each discrepancy that arises. You can populate the user interface (UI) that you present to the user with data from the ObjectChangeConflict objects in the ChangeConflicts property, and from their child MemberChangeConflict objects; particularly the OriginalValue, DatabaseValue, and CurrentValue properties.

Tip

Consider distinguishing clearly in your UI the discrepancies that represent actual concurrency conflicts—on the one hand, discrepancies between the OriginalValue property and the DatabaseValue property, and on the other, discrepancies that simply represent changes that would have been written to the content database automatically if there had been no concurrency conflicts; that is, discrepancies between DatabaseValue and CurrentValue, where OriginalValue = DatabaseValue.

Implement the user’s choices with calls to some combination of the following methods:

In other cases, you may know when you are coding the best way to resolve discrepancies, based on the purpose of your application and the kind of changes it makes to the content databases. In such cases, it can be best to call some combination of the methods in the preceding list without prompting the user. There are three major things you have to think about while constructing your resolution logic:

  • Which version of a field’s value should be persisted in the content database: the original value, the value currently in the database, the value in your application’s process (the client value), or some fourth value? For guidance, see RefreshMode and the reference topics for the methods in the preceding list. Also, although the articles in the following table are about the LINQ to SQL provider, the logic they describe also applies to LINQ to SharePoint.

    How to: Resolve Concurrency Conflicts by Merging with Database Values (LINQ to SQL)

    How to: Resolve Concurrency Conflicts by Retaining Database Values (LINQ to SQL)

    How to: Resolve Concurrency Conflicts by Overwriting Database Values (LINQ to SQL)

  • What do you want to do when the current user has submitted a change to a list item that another user has deleted entirely from the list? For more information about your options, see Resolve(RefreshMode, Boolean) and ResolveAll(RefreshMode, Boolean).

  • At what level do you need to apply your decisions about the two preceding scenarios? Suppose, for example, you want to apply the following rules to every discrepancy for every list item for every list:

    • Ignore list items that have been deleted by another user.

    • Preserve all changes made by your application’s process and also those of other user processes, with preference for your own when they conflict with another user’s.

    You can apply this logic with a simple call to ChangeConflictCollection.ResolveAll().

    But if you need to preserve all changes on the items of some lists, while canceling your own process’s changes on items of certain other lists, your code must iterate the members of the ChangeConflicts property, calling different overloads of ObjectChangeConflict.Resolve(), and passing different parameters for different ObjectChangeConflict items.

    For some content types, you may need to apply different rules for different fields. In that situation, your code must iterate the members of the MemberConflicts property of some ObjectChangeConflict objects, call different overloads of MemberChangeConflict.Resolve(), and pass different parameters for different fields.

  • When do you want throw a ChangeConflictException exception and stop making further changes? This is part of your resolution logic, although you implement your decision with the call to SubmitChanges(). You have two options: You can have the exception thrown immediately when a concurrency conflict is detected, or you can have all pending changes continue to be written. In the latter case, the exception is thrown (and the changes to fields for which there is a conflict are canceled) if one or more concurrency conflicts are detected. One advantage of the second option is that the MemberConflicts property contains a complete itemization of all concurrency conflicts (and other discrepancies) from the entire set of changes that were submitted. Accordingly, your resolution logic can handle the entire set. You determine which option to take by which overload of SubmitChanges() you call and what parameters you pass to it.

The following code shows the simplest way to resolve all discrepancies.

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

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

The first call of SubmitChanges() is the parameterless overload. It throws the exception as soon as the first concurrency conflict is detected. Therefore, the call of ChangeConflictCollection.ResolveAll() only resolves all discrepancies recorded until that point. If there is another concurrency conflict included among the submitted changes, it causes the second call of SubmitChanges() to throw the exception.

The following code shows a more complex example of resolving discrepancies.

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