0

I’ve been tasked with converting some code we have in MVC from .NET Framework to .NET Core. The code dynamically binds a View to the appropriate Model based on a simple pattern. The View name is the first field in the page that is transmitted. For example, if the first data field is PAGE1 then there is a corresponding Model named PAGE1Model. The code does the dynamic binding by using ModelBindingContext(). This code works perfectly in .NET Framework but syntaxes in .NET Core.

I tried writing what I thought would be the equivalent in .NET Core. It compiles but it doesn't work. As I understand it, on the surface binding in .NET Core and Framework produce the same results but inside they’re different and I think that’s what’s causing the problem. Since I’m new to .NET Core I’ve spent 3 days searching for some code that will dynamically assign a Model to a Binder but every example I can find dynamically changes something within the View or Model but assumes the View and Model themselves are fixed.

This is the code that works in .NET Framework

private void BindModel(MapModel pModel)
    {
        Type ModelType = pModel.GetType();
        // bind model data
        var binder = Binders.GetBinder(ModelType);
        ModelBindingContext bindingContext = new ModelBindingContext()
        {
            ModelMetadata = 
              ModelMetadataProviders.Current.GetMetadataForType(() 
           => pModel, ModelType),
            ModelState = ModelState,
            ValueProvider = new FormValueProvider(this.ControllerContext)
        };
        binder.BindModel(ControllerContext, bindingContext);
    }

The input is the name of the Map + "Model" e.g. BindModel(Map1Model)

My one attempt at .NET Core, which doesn't work, is

public class ModelBinder : IModelBinder
{
    public ModelBinder(MapModel pModel, ControllerContext pContext)
    {
        model = pModel;
        context = pContext;
    }
    MapModel model;
    ControllerContext context;
    private readonly IModelBinder thisBinder;
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }
        bindingContext.Model = model;
        bindingContext.ValueProvider = (IValueProvider) context;
        return Task.CompletedTask;
    }
}

and the new call is

ModelBinder BindModel = new ModelBinder(Model, this.ControllerContext);

I expected this would accomplish the same thing as the .NET Framework code but it isn't doing anything because the BindModelAsync task is never being run.

The application is a legacy application moved to C# .NET from mainframe COBOL so I’m constrained in that I can’t change the input format or the correspondence between View name and Model name.

William C
  • 19
  • 4
  • What do you mean by dynamic binding? Binding is dynamic by default in Core. – Avin Kavish Jun 13 '19 at 19:54
  • 1
    The most common occurrence in a Controller is for the names of the View and the Model to be specified at compile time. I'm using dynamic binding to indicate that the View and the Model are specified at run time and will change from one transaction to another. – William C Jun 14 '19 at 20:32

2 Answers2

2

The accepted answer re-written to be non-blocking in modern C# syntax

public async Task<IActionResult> Action() {
    // ...
    if (ModelState.IsValid)
    {
        var result = await TryUpdateModelAsync(Model, Model.GetType(), "");
        if (!result)
           throw new Exception("TxServerController.TxMap: TryUpdateModelAsync failed");
    }
    ModelState.Clear();
}
Avin Kavish
  • 8,317
  • 1
  • 21
  • 36
-1

For the benefit of anyone else who has a similar problem, here is the code I came up with that actually works:

if (this.ModelState.IsValid)
            {
                Task<bool> task = this.TryUpdateModelAsync(Model, Model.GetType(), "");
                task.Wait();
                if (!task.Result)
                    throw new Exception("TxServerController.TxMap: TryUpdateModelAsync failed");
            }
            this.ModelState.Clear();
William C
  • 19
  • 4
  • Have you use the `await` keyword before? – Avin Kavish Jun 13 '19 at 19:58
  • In this case wait is the necessary keyword since the completion of the binding is required before any subsequent code can be executed. – William C Jun 14 '19 at 20:32
  • That's what await does. `await` waits without blocking the thread. `Wait()` waits too but blocks. https://stackoverflow.com/questions/13140523/await-vs-task-wait-deadlock – Avin Kavish Jun 14 '19 at 20:43