In this release we have updated Cofoundry to target ASP.NET Core 2.1 and implemented a number of bug fixes and features. The release includes breaking changes; the ones you'll most likely encounter are mentioned here, but for a comprehensive list check out the full 0.4 release notes and do get in touch if you have any issues.
Please note that due to a tagging mishap the actual NuGet package version is 0.4.1.
Upgrading to .NET Core 2.1
Upgrading from .NET Core 2.0 to .NET Core 2.1 is a fairly painless process and is not considered a breaking change; you can find guidance from Microsoft on the upgrade process here.
There's a few ASP.NET 2.1 related changes in Cofoundry that you should be aware of when upgrading:
Compatibility Version
Cofoundry automatically sets the CompatibilityVersion
to Version_2_1
. In future updates we'll increment the compatibility version inline with framework updates once we've tested them with Cofoundry. You can override this behaviour by setting the compatibility version after AddCofoundry()
is called e.g.
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.AddCofoundry(Configuration)
.SetCompatibilityVersion(CompatibilityVersion.Version_2_0);
}
Startup
Cofoundry does not set CookiePolicyOptions
or run UseHsts
, UseHttpsRedirection
, or UseCookiePolicy
. These are outside the scope of Cofoundry and you should set them as appropriate for your application. As before, Cofoundry does handle errors and static files so there's no need to run UseDeveloperExceptionPage
, UseExceptionHandler
or UseStaticFiles
which are shown in the Microsoft upgrade document.
A Cofoundry 0.4 equivalent of the example startup.cs file would look like this:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Cofoundry.Web;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Hosting;
namespace WebApp1
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services
.AddMvc()
.AddCofoundry(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseCookiePolicy();
app.UseCofoundry();
}
}
}
Cookies
The Cofoundry auth cookie has been marked as essential so it does not conflict with the new cookie consent feature should you choose to use it.
Sourcelink
Cofoundry NuGet packages now use SourceLink so you can step into Cofoundry code when debugging your website. You'll need to disable the "Just My Code" debugging option in Visual Studio to enable this.
Duplicate custom entity
Cofoundry has always had the ability to duplicate/clone a page, but not a custom entity. We've now added that feature and also fixed page duplication so that it also copies all the block data.
To clone a custom entity, simply click the 'Duplicate' button in the admin panel and fill out the identity fields.
Referencing the ambient visual editor mode
We've added an injectable service that provides access to the visual editor mode of the request. This is useful if your displaying versioned data such as custom entities in a separate component and you want to display the correct version of the data to match the edit mode.
In the example below we use IVisualEditorStateService
to get an object representing the visual editor state for request and use it to get a PublishStatusQuery
value that we can use to query the blog post listing. If the visual editor is in live mode then only published blog posts will be returned from the query, otherwise the latest version (including drafts) will be returned.
public class HomepageBlogPostsViewComponent : ViewComponent
{
private readonly ICustomEntityRepository _customEntityRepository;
private readonly IVisualEditorStateService _visualEditorStateService;
public HomepageBlogPostsViewComponent(
ICustomEntityRepository customEntityRepository,
IVisualEditorStateService visualEditorStateService
)
{
_customEntityRepository = customEntityRepository;
_visualEditorStateService = visualEditorStateService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var visualEditorState = await _visualEditorStateService.GetCurrentAsync();
var query = new SearchCustomEntityRenderSummariesQuery();
query.CustomEntityDefinitionCode = BlogPostCustomEntityDefinition.DefinitionCode;
query.PublishStatus = visualEditorState.GetAmbientEntityPublishStatusQuery();
query.PageSize = 3;
var entities = await _customEntityRepository.SearchCustomEntityRenderSummariesAsync(query);
return View(entities);
}
}
More information can be found in our new entity versioning documentation.
Changes to custom entity sorting
We've updated the default ordering of custom entities for SearchCustomEntityRenderSummariesQuery
to order by title, which puts it inline with the behavior of other custom entity queries. This is a breaking change so if you were relying on that query to return entities by the create date then you can specify the sorting explicitly on the query:
var query = new SearchCustomEntityRenderSummariesQuery();
query.CustomEntityDefinitionCode = "EXAMPL";
query.SortBy = CustomEntityQuerySortType.CreateDate;
Note that if your definition implements IOrderableCustomEntityDefinition
then this change does not effect custom entities with a custom set ordering.
Specifying the default sort type
Alternatively you can now set the default sorting options on the custom entity definition by implementing ISortedCustomEntityDefinition
. This has the additional benefit of changing the sorting for the custom entity list view in the admin panel.
A good example of a custom entity we might want to change the sorting for is a blog post, which typically you'd want to order by publish date. Here's an example of what that might look like:
public class BlogPostCustomEntityDefinition
: ICustomEntityDefinition<BlogPostDataModel>
, ISortedCustomEntityDefinition
{
public const string DefinitionCode = "EXABLP";
public string CustomEntityDefinitionCode => DefinitionCode;
public string Name => "Blog Post";
/// …other properites removed for brevity
/// <summary>
/// The sorting to apply by default when querying collections of custom
/// entities of this type. A query can specify a sort type to override
/// this value.
/// </summary>
public CustomEntityQuerySortType DefaultSortType => CustomEntityQuerySortType.PublishDate;
/// <summary>
/// The default sort direction to use when ordering with the
/// default sort type.
/// </summary>
public SortDirection DefaultSortDirection => SortDirection.Default;
Setting the bounds of a pageable query
Cofoundry has built-in classes you can use to make paging your data models easier. You can create a query that supports paging by implementing IPageableQuery
, which is usually done by inheriting from the SimplePageableQuery
base class.
We've added some new extension methods to help you manage the bounds of your queries, setting the default page size and limiting the maxium page size to prevent large data requests.
public void Example(IPageableQuery query)
{
// Set default page size to 10, allow unbounded page size e.g. page size of -1 returns all items
query.SetBounds(10, true);
// Set default page size to 40, max page size to 100
query.SetBounds(40, 100);
}
Note that built-in Cofoundry queries that use paging do not set bounds, if you are exposing these queries to the web e.g. via a web api, then you should set those bounds to your requirements.
You can read more about this in the new paging documentation
Changes to transactions and message publishing
In a typical Cofoundry site it's unlikely that you'll be orchestrating transactions and dealing with messaging/events, but it's worth mentioning that the way we handle transactions has changed with this release.
I won't go into too much detail here, but our abstraction has been renamed fromITransactionScopeFactory
to ITransactionScopeManager
and our default implementation has moved away from using EF transactions to using System.Transactions
, which was recently re-introduced in .NET Standard 2.0.
We've also added the ability for ITransactionScopeManager
to queue up tasks that will execute after the transaction has completed, namely cache busting and message publishing tasks. The upshot of this is that when coordinating multiple commands that each trigger their own message publishing, you can rely on the messages only being published after the transaction has completed.
This is mostly an internal detail of Cofoundry, but if you're using transactions, particularly if you have your own DbContext
, then it's worth reading up on our new entity framework and transactions documentation. The main change you'll notice with this is that completion is now an async operation which allows for async tasks to be run after the scope has completed.
Automatic database updates
Please note that this release contains automated database updates.
These will apply when you run your application for the first time so please ensure you are not connecting to production databases from your development environment and have tested the update before applying it to production systems.
Other noteable features and bug fixes
- #217 Add mailto links to the default sanitization ruleset
- #206 Custom Entity Permissions: Easily add new permissions to roles after initialization
- #87 Improve Page Querying outside of the admin panel
- #229 Duplicate pages: Does not duplicate block content
Notable breaking changes
Whilst many of the breaking changes are unlikely to affect a typical Cofoundry site, there's a few that are worth highlighting here:
ApiResponseHelper.SimpleQueryResponse
now returns a 404 status code if the result is null.IEntityFrameworkSqlExecutor
: All sync operations have been removed.PropertyBuilderExtensions
has been moved toCofoundry.Core.EntityFramework
namespace.IDatabase
is no longer injected, useICofoundryDatabase
.ModelBuilder.UseDefaultConfig
has been replaced withHasAppSchema
because since the migration to EF Core no other configuration was being performed here other than setting the default schema.- Removed the generic versions of
IOrderableCustomEntityDefinition
andICustomizedTermCustomEntityDefinition
. Use the non-generic versions instead alongside the genericICustomEntityDefinition<TDataModel>
. The generic versions were a shortcut, but I think it's better to be explicit here so we don't have multiple ways of doing things which can be confusing. VisualEditorMode
has been moved from theCofoundry.Web
project to theCofoundry.Domain
project.VisualEditorMode.Draft
has been renamedVisualEditorMode.Preview
SortDirection
naming has been changed from Ascending/Descending to be Default/Reversed to better describe the behaviour, this is because for publish/create date sorting the default behaviour is latest first which is not technically ascending ordering.
A complete list of bug fixes, features and breaking changes can be found in the full version 0.4 release notes on GitHub.