asp.net-corejson.netcustom-model-binder

How to bind a model to a session in ASP MVC Core


I bind the model to a session in ASP MVC Framework like this:

 public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {

            Cart cart = null;


            if(controllerContext.HttpContext.Session != null)
            {
                cart = (Cart)controllerContext.HttpContext.Session[sessionKey];
            }


            if(cart == null)
            {
                cart = new Cart();

                if (controllerContext.HttpContext.Session != null)
                {

                    controllerContext.HttpContext.Session[sessionKey] = cart;
                }


            }

            return cart;
        }

Now I want to do the same thing in ASM MVC Core, and this was my attempt:

public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            Cart cart = null;

            if (bindingContext.HttpContext.Session != null)
            {
                cart = (Cart)JsonConvert.DeserializeObject(bindingContext.HttpContext.Session.GetString(sessionKey));
            }

            if (cart == null)
            {
                cart = new Cart();

                if (bindingContext.HttpContext.Session != null)
                {
                    bindingContext.HttpContext.Session.SetString(sessionKey, JsonConvert.SerializeObject(cart));
                }


            }

            return Task.CompletedTask;
        }

I also have the class for model binder provider.

But I get a run-time error on this line, saying that the object is null:

cart = (Cart)JsonConvert.DeserializeObject(bindingContext.HttpContext.Session.GetString(sessionKey));

The string returned from 'GetString(sessionKey)' is null. The full message is:

System.ArgumentNullException: 'Value cannot be null. Parameter name: value''.

Solution

  • The question doesn't mention what exception is thrown, but this code is guaranteed to fail the first time an attempt is made to read from the session.

    The second snippet tries to deserialize a string without checking whether it's null or not :

    cart=(Cart)JsonConvert.DeserializeObject(bindingContext.HttpContext.Session.GetString(sessionKey));
    

    Or, in a more readable way:

    var json=bindingContext.HttpContext.Session.GetString(sessionKey);
    cart = (Cart)JsonConvert.DeserializeObject(json);
    

    JsonConvert.DeserializeObject() will throw if its argument is null.

    The json string must be checked before calling DeserializeObject. With some cleanup, the code could look like this:

    var session=bindingContext.HttpContext.Session;
    if(session == null)
    {
        return null;
    }
    var json = sessio.GetString(sessionKey);
    if (!String.IsNullOrWhitespace(json))
    {
        var cart=JsonConvert.DeserializeObject<Cart>(json);
        return cart;
    }
    else
    {  
        var emptyCart=new Cart();
        var value=JsonConvert.SerializeObject(emptyCart);
        session.SetString(sessionKey, value);
        return emptyCart;
    }
    

    The null safe operator can be used to handle missing context values, eg during testing :

    var session=bindingContext?.HttpContext?.Session;
    

    This will return null if any of the objects is null.