Client-Side Object Model (CSOM) Support¶
Home → Documentation → Features → CSOM
Overview¶
Cesivi Server implements the Client-Side Object Model (CSOM) protocol, enabling .NET applications to interact with mock SharePoint environments using the same code that targets real SharePoint servers.
CSOM Coverage: 70.3% (175/249 tests passing) Status: ⚠️ Architectural Baseline - Protocol-level constraints limit further improvement Best For: .NET applications requiring complex object model operations
What Works¶
Core CRUD Operations (100% Coverage)¶
All fundamental create, read, update, and delete operations work reliably:
Web Operations: - ✅ Get web properties (Title, Description, URL, Created, etc.) - ✅ Create subsites - ✅ Update web properties - ✅ Delete webs
List Operations: - ✅ Get lists by title or ID - ✅ Create lists with templates - ✅ Update list properties - ✅ Delete lists - ✅ Enumerate all lists in a web
List Item Operations: - ✅ Get items by ID - ✅ Add new items - ✅ Update item fields - ✅ Delete items - ✅ Query items with CAML
File & Folder Operations: - ✅ Upload files - ✅ Download files - ✅ Get file metadata - ✅ Delete files - ✅ Create folders - ✅ Navigate folder hierarchy
Field & Content Type Operations: - ✅ Get fields by internal name or title - ✅ Add fields to lists - ✅ Get content types - ✅ Add content types to lists
Permission Operations: - ✅ Get role assignments - ✅ Break role inheritance - ✅ Grant permissions - ✅ Check effective permissions
Code Examples - Working Patterns¶
Basic Web and List Operations:
using Microsoft.SharePoint.Client;
// Connect to mock server
var ctx = new ClientContext("http://localhost:5000");
ctx.Credentials = new System.Net.NetworkCredential("testuser", "password");
// Get web properties (WORKS)
ctx.Load(ctx.Web);
ctx.ExecuteQuery();
Console.WriteLine($"Web Title: {ctx.Web.Title}");
// Get all lists (WORKS)
var lists = ctx.Web.Lists;
ctx.Load(lists);
ctx.ExecuteQuery();
foreach (var list in lists)
{
Console.WriteLine($"List: {list.Title}");
}
List Item CRUD Operations:
// Get a list (WORKS)
var list = ctx.Web.Lists.GetByTitle("Documents");
ctx.Load(list);
ctx.ExecuteQuery();
// Add an item (WORKS)
var itemCreateInfo = new ListItemCreationInformation();
var newItem = list.AddItem(itemCreateInfo);
newItem["Title"] = "My Document";
newItem.Update();
ctx.ExecuteQuery();
// Get items (WORKS - without lambda)
ctx.Load(list.Items);
ctx.ExecuteQuery();
foreach (var item in list.Items)
{
Console.WriteLine($"Item Title: {item["Title"]}");
}
// Update an item (WORKS)
var item = list.GetItemById(1);
ctx.Load(item);
ctx.ExecuteQuery();
item["Title"] = "Updated Title";
item.Update();
ctx.ExecuteQuery();
// Delete an item (WORKS)
var itemToDelete = list.GetItemById(1);
itemToDelete.DeleteObject();
ctx.ExecuteQuery();
File Operations:
// Upload a file (WORKS)
var fileCreationInfo = new FileCreationInformation
{
Content = System.IO.File.ReadAllBytes("document.pdf"),
Url = "document.pdf",
Overwrite = true
};
var uploadedFile = list.RootFolder.Files.Add(fileCreationInfo);
ctx.Load(uploadedFile);
ctx.ExecuteQuery();
// Download a file (WORKS)
var file = ctx.Web.GetFileByServerRelativeUrl("/sites/site/Shared Documents/document.pdf");
ctx.Load(file);
ctx.ExecuteQuery();
var fileInfo = Microsoft.SharePoint.Client.File.OpenBinaryDirect(ctx, file.ServerRelativeUrl);
using (var stream = fileInfo.Stream)
{
// Read file content
}
Known Limitations¶
1. Load() with Lambda Expressions (Primary Limitation)¶
Issue: Using Load() with lambda expressions (property selectors) often fails with PropertyNotInitializedException.
Root Cause: The Microsoft CSOM protocol uses a proprietary binary JSON serialization format that is not publicly documented. The mock server's response format doesn't exactly match Microsoft's implementation, causing the CSOM client library to not properly mark properties as "initialized."
Impact: ~54 tests (15.9% of CSOM test suite) fail due to this limitation.
Affected Scenarios:
// ❌ PROBLEMATIC PATTERN - May throw PropertyNotInitializedException
ctx.Load(folder, f => f.ItemCount);
ctx.Load(contentType, ct => ct.Fields);
ctx.Load(list, l => l.ContentTypes);
ctx.Load(web, w => w.Folders);
ctx.ExecuteQuery();
var count = folder.ItemCount; // May throw "property not initialized"
Collections Affected:
- contentType.Fields - CollectionNotInitializedException
- contentType.FieldLinks - CollectionNotInitializedException
- list.ContentTypes - CollectionNotInitializedException
- web.Folders - CollectionNotInitializedException
- web.RecycleBin - CollectionNotInitializedException
Properties Affected:
- folder.ItemCount - Property not initialized
- folder.ParentFolder - Property not initialized
- folder.WelcomePage - Property not initialized
Workaround:
// ✅ RECOMMENDED PATTERN - Load all properties
ctx.Load(folder); // No lambda expression
ctx.ExecuteQuery();
var count = folder.ItemCount; // Now works
// ✅ ALTERNATIVE - Use REST API for selective fields
var client = new HttpClient();
var response = await client.GetAsync(
"http://localhost:5000/_api/web/getfolderbyserverrelativeurl('/sites/site/Documents')?$select=ItemCount"
);
var json = await response.Content.ReadAsStringAsync();
2. Site Column Updates Not Persisted¶
Issue: Updates to site-level fields (web.Fields) via Field.Update() are not persisted to storage.
Root Cause: The persistence logic marks ParentList.HasPendingChanges when properties change, but site columns have ParentList = null by design.
Affected Operations:
- web.Fields.GetByInternalNameOrTitle("Title").Description = "New"; field.Update();
- Similar updates to Views and ContentTypes at the web level
Status: Documented architectural limitation - requires 4-6 hours of refactoring to fix.
Workaround: Currently no practical workaround. List-level field updates work correctly.
3. Binary JSON Serialization Format¶
Issue: Some complex CSOM operations fail with "Type of data at position X is different than expected" errors.
Root Cause: The CSOM protocol uses a proprietary binary JSON format. Without access to Microsoft's specification or real SharePoint Server for comparison, exact format matching is not possible.
Impact: ~27% of complex CSOM operations may fail.
Workaround: Use REST API for operations that fail with serialization errors.
Why These Limitations Exist¶
The CSOM Protocol Challenge¶
The CSOM protocol is: 1. Proprietary - Not publicly documented by Microsoft 2. Binary - Uses custom binary JSON serialization 3. Complex - Supports batching, lazy loading, and selective property retrieval 4. Closed-Source - CSOM client library source code not fully available
What Would Fix This: - Access to SharePoint Server Subscription Edition for response comparison - Microsoft CSOM protocol specification - CSOM client library source code access - Community experts with protocol knowledge
Investigation History: - 5 hours across 6 iterations (S2.26-S2.31) attempting fixes - All attempted fixes resulted in regressions - Conclusion: External resources needed for further improvement
Current Approach¶
The mock server implements CSOM based on: - ✅ Reverse engineering from CSOM client behavior - ✅ Analysis of network traffic patterns - ✅ Trial-and-error response format matching - ❌ No access to official protocol specification
Result: 70.3% coverage - reliable for common scenarios, limited for advanced operations.
API Selection Guide¶
When to Use CSOM ✅¶
Good Use Cases: - .NET applications requiring object model operations - Complex queries requiring multiple round-trips - When you can avoid selective property loading - Batch operations (ExecuteQueryBatch) - When REST API lacks required functionality
Example Scenario:
// CSOM excels at operations requiring multiple related objects
var web = ctx.Web;
var lists = web.Lists;
var users = web.SiteUsers;
ctx.Load(web);
ctx.Load(lists);
ctx.Load(users);
ctx.ExecuteQuery(); // Single round-trip retrieves all
// Now process all objects
foreach (var list in lists)
{
Console.WriteLine($"List: {list.Title}, Item Count: {list.ItemCount}");
}
When to Use REST API Instead ⚠️¶
Better Use Cases for REST:
- Selective field loading ($select=Title,Created)
- OData filtering and ordering
- JavaScript/TypeScript applications
- Single-property retrieval
- When CSOM throws PropertyNotInitializedException
Example Scenario:
// If this CSOM pattern fails:
ctx.Load(list.Items, items => items.Include(i => i["Title"], i => i["Created"]));
ctx.ExecuteQuery();
// Use REST API instead:
var client = new HttpClient();
var response = await client.GetAsync(
"http://localhost:5000/_api/web/lists/getbytitle('Documents')/items?$select=Title,Created"
);
var json = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<ODataResponse>(json);
Connection Examples¶
Basic Authentication¶
using Microsoft.SharePoint.Client;
var ctx = new ClientContext("http://localhost:5000");
ctx.Credentials = new System.Net.NetworkCredential("testuser", "password");
ctx.Load(ctx.Web);
ctx.ExecuteQuery();
Generic Authentication (Testing)¶
When AcceptAllCredentials: true in appsettings.json:
var ctx = new ClientContext("http://localhost:5000");
ctx.Credentials = new System.Net.NetworkCredential("any-user", "any-password");
ctx.Load(ctx.Web);
ctx.ExecuteQuery();
HTTPS with Certificate Validation¶
// For development with self-signed certificates
ServicePointManager.ServerCertificateValidationCallback =
(sender, cert, chain, sslPolicyErrors) => true;
var ctx = new ClientContext("https://localhost:5001");
ctx.Credentials = new System.Net.NetworkCredential("testuser", "password");
ctx.Load(ctx.Web);
ctx.ExecuteQuery();
Performance Characteristics¶
| Operation Type | Avg Response Time | Reliability | Notes |
|---|---|---|---|
| Simple Load | 15-30ms | High | Web, List, Item retrieval |
| Full Load | 20-40ms | High | Load without lambda |
| Selective Load | 25-50ms | Medium | May fail with lambda |
| Batch Operations | 50-100ms | High | ExecuteQueryBatch |
| File Upload | 100-500ms | High | Depends on file size |
Notes: - Response times measured on local server - Network latency adds 5-20ms - Selective loading may fail (see limitations)
Test Coverage Details¶
Overall Coverage: 175/249 tests passing (70.3%)
By Category: - ✅ Basic CRUD: 100% (all core operations work) - ✅ File Operations: 95%+ (upload, download, metadata) - ✅ Permission Operations: 90%+ (roles, assignments, inheritance) - ⚠️ Selective Loading: 46% (lambda expression issues) - ⚠️ Advanced Properties: 55% (complex object graphs)
Failing Scenarios (54 tests): - 40 tests: PropertyNotInitializedException (selective loading) - 14 tests: Binary JSON serialization errors
Troubleshooting¶
Error: PropertyNotInitializedException¶
Symptom:
Microsoft.SharePoint.Client.PropertyNotInitializedException: The property or field has not been initialized
Solution:
// ❌ Problematic code
ctx.Load(list, l => l.ItemCount);
ctx.ExecuteQuery();
var count = list.ItemCount; // Throws exception
// ✅ Fixed code
ctx.Load(list); // No lambda
ctx.ExecuteQuery();
var count = list.ItemCount; // Works
Error: Type of data at position X is different than expected¶
Symptom:
The type of data at position X is different than expected
Solution: Use REST API for this operation instead:
// Instead of complex CSOM operation, use REST
var response = await httpClient.GetAsync("/_api/web/lists/...");
Cannot contact site or server did not respond¶
Symptom:
Cannot contact site at the specified URL
Solutions:
1. Wait 5-10 seconds after starting server (initialization time)
2. Verify server is running: GET http://localhost:5000/health
3. Check authentication credentials
4. Verify firewall/network settings
Related Documentation¶
Features¶
- REST API - Alternative API with better compatibility
- PnP PowerShell - PowerShell wrapper around CSOM
- SOAP Services - Legacy web services
- Authentication - Auth methods and configuration
Reference¶
- Known Limitations - Complete limitation details
- Error Catalog - Error patterns and solutions
- API Reference - Complete API documentation
Troubleshooting¶
- Troubleshooting Guide - Common issues and fixes
- Quick Start - Get started quickly
Last Updated: November 15, 2025 Version: 1.0.0
Navigation: Home | Documentation | Features | CSOM