Injecting properties in content parts

Nov 12, 2010 at 12:39 PM

Hello,

is it possible to inject properties in content parts? I've tried with [InjectProperties] but it doesn't work.

Coordinator
Nov 12, 2010 at 6:26 PM

Can you be a little more specific? What are you trying to do? How does it not work? Does it fail silently? Are you getting an error message?

Nov 12, 2010 at 6:52 PM
Edited Nov 12, 2010 at 7:01 PM

Hello,

I've got a class that derives from ContentPart:

 public class EventPart : ContentPart<EventPartRecord> {
   
}

I'm trying to inject the IAuthenticationService dependency at runtime, using Autofac. IAuthenticationService implements IDependency.

If I try to inject the service in the content part's constructor, Orchard complains saying I must declare a parameterless constructor:

public class EventPart : ContentPart<EventPartRecord> {
   private readonly IAuthenticationService _authService;

   public EventPart(IAuthenticationService authService) {
      _authService = authService;
   }
}

I thought I'd inject it using a property, but if I try to decorate the class with the [InjectProperties] attribute provided by Autofac, AuthService always returns null:

[InjectProperties]
public class EventPart : ContentPart<EventPartRecord> {
   public IAuthenticationService AuthService { get; set; }
}

My goal is to access the service in some of my content part's methods. What am I doing wrong?

Thanks.

 

 

Coordinator
Nov 12, 2010 at 9:06 PM

I think the part should not rely on external services. It's the driver or handler that should take care of that sort of thing. Makes sense?

Nov 13, 2010 at 8:40 AM
Edited Nov 13, 2010 at 8:44 AM

My EventPart has a method called MarkAsApproved, which

  • Verifies and manipulates some of its state variables
  • Sets the Status property to Approved
  • Adds an EventPartAction to an internal collection with details about the transition to the Approved state. These details involve some state variables as well as the username of the user that performed the action.

Given that all the values I need to perform the operation are stored inside the part, except for the username, I just need to query a service for the username of the authenticated user.

I've thought of different approaches. I can let an external object (a driver, a handler or whatever) play with my part's state:

public class AnExternalObject { 
   private readonly IAuthenticationService _auth;
  
   public MarkPartAsApproved(EventPart part) {
      // Play with part's state. 
     ...
      part.Status = status.Approved;
      part.RegisterAction(..., _auth.UserName, ...); 
   }
}

If I go that way, I'm moving all the logic in the external object. My part depends from that object and it becomes just a data holder. Or, I can do

public class AnExternalObject { 
   private readonly IAuthenticationService _auth;

   public MarkPartAsApproved(EventPart part) {
      part.MarkAsApproved();
      part.RegisterAction(..., Auth.UserName, ...); 
   }
}

In this way I'm splitting the logic for the "Approve" operation in two objects. My part depends on the external object and testing the Approve operation is slightly more difficult. Or, I can do

public class AnExternalObject { 
   private readonly IAuthenticationService _auth;

   public MarkPartAsApproved(EventPart part) { 
      part.MarkAsApproved(_auth.UserName); 
   }
}

This is not bad, but if I change my mind about some of the Approve operation's workings, or need to access other services, I might need to make changes in multiple objects.

If I go with dependency injection in EventPart, and I eventually want to make my model even Orchard-agnostic, I could do

public class Event : IEvent { 
   public class Event(IAuthenticationService authService) {}
}

public class EventPart : ContentPart<EventPartRecord> { 
   private IEvent _evt;

   public void MarkAsApproved() {
      _evt.MarkAsApproved(); 
   }
}

and all I need for performing this refactoring is already in one place (EventPart).

In the end, I'd prefer to perform the whole operation inside the part, and let the part query some external objects for the missing pieces. This is why I asked how to perform dependency injection in content parts. 

Does it make sense? Any suggestions for improving my design?

Dec 19, 2011 at 1:40 AM

You might want to check out the SmtpSettingsPart and how it uses the ComputedField utility.  I used it for something similar when I wanted to encrypt some fields in a content part.