Consolidated Data Access
The biggest feature of this release is the introduction of a new consolidated data access repository that simplifies working with Cofoundry data programmatically.
IContentRepository is the primary interface for our new data access API. It has an easily discoverable fluent API and is enriched with inline documentation to help you choose the most suitable query or command for you needs.
This example uses the fluent builder to describe the query we want and select how much detail is returned by selecting the projection type:
public class ExampleController : Controller
private readonly IContentRepository _contentRepository;
_contentRepository = contentRepository;
public async Task<IActionResult> Categories()
var categories = await _contentRepository
.CustomEntities() // select entity type
.GetByDefinition<CategoryCustomEntityDefinition>() // select query type
.AsRenderSummary() // select projection
.ExecuteAsync(); // execute
As you build the query, you can use the intellisense hints provided by our XML comments to understand the difference between different queries or projection types:
This example uses an MVC controller, but you can use this interface anywhere that supports dependency injection just by requesting an instance of
IContentRepository to be noise-free and only include the most common queries you might use in a website, however, we have also created
IAdvancedContentRepository, which builds upon
IContentRepository and includes the full range of queries and commands available in Cofoundry.
Both repositories can be extended via extension methods, so that plugins can light-up additional data access paths.
They also share the following features:
Here's an example combining a few of these features, which prior to this release would have required dependencies on 7 different services to be injected:
public async Task RegisterUser(string email, string password)
var exampleRoleId = await _advancedContentRepository
.Map(r => r.RoleId)
using (var scope = _advancedContentRepository
Email = email,
Password = password,
UserAreaCode = MemberUserArea.AreaCode,
RoleId = exampleRoleId
// ...do some other things
You can find out more in the data access docs.
Deprecated code will be removed in an upcoming release.
Handler names have been changed to remove the reference to "Async". When we used to target .NET Framework we needed sync handlers, but they are no longer necessary in .NET Core.
The individual entity repositories have now been replaced by
IContentRepository and are now marked as deprecated.
We've started moving some code to an
Internal namespace to better indicate that these classes are not for general use. This includes classes such as handler and service implementations, which should not be referenced directly.
Although Microsoft are in the process of removing all their "Pubternal" namespaces, for us I think it's a good way to keep all our code accessible while clearly defining what is and is not part of the public API surface.
We've updated some of the terms used in asset file type validation to put them inline with current standards:
If you've customized
AssetFilesSettings.MimeTypeValidation, you may need to update your settings.
PropertyValidationErrorException is now ValidationErrorException
We've improved some of our validation error classes in include a new
To enable this feature we have restructured this area a little and
PropertyValidationErrorException has changed to be
TransactionScopeManager: default isolation changed from Serializable to ReadCommitted
The default transaction scope isolation of Serializable is not ideal and not aligned with the SQLServer default. We have therefore decided to change it to ReadCommited.
We've also added ways to override this behaviour by default and on a per-use basis. See the Transactions section of the docs for more information on how to do this.
Notice on .NET 5 and .NET 6
Having been burnt a little with the support cycle around .NET Core 2.2 we are now only going to target LTS .NET releases. This means that we will not move to .NET 5 and instead wait for the .NET 6 LTS release.
- #383 Error loading page template detail page