Cofoundry 0.9 delivers a raft of new features and bug fixes, predominantly around data models, annotations and admin UI controls.

We've also been busy over the last couple of months improving our documentation, in particular we now have a comprehensive section covering all the data model annotations available in Cofoundry.

Here's a rundown of the new features:

Multiple nested data model types in one collection

Nested data models were introduced in Cofoundry 0.3, allowing you to create data model types that can be used in the properties of other data models. An example of this is the data model for a Carousel block type, which could have a child collection of slides:

public class CarouselDataModel : IPageBlockTypeDataModel
    [NestedDataModelCollection(IsOrderable = true)]
    public ICollection<CarouselSlideDataModel> Slides { get; set; }

In this release we've added the [NestedDataModelMultiTypeCollection] data annotation to allow you to combine multiple different types of data into one list.

This can be useful for where you want to offer a range of options to the content editor. The example in our docs shows a "SocialProfiles" property made up of different types of links:

using Cofoundry.Domain;

public class ExampleDataModel : ICustomEntityDataModel
        new Type[] {
        IsOrderable = true,
        MinItems = 1,
        MaxItems = 3,
        TitleColumnHeader = "Profile"
    public ICollection<NestedDataModelMultiTypeItem> SocialProfiles { get; set; }

This will display in the editor as a table of items:

NestedDataModelMultiTypeCollection example displaying in the admin UI

The TitleColumnHeader property in the example above helps customize the grid when it is displaying multiple different content types. There's similar properties available for the description and image column, and we've also added them to the existing [CustomEntityMultiTypeCollection] annotation so that the display grids for these annotations work the same way.

See nested data model annotation documentation for more information

Date and Time controls

Our support for dates has been a bit lacking, as we only had the [Date] annotation, and it wasn't documented. We now have several date and time annotations to choose from, which now all appear in our documentation:

Note that all dates and times are stored in UTC, however the "local" variants of these annotation will capture/display the value in the user's local timezone and convert to UTC when saving to the database.

See date and time data annotation documentation for more information.

Breaking change with [Date]

Our existing [Date] annotation captured the date in the user's local timezone, however this has now been changed to capture UTC, making it consistent with the other controls, and mirror the equivalent input types in HTML.

If you are using [Date] and want to keep the existing behavior, change the annotation to [DateLocal] instead.

New default UI controls for primitive types

When defining a data model, you've never needed to add a data annotation for string, int or bool types. We've now expanded our default mapping to include every primitive type:

  • bool: Displays as a checkbox
  • char: Displays as a single-character text input
  • string: Displays as a text input
  • byte, sbyte, short, ushort, int, uint, long, ulong: Displays as an integer number input
  • float, double, decimal: Displays as a numerical input

See data model primitive types documentation for more information.

ReadOnly Field

A new [ReadOnly] data annotation has been added. This indicates that a property should not be editable in the admin UI.

You can still use other annotations to influence the display of the value, such as [DateAndTime] or [Image]

See read-only field documentation for more information.

Default Property Values

You can now add default values to your data model properties by initializing them in your data model:

public class CatDataModel : ICustomEntityDataModel
    public CatDataModel()
        RegistrationDate = DateTime.UtcNow;

    public DateTime RegistrationDate { get; set; }

    public int NumberOfOwners { get; set; } = 1;

Default values will be used to populate the "create" forms in the admin panel.

Image Uploading

Prior to Cofoundry 0.9, if you uploaded an image that exceeded the MaxUploadWidth or MaxUploadHeight settings, you would see an error message.

In this release we've improved the image upload control so that if these limits are exceeded, the image will be resized in-browser before uploading the file to Cofoundry.

A message is be displayed if resizing is to occur:

Automatic image downsizing of image uploads


We've added a CacheMode setting that can be used to define the lifetime scope of the default in-memory object cache.

The default value is Persitent, but it can be changed to PerScope to make the cache expire at the end of a request:

  "Cofoundry": {
    "InMemoryObjectCache:CacheMode": "PerScope"

This is an easy (but less performant) way of enabling a multi-server deployment and can be useful if you don't have another option such as a Redis cache.

See caching documentation for more information.

JSON Configuration

Cofoundry no longer relies on the JSON.NET configuration registered with JSON.NET or ASP.NET Core. This means that should you wish to, you can configure JSON.NET however you want without affecting Cofoundry.

For now Cofoundry will still set the default settings to our configuration, however you can override it by re-registering JSON.NET:

    .AddNewtonsoftJson(o =>
        o.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();

Note that the ASP.NET Core JSON serializer must still be set to JSON.NET, System.Text.Json is not yet supported. We expect to be migrating to System.Text.Json when .NET 6 arrives.

See JSON configuration documentation for more information.

Breaking changes

  • IApiController methods now returns JsonResult instead of IActionResult, allowing further customization of the result before it is returned.
  • IApiController methods no longer require a Controller parameter
  • The UI editor associated with the [Date] annotation now captures the date at midnight UTC time instead of local time. To capture local time use the [DateLocal] data annotation instead.
  • Razor comments blocks (i.e. @* My comment *@) are now removed when parsing PageTemplate files. If for some reason you are relying on comments being parsed in your templates, please check them before upgrading.


For a complete list of bug fixes and features, check out the full release notes on GitHub using the link below.