1

I'm building a custom content part that fetches its information from an external repository, mostly following the advice found @ How to change Orchard record repository and using a custom handler to fetch the data.

Working with external data stores opens up the possibility of all sorts of network exceptions, etc., which would cause the underlying record not be saved. However, if there's an exception thrown in the ContentHandler, it's swallowed up by the Invoke<TEvents> method so that (unless it's a "fatal" exception) the user wouldn't know about the exception and would be notified by the AdminController that "Your {0} has been saved.", when in fact it hasn't been.

A workaround that's obvious to me is to intercept the error somehow and notify the content driver for my content part, which exposes the executing AdminController. At that point, I can hook into the controller's ModelState and introduce an error, which would then be caught and then I'd be notified of an error without any false positive notifications.

Are there any other extensibility points available in Orchard that would handle this kind of external access better than altering the controller's ModelState via a content driver?

Community
  • 1
  • 1
Peter Majeed
  • 5,304
  • 2
  • 32
  • 57

1 Answers1

1

The easiest way would be to implement you own, simple, per-request storage object for those errors, ie.

public interface IErrorLog : IDependency {
    public void Add(string message){ ... }
    public IEnumerable<string> List() { ... }
}

public class DefaultErrorLog : IErrorLog { ... }

Inject IErrorLog in both your controller and handler. In the handler, catch all errors you need to catch and add them to the collection with Add(...). Then, in the controller, call List() and add model error for each entry.

UPDATE

If you're not in control of a Controller that updates your content item, then you should use a driver. Catch the exception during item save (the second Editor method with 3 params) and push some error info using AddModelError of the provided IUpdateModel object. Way easier, but you'll be able to catch only those errors that happen when an item is saved.

UPDATE 2

If you're not in control of a Controller that updates your content item but would like to have a pure solution and gain control over the whole process, you can use your own controller for editing items. In order to do that:

  • create a new controller (or copy and alter the default AdminController found in Orchard.Core\Contents) first
  • now tell Orchard to use this controller instead of the default one for all types that contain your custom part. It can be done by putting something like this in your handler:

    OnGetContentItemMetadata<MyCustomPart>((context, part) =>
    {
        context.Metadata.AdminRouteValues = new RouteValueDictionary 
        {
            { "Area", "My.Module" },
            { "Controller", "Admin" },
            { "Action", "Edit" },
            { "id", context.ContentItem.Id }
        };
    });
    
Piotr Szmyd
  • 13,371
  • 6
  • 44
  • 61
  • Thanks for the answer. I get the IoC approach here, but in this case, the controller is the `AdminController` in `Orchard.Core.Contents.Controllers`, since the item I'm editing is a content item. I could just add the dependency there, but I'm hesitant to do that as that's not something that'd normally be contained in a module. I could do it in the content driver, but I think that'd be the same approach as what I'm trying now. – Peter Majeed May 07 '13 at 20:45
  • Catching the exception and pushing information back to the controller would be way easier when done from the driver. In the second `Editor` method (the one with 3 params), the `IUpdateModel` object *is* in fact the base controller object. Just add the model error using `AddModelError` and you're set. – Piotr Szmyd May 07 '13 at 22:21
  • Right, that was my original plan. It works, but (I think) it has a bit of code smell. It's not really a `ModelError` here, but, say, a connection issue related to the external service. So far, that's the only way I know how to "trick" the `AdminController` to bypass the default notification actions, so my question was more to find out whether there's a better way to do that short of this approach. – Peter Majeed May 07 '13 at 22:27
  • Agreed. There is an ultimate option available if you need full control. Please see my second update. – Piotr Szmyd May 07 '13 at 23:53
  • Piotr, thanks much for sharing your advice and thoughts. I will try it out over the next few days and circle back and let you know how it goes. – Peter Majeed May 08 '13 at 03:31