Upgrading from 1.1 to 2.0

DotVVM 2.0 brings several new features including:

There have been several breaking changes between DotVVM 1.1 and DotVVM 2.0. Perform the following steps to do the upgrade:

1. Upgrade DotVVM NuGet packages

Every DotVVM application uses either DotVVM.AspNetCore or DotVVM.Owin NuGet package. These packages depend on DotVVM and DotVVM.Core packages. All of these packages must be upgraded to their 2.x versions.

Run the following command in the Package Manager Console window, or click Manage NuGet Packages item in the context menu of the project and perform the upgrade in UI.

# for ASP.NET Core
Update-Package DotVVM.AspNetCore

# for OWIN
Update-Package DotVVM.Owin

Because of changes in DotVVM Compiler (a tool which provides metadata for Visual Studio IntelliSense), we had to move registration of DotVVM-related services into DotvvmStartup (or other class that implements IDotvvmServiceConfigurator interface).

In Startup.cs file, remove the lambda method in UseDotVVM call:

// OWIN
// ==========================================================

// DotVVM 1.1
var config = app.UseDotVVM<DotvvmStartup>(ApplicationPhysicalPath, options => 
{
    // copy the body of the lambda and remove it
    options.AddDefaultTempStorages("Temp");
});

// DotVVM 2.0
var config = app.UseDotVVM<DotvvmStartup>(ApplicationPhysicalPath);
// ASP.NET Core
// ==========================================================

// DotVVM 1.1
var config = app.UseDotVVM<DotvvmStartup>(options => 
{
    // copy the body of the lambda and remove it
    options.AddDefaultTempStorages("Temp");
});

// DotVVM 2.0
var config = app.UseDotVVM<DotvvmStartup>();

Add ConfigureServices method in DotvvmStartup.cs and place contents of the lambda inside:

public class DotvvmStartup : IDotvvmStartup
{
    ...

    public void ConfigureServices(IDotvvmServiceCollection options)
    {
        // paste the body of the lambda here
        options.AddDefaultTempStorages("Temp");
    }

}

And last, make the DotvvmStartup.cs class implement IDotvvmServiceConfigurator:

public class DotvvmStartup : IDotvvmStartup, IDotvvmServiceConfigurator
{
    ...
}

This ConfigureServices should register only services that are related to DotVVM - uploaded file storage, custom viewmodel loaders, or commercial controls like DotVVM Business Pack. All other services should be configured in Startup.cs like it was before.

3. Add jQuery resource if you need it

DotVVM 1.1 was including jQuery in the page in Debug mode, because it was required by dotvvm.debug.js helper library. The need for jQuery in this helper was removed with DotVVM 2.0, so jQuery resource is not registered in the DotVVM configuration.

If you application uses jQuery and if it is not included with another library (like Bootstrap for DotVVM or DotVVM Business Pack), add the following code into ConfigureResources method in DotvvmStartup.cs:

config.Resources.Register("jquery", new ScriptResource()
{
    // use relative URL if you ship jQuery with your application
    Location = new UrlResourceLocation("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js")
});

4. Route Registration with Custom Presenters

We have changed the signature of RouteTable.Add method for registering custom presenters. Now you can specify only a type of the presenter - in this case, the presenter instance will be retrieved from IServiceProvider.

An unnecessary parameter specifying the virtual path has been removed the overloads with presenters:

// DotVVM 1.1
config.RouteTable.Add("Rss", "export/rss", null, () => new RssPresenter());

// DotVVM 2.0
config.RouteTable.Add("Rss", "export/rss", typeof(RssPresenter));

5. Action Filters

If you have implemented custom action or exception filters and used OnPageLoadingAsync or OnPageLoadedAsync methods, they were removed and the behavior was changed.

These methods were originally called for DotVVM pages and also custom presenters, and because of a bug in the pipeline, they were called twice for DotVVM pages.

We have split these methods to presenter-level and page-level events:

  • Presenter-level Events (applicable to DotVVM pages and custom presenters)

    • OnPresenterExecutingAsync is executed immediately after the URL is mapped to a specific route and presenter is resolved. This method is called also for DotVVM pages as they are handled by DotvvmPresenter class.
    • OnPresenterExecutedAsync is executed immediately after the presenter completes processing of the request. This method is called also for DotVVM pages as they are handled by DotvvmPresenter class.
  • Page-level Events (applicable to DotVVM pages)

    • OnPageInitializedAsync is executed after the page control tree is built and viewmodel instance is initialized.
    • OnPageRenderedAsync is executed after the response is rendered completely and before the viewmodel instance is disposed.

If you need to handle these events for both DotVVM pages and custom presenters, override the presenter-level methods.

If you only need to handle these events for DotVVM pages, override the page-level methods.

5.1 Exception Filters

There is a new method OnPresenterExceptionAsync which is called when an unhandled exception is thrown from a custom presenter or a DotVVM page (which is processed using DotvvmPresenter).

If you have a custom exception filter and need to handle exceptions from presenters, you may want to override this exception to log the errors.

6. Custom PostBack Handlers

We have rearchitected the way how custom postback handlers are implemented.

If you implemented your own postback handlers, you will need to make the following change in the C# part of the handler:

...
protected override Dictionary<string, object> GetHandlerOptions()
{
    return new Dictionary<string, object>()
    {
        ["message"] = GetValueRaw(MessageProperty)      // TranslateValueOrBinding was removed - use GetValueRaw
    };
}
...

Then, update the JavaScript implementation of the postback handler so the execute method returns Promise:

dotvvm.events.init.subscribe(function () {
    dotvvm.postbackHandlers["confirm"] = function ConfirmPostBackHandler(options) {

        var message = options.message; // you'll get the parameters passed to the handler in the options object
        
        return {
                execute: function(callback) {
                    return new Promise(function (resolve, reject) {
                        // do whatever you need and if you need to do the postback, invoke the 'callback()' function
                        if (confirm(message)) {
                            // call next postback handler
                            callback().then(resolve, reject);
                        } else {
                            // signalize that the postback was canceled
                            reject({type: "handler", handler: this, message: "The postback was aborted by user."});
                        }
                    });
                },

                // optional settings
                after: ["xxx"],        // you can specify that this handler should be launched after some other handler
                before: ["xxx"]        // you can specify that this handler should be launched before some other handler
            };
        };
    });

Also note that dotvvm.postBackHandlers collection was renamed to dotvvm.postbackHandlers.

7. GridView Control

We have changed the way collection data are loaded into GridViewDataSet. In DotVVM 1.1, you could provide delegate to GridViewDataSet which would be used to load data. We have removed the delegate because loading the data could lead to deadlock.

public GridViewDataSet<Customer> Customers { get; set; }

public override Task Init()
{
    Customers = GridViewDataSet.Create(gridViewDataSetLoadDelegate: GetData, pageSize: 4);
    return base.Init();
}

In DotVVM 2.0, you have to manually load collection to GridViewDataSet when it's appropriate:

public GridViewDataSet<Customer> Customers { get; set; } 
        = new GridViewDataSet<Customer>() { PagingOptions = { PageSize = 4 } };

public override Task PreRender()
{
    if (Customers.IsRefreshRequired)
    {
        var queryable = GetData();
        Customers.LoadFromQueryable(queryable);
    }
    return base.PreRender();
}

8. Obsolete Constructs

There are several things in DotVVM 2.0 which were marked as obsolete. Although the features still work, we recommend to fix them soon.

1. ValueType property on TextBox, Literal and GridViewTextColumn

The ValueType property was needed whenever you worked with date or numeric values in TextBox, Literal or GridViewTextColumn controls. In DotVVM 2.0, this property was made obsolete and is not used by the framework - the type of the data-bound value is inferred automatically.

2. ComboBox now supports ItemTextBinding and ItemValueBinding

The DisplayMember property was replaced by ItemTextBinding, the ValueMember was replaced with ItemValueBinding.

<!-- DotVVM 1.1 -->
<dot:ComboBox DataSource="{value: People}" 
              DisplayMember="FullName" 
              ValueMember="Id" 
              SelectedValue="{value: SelectedPerson}" />

<!-- DotVVM 2.0 -->
<dot:ComboBox DataSource="{value: People}" 
              ItemTextBinding="{value: FullName}" 
              ItemValueBinding="{value: Id}" 
              SelectedValue="{value: SelectedPerson}" />

3. DotvvmConfiguration now exposes IServiceProvider

The ServiceLocator property was replaced by ServiceProvider in the DotvvmConfiguration.

See also