2
votes

I'm trying to return a ContentPart from the HTTP session in a Driver. Here is the code for my Driver and ContentPart:

public class UnpersistedPartDriver : ContentPartDriver<UnpersistedPart>
{
    public Localizer T { get; set; }
    private readonly IHttpContextAccessor _httpContextAccessor;

    public UnpersistedPartDriver(IHttpContextAccessor httpContextAccessor) {
        T = NullLocalizer.Instance;
        _httpContextAccessor = httpContextAccessor;
    }

    /// <summary>
    /// This method is responsible for displaying your part in the frontend.
    /// </summary>
    /// <param name="part">Your part.</param>
    /// <param name="displayType"></param>
    /// <param name="shapeHelper"></param>
    /// <returns></returns>
    protected override DriverResult Display(UnpersistedPart part, string displayType, dynamic shapeHelper)
    {
        var session = _httpContextAccessor.Current().Session;
        var cart = session["Cart"] as UnpersistedPart;
        if (cart == null) {
            cart = new UnpersistedPart();
            session["Cart"] = cart;
        }

        return ContentShape("Parts_Jumpstart_Unpersisted",
            () => shapeHelper.Parts_Jumpstart_Unpersisted(ContentPart: part));
    }

    // There is nothing to edit and update, so we don't need Editor methods.
}
public class UnpersistedPart : ContentPart
{
    public UnpersistedPart()
    {
        this.Items = new List<Item>();
        this.Items.Add(new Item { Id = 1, Title = "Book 1", Price = "130.00", Quantity = "1" });
        this.Items.Add(new Item { Id = 2, Title = "Book 2", Price = "145.00", Quantity = "2" });
        this.Items.Add(new Item { Id = 3, Title = "Book 3", Price = "150.00", Quantity = "3" });
    }

    public IList<Item> Items;
    public string Total
    {
        get 
        {
            return Items.Sum(i => i.Total).ToString();
        }
    }
    public string SessionId;
}

[Serializable]
public class Item
{
    public int Id;
    public string Title;
    public string Price;
    public string Quantity;
    public decimal Total { get { return decimal.Parse(Price) * int.Parse(Quantity); } }
}

When I change part to cart:

return ContentShape("Parts_Jumpstart_Unpersisted",
            () => shapeHelper.Parts_Jumpstart_Unpersisted(ContentPart: part));

to:

return ContentShape("Parts_Jumpstart_Unpersisted",
            () => shapeHelper.Parts_Jumpstart_Unpersisted(ContentPart: cart));

it doesn't display anything. I get an empty article tag:

<article class="content-item book" shape-id="0"></article>

1

1 Answers

5
votes

Empty tag usually means that there was some exception in the background. Please take a look in /App_Data/Logs.

I see what you want to achieve, but you should've done that differently.

Best practice is to utilize OnLoading event of content handler to populate part properties with data from different sources, eg. session. Storing the whole part in session will surely run you into trouble at some point, especially when the part is bound to a database record.

Handlers should perform all preparation operations.

Drivers should only hold display/editor-related logic on a ready-to-use part (like choosing the right shape, preparing the view model). So drivers should be kept very simple most of the time.

So in your case the steps would be:

  1. Create a CartHandler class that derives from ContentHandler
  2. Use OnLoading event in the handler created in 1. to hydrate the property/ies with data from session.

Example:

public class CartHandler : ContentHandler
{
    public CartHandler(IHttpContextAccessor context)
    {
        this.OnLoaded<UnpersistedPart>(ctx, part =>
        {
            var session = context.GetCurrent().Session;
            // Here comes loading data from session

            part.MyProperty = session["Something"]; 
            // ........ 
        });
    }
}

This event will get called every time a content item containing it will get loaded, before displaying it. So in the driver you will receive a fully populated part.

After adding a handler, your driver Display method would look way much simpler:

protected override DriverResult Display(UnpersistedPart part, string displayType, dynamic shapeHelper)
{
    return ContentShape("Parts_Jumpstart_Unpersisted",
        () => shapeHelper.Parts_Jumpstart_Unpersisted(ContentPart: part));
}