Skip to content

Cesivi Server - CSOM Programming Guide

Version: 2.0 Last Updated: 2026-03-28


Table of Contents

  1. Overview
  2. Setup
  3. Basic Operations
  4. List Operations
  5. List Item Operations
  6. File Operations
  7. User and Group Operations
  8. Known Limitations
  9. Best Practices

Overview

Cesivi Server supports the SharePoint Client-Side Object Model (CSOM) for .NET applications. This enables you to programmatically interact with Cesivi using the familiar SharePoint CSOM API.

Supported CSOM Version: SharePoint Server 2019 / SharePoint Server Subscription Edition

Compatibility: 100% of common CSOM operations are supported (as of 2026-03-28) - CSOM Test Pass Rate: 100% (580P/0F/5S across 49 test classes) - Lambda expressions: ✅ Fully working - Property selection: ✅ Fully working - Nested Include(): ✅ Fully working


Setup

Installation

Install the SharePoint CSOM NuGet packages:

Install-Package Microsoft.SharePointOnline.CSOM -Version 16.1.24211.12000

Or for on-premises SharePoint Server:

Install-Package Microsoft.SharePoint2019.CSOM -Version 16.0.10372.20015

Basic Connection

using Microsoft.SharePoint.Client;

// Create client context
var siteUrl = "http://localhost:5000/Default/RootSite";
using (var context = new ClientContext(siteUrl))
{
    // Configure authentication
    context.Credentials = new NetworkCredential("username", "password");

    // Or use Basic authentication
    // context.Credentials = new SharePointOnlineCredentials("username", securePassword);

    // Execute operations
    Web web = context.Web;
    context.Load(web, w => w.Title, w => w.Description);
    context.ExecuteQuery();

    Console.WriteLine($"Site: {web.Title}");
    Console.WriteLine($"Description: {web.Description}");
}

Basic Operations

Get Current Web

using (var context = new ClientContext(siteUrl))
{
    context.Credentials = credentials;

    Web web = context.Web;
    context.Load(web);
    context.ExecuteQuery();

    Console.WriteLine($"Title: {web.Title}");
    Console.WriteLine($"URL: {web.Url}");
    Console.WriteLine($"Created: {web.Created}");
}

Load Specific Properties ✅ WORKING

// ✅ Load only specific properties (now fully working!)
context.Load(web,
    w => w.Title,
    w => w.Description,
    w => w.Created);
context.ExecuteQuery();

// ✅ Works with multiple properties
context.Load(list,
    l => l.Title,
    l => l.Description,
    l => l.Id,
    l => l.ItemCount);
context.ExecuteQuery();

Status: ✅ Lambda expressions with property selection now work correctly (as of 2026-01-22, PLAN-171).


List Operations

Create a List

using (var context = new ClientContext(siteUrl))
{
    context.Credentials = credentials;

    Web web = context.Web;
    ListCreationInformation creationInfo = new ListCreationInformation
    {
        Title = "Project Tasks",
        Description = "Task tracking list",
        TemplateType = (int)ListTemplateType.Tasks  // 107
    };

    List list = web.Lists.Add(creationInfo);
    context.Load(list);
    context.ExecuteQuery();

    Console.WriteLine($"List created: {list.Title} (ID: {list.Id})");
}

List Template Types: - GenericList (100) - Custom list - DocumentLibrary (101) - Document library - Tasks (107) - Task list - Calendar (106) - Calendar - Announcements (104) - Announcements

Get a List

// Get by title
List list = web.Lists.GetByTitle("Project Tasks");
context.Load(list);
context.ExecuteQuery();

// Get by ID
List listById = web.Lists.GetById(listGuid);
context.Load(listById);
context.ExecuteQuery();

Get All Lists

ListCollection lists = web.Lists;
context.Load(lists);
context.ExecuteQuery();

foreach (List list in lists)
{
    Console.WriteLine($"{list.Title} ({list.ItemCount} items)");
}

Update a List

List list = web.Lists.GetByTitle("Project Tasks");
list.Description = "Updated description";
list.EnableVersioning = true;
list.Update();
context.ExecuteQuery();

Delete a List

List list = web.Lists.GetByTitle("Project Tasks");
list.DeleteObject();
context.ExecuteQuery();

List Item Operations

Create an Item

List list = web.Lists.GetByTitle("Project Tasks");
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
ListItem newItem = list.AddItem(itemCreateInfo);

newItem["Title"] = "Complete documentation";
newItem["Status"] = "Active";
newItem["Priority"] = 10;
newItem["AssignedTo"] = new FieldUserValue { LookupId = userId };

newItem.Update();
context.ExecuteQuery();

Console.WriteLine($"Item created with ID: {newItem.Id}");

Get All Items

List list = web.Lists.GetByTitle("Project Tasks");
CamlQuery query = CamlQuery.CreateAllItemsQuery();

ListItemCollection items = list.GetItems(query);
context.Load(items);
context.ExecuteQuery();

foreach (ListItem item in items)
{
    Console.WriteLine($"{item.Id}: {item["Title"]}");
}

Query Items with CAML

CamlQuery query = new CamlQuery();
query.ViewXml = @"
<View>
    <Query>
        <Where>
            <Eq>
                <FieldRef Name='Status' />
                <Value Type='Choice'>Active</Value>
            </Eq>
        </Where>
        <OrderBy>
            <FieldRef Name='Priority' Ascending='FALSE' />
        </OrderBy>
    </Query>
    <RowLimit>10</RowLimit>
</View>";

ListItemCollection items = list.GetItems(query);
context.Load(items);
context.ExecuteQuery();

foreach (ListItem item in items)
{
    Console.WriteLine($"{item["Title"]} - Priority: {item["Priority"]}");
}

Get a Specific Item

List list = web.Lists.GetByTitle("Project Tasks");
ListItem item = list.GetItemById(1);
context.Load(item);
context.ExecuteQuery();

Console.WriteLine($"Title: {item["Title"]}");
Console.WriteLine($"Status: {item["Status"]}");

Update an Item

ListItem item = list.GetItemById(1);
item["Title"] = "Updated title";
item["Status"] = "Completed";
item.Update();
context.ExecuteQuery();

Delete an Item

ListItem item = list.GetItemById(1);
item.DeleteObject();
context.ExecuteQuery();

File Operations

Upload a File

List library = web.Lists.GetByTitle("Documents");
context.Load(library, l => l.RootFolder);
context.ExecuteQuery();

string fileName = "test-document.txt";
byte[] fileContent = System.Text.Encoding.UTF8.GetBytes("File content here");

FileCreationInformation fileInfo = new FileCreationInformation
{
    Content = fileContent,
    Url = fileName,
    Overwrite = true
};

File uploadedFile = library.RootFolder.Files.Add(fileInfo);
context.Load(uploadedFile);
context.ExecuteQuery();

Console.WriteLine($"File uploaded: {uploadedFile.Name}");

Download a File

File file = web.GetFileByServerRelativeUrl("/Default/RootSite/Documents/test-document.txt");
context.Load(file);
context.ExecuteQuery();

ClientResult<Stream> stream = file.OpenBinaryStream();
context.ExecuteQuery();

using (FileStream fileStream = System.IO.File.Create("downloaded-file.txt"))
{
    stream.Value.CopyTo(fileStream);
}

Get File Properties

File file = web.GetFileByServerRelativeUrl("/Default/RootSite/Documents/test-document.txt");
context.Load(file,
    f => f.Name,
    f => f.Length,
    f => f.TimeCreated,
    f => f.TimeLastModified);
context.ExecuteQuery();

Console.WriteLine($"Name: {file.Name}");
Console.WriteLine($"Size: {file.Length} bytes");
Console.WriteLine($"Created: {file.TimeCreated}");

User and Group Operations

Get Current User

User currentUser = web.CurrentUser;
context.Load(currentUser);
context.ExecuteQuery();

Console.WriteLine($"Current user: {currentUser.Title} ({currentUser.Email})");

Get a User

User user = web.EnsureUser("user@example.com");
context.Load(user);
context.ExecuteQuery();

Console.WriteLine($"User: {user.Title} (ID: {user.Id})");

Get All Users

Web web = context.Web;
UserCollection users = web.SiteUsers;
context.Load(users);
context.ExecuteQuery();

foreach (User user in users)
{
    Console.WriteLine($"{user.Title} ({user.Email})");
}

Get a Group

Group group = web.SiteGroups.GetByName("Owners");
context.Load(group);
context.ExecuteQuery();

Console.WriteLine($"Group: {group.Title} (ID: {group.Id})");

Add User to Group

User user = web.EnsureUser("user@example.com");
Group group = web.SiteGroups.GetByName("Members");
group.Users.AddUser(user);
context.ExecuteQuery();

Known Limitations

1. Lambda Expression Support ✅ RESOLVED

Status:WORKING AS OF 2026-01-22 (PLAN-171)

Lambda expressions in Load() statements now work correctly:

✅ Now Fully Supported:

// ✅ Simple properties
context.Load(list, l => l.Title, l => l.Description, l => l.Id);
context.ExecuteQuery();

// ✅ Nested Include() expressions
context.Load(web.Lists, lists => lists.Include(
    l => l.Title,
    l => l.Description,
    l => l.ItemCount
));
context.ExecuteQuery();

// ✅ Complex property selections
context.Load(web, w => w.Title, w => w.Description, w => w.Url, w => w.Created);
context.ExecuteQuery();

Test Coverage: - 25/25 tests passing (100%) in LoadLambdaTests.cs - Simple properties, multiple properties, nested Include() all working

2. Selective Property Loading ✅ RESOLVED

Status:WORKING AS OF 2026-01-22 (PLAN-171)

The Load() method with lambda property selectors now works correctly:

✅ Recommended Approach (all work now):

// Option 1: Load all properties
context.Load(list);
context.ExecuteQuery();

// Option 2: Load specific properties (now works!)
context.Load(list, l => l.Title, l => l.ItemCount);
context.ExecuteQuery();

// Option 3: Load with Include (now works!)
context.Load(web.Lists, lists => lists.Include(l => l.Title, l => l.Id));
context.ExecuteQuery();

3. AsEnumerable() in PnP Provisioning

Issue: Using AsEnumerable() on collections loaded via CSOM may cause CollectionNotInitializedException.

Workaround:

// Use foreach instead of LINQ
foreach (var item in collection)
{
    // Process item
}

// Instead of
// collection.AsEnumerable().Where(x => ...).ToList();

See Also: KNOWN_LIMITATIONS.md for comprehensive list of limitations


Best Practices

1. Batch Operations

Execute all Load() calls together to minimize round-trips:

// Good - Single ExecuteQuery()
context.Load(web);
context.Load(web.Lists);
context.Load(currentUser);
context.ExecuteQuery();

// Bad - Multiple ExecuteQuery() calls
context.Load(web);
context.ExecuteQuery();  // ❌
context.Load(web.Lists);
context.ExecuteQuery();  // ❌

2. Error Handling

Always wrap CSOM operations in try-catch blocks:

try
{
    List list = web.Lists.GetByTitle("NonExistentList");
    context.Load(list);
    context.ExecuteQuery();
}
catch (ServerException ex)
{
    Console.WriteLine($"CSOM Error: {ex.Message}");
    Console.WriteLine($"Server error code: {ex.ServerErrorCode}");
}

3. Dispose Context

Always dispose ClientContext to free resources:

using (var context = new ClientContext(siteUrl))
{
    // Operations here
}  // Automatically disposed

4. Use Proper Field Types

When setting field values, use appropriate field types:

// User field
item["AssignedTo"] = new FieldUserValue { LookupId = userId };

// Lookup field
item["Category"] = new FieldLookupValue { LookupId = categoryId };

// Multi-choice field
item["Tags"] = new string[] { "Tag1", "Tag2" };

// Date field
item["DueDate"] = DateTime.Now.AddDays(7);

Example: Complete Workflow

Here's a complete example demonstrating common operations:

using System;
using Microsoft.SharePoint.Client;

class Program
{
    static void Main()
    {
        var siteUrl = "http://localhost:5000/Default/RootSite";
        var credentials = new NetworkCredential("admin", "password");

        using (var context = new ClientContext(siteUrl))
        {
            context.Credentials = credentials;

            // 1. Create a list
            Web web = context.Web;
            ListCreationInformation creationInfo = new ListCreationInformation
            {
                Title = "Project Tasks",
                TemplateType = (int)ListTemplateType.Tasks
            };

            List list = web.Lists.Add(creationInfo);
            context.Load(list);
            context.ExecuteQuery();
            Console.WriteLine($"List created: {list.Title}");

            // 2. Add items
            for (int i = 1; i <= 5; i++)
            {
                ListItem item = list.AddItem(new ListItemCreationInformation());
                item["Title"] = $"Task {i}";
                item["Status"] = i % 2 == 0 ? "Completed" : "Active";
                item["Priority"] = i * 2;
                item.Update();
            }
            context.ExecuteQuery();
            Console.WriteLine("5 items added");

            // 3. Query items
            CamlQuery query = new CamlQuery();
            query.ViewXml = @"
            <View>
                <Query>
                    <Where>
                        <Eq>
                            <FieldRef Name='Status' />
                            <Value Type='Choice'>Active</Value>
                        </Eq>
                    </Where>
                </Query>
            </View>";

            ListItemCollection items = list.GetItems(query);
            context.Load(items);
            context.ExecuteQuery();

            Console.WriteLine($"\nActive tasks:");
            foreach (ListItem item in items)
            {
                Console.WriteLine($"  {item["Title"]} - Priority: {item["Priority"]}");
            }

            // 4. Update an item
            ListItem firstItem = list.GetItemById(1);
            firstItem["Status"] = "Completed";
            firstItem.Update();
            context.ExecuteQuery();
            Console.WriteLine("\nUpdated item 1");

            // 5. Delete an item
            ListItem lastItem = list.GetItemById(5);
            lastItem.DeleteObject();
            context.ExecuteQuery();
            Console.WriteLine("Deleted item 5");

            // 6. Verify final count
            items = list.GetItems(CamlQuery.CreateAllItemsQuery());
            context.Load(items);
            context.ExecuteQuery();
            Console.WriteLine($"\nFinal item count: {items.Count}");
        }
    }
}

Additional Resources

  • API_REFERENCE.md - REST API reference documentation
  • ODATA_QUERY_GUIDE.md - OData query syntax and examples
  • KNOWN_LIMITATIONS.md - Comprehensive list of known limitations
  • PNP_COMPATIBILITY.md - PnP PowerShell compatibility guide

Last Updated: 2026-03-28 Version: 2.0 Compatibility: SharePoint Server 2019 / SharePoint Server Subscription Edition CSOM