How are you supposed to use the Preview 2 IControllerFactory?
In the 1st preview, we were given the type, which made it very easy to pull objects out of an IoC, in my case Windsor.
Now, all I am given is the short name, i.e. "Admin" for "AdminController".
I like the type better, but is there some guidance on how to use this?
My solution for MvcContrib's WindsorControllerFactory has been this:
1) Register controllers in Windsor using the controller's type name (rather than full name):
foreach (Assembly assembly in controllerAssemblies)
{
foreach (Type type in assembly.GetExportedTypes())
{
if (IsController(type))
{
container.AddComponent(type.Name, type);
}
}
}
2) From the ControllerFactory you can then do something like this
I really don't like having to append "Controller" on the controller name, but found no other way to do it, like you said, it feels / smells like a hack.
One of the types of things we changed from Preview 1 to Preview 2 is that we're trying to make fewer assumptions about the structure and layout of your application. One such example is that in Preview 1 we required your controller name to be of the format
"{namespace}.{controller}Controller". While that works most of the time, there are advanced scenarios where it does not. For example, with some dependency injection libraries there is no type name to map to.
However, to keep things simple for the most common scenarios, by default we do make
some assumptions, such as the naming conventions for controllers. If you're extending the MVC (or Routing) frameworks then you typically have four options:
1. Use it as-is, without any changes.
2. Swap out an entire piece of the framework by implementing the base interface or base abstract class.
3. Derive from a concrete base class and override some methods to slightly change the behavior.
4. Use a combination of #2 and #3. Implement a base interface but delegate some of the hard work to an existing concrete class.
For the specific problem you're trying to solve, I recommend option #3 or #4. Implement your own IControllerFactory, but delegate some of the work to DefaultControllerFactory. It already has a method that takes in the "short controller name" and returns the matching Type.
In Preview 1 that logic was hard-coded and not customizable at all (well, it was sort of customizable, but it was extremely cumbersome).
Thanks,
Eilon
Blog: http://weblogs.asp.net/LeftSlipper/
Marked as answer by jyoung1024 on Mar 10, 2008 03:37 AM
Hmm... did not know there ws a DefaultControllerFactory lurking around. [:D]
I took your advice and inherited from DefaultControllerFactory instead of IControllerFactory and simply overrode the GetControllerInstance method, which as you mentioned passes a type instead of a short name.
Jeremy, There's a small problem with your hack in that the IWindsorContainer.Resolve() method is case sensitive but URLs are not. I've slightly changed your code here to get it working with some more notable hackery:
JeremyS
My solution for MvcContrib's WindsorControllerFactory has been this:
1) Register controllers in Windsor using the controller's type name (rather than full name):
foreach (Assembly assembly in controllerAssemblies)
{
foreach (Type type in assembly.GetExportedTypes())
{
if (IsController(type))
{
// convert type name to lower case container.AddComponent(type.Name.ToLower(), type); } } }
2) From the ControllerFactory you can then do something like this
public IController CreateController(RequestContext context, string controllerName) {
// convert to lower case here controllerName.ToLower += "controller"; IWindsorContainer container = GetContainer(context); return (IController)container.Resolve(controllerName); }
A bit hacky...but it works. The complete code can be found
here if you're interested.
Unless I'm missing something, you really should use the AddComponentWithLifestyle method when registering the controllers and set the controller's lifestyle to 'Transient'. Otherwise they'll use the default singleton lifestyle and hang around for the life
of the application. Here's my complete 'InitializeWindsor' method with a little bit of Linq goodness thrown in for good measure :)
protected virtual void InitializeWindsor()
{
if (container == null)
{
// create a new Windsor Container
container = new WindsorContainer(new XmlInterpreter("Configuration\\Windsor.config"));
// set the controller factory to the Windsor controller factory (in MVC Contrib)
ControllerBuilder.Current.SetControllerFactory(typeof(WindsorControllerFactory));
}
}
Would it be nice to have a method on WindsorControllerFactory that does the controller registration? Or maybe an extension method so you can just write:
jyoung1024
Member
14 Points
37 Posts
Preview 2 IControllerFactory
Mar 09, 2008 05:14 AM|LINK
How are you supposed to use the Preview 2 IControllerFactory?
In the 1st preview, we were given the type, which made it very easy to pull objects out of an IoC, in my case Windsor.
Now, all I am given is the short name, i.e. "Admin" for "AdminController".
I like the type better, but is there some guidance on how to use this?
JeremyS
Member
506 Points
99 Posts
Re: Preview 2 IControllerFactory
Mar 09, 2008 10:55 AM|LINK
My solution for MvcContrib's WindsorControllerFactory has been this:
1) Register controllers in Windsor using the controller's type name (rather than full name):
foreach (Assembly assembly in controllerAssemblies) { foreach (Type type in assembly.GetExportedTypes()) { if (IsController(type)) { container.AddComponent(type.Name, type); } } }2) From the ControllerFactory you can then do something like this
A bit hacky...but it works. The complete code can be found here if you're interested.
jyoung1024
Member
14 Points
37 Posts
Re: Preview 2 IControllerFactory
Mar 09, 2008 08:49 PM|LINK
Thats just about how I did it as well.
I really don't like having to append "Controller" on the controller name, but found no other way to do it, like you said, it feels / smells like a hack.
Thanks, for the reply.
abombss
Member
575 Points
164 Posts
Re: Preview 2 IControllerFactory
Mar 09, 2008 09:10 PM|LINK
Show me something in ms mvc that doesn't feel like a hack?
Nice work Jeremy on getting everything building in contrib for preview 2.
Eilon
Contributor
5753 Points
976 Posts
Microsoft
Re: Preview 2 IControllerFactory
Mar 10, 2008 01:14 AM|LINK
Hi everyone,
One of the types of things we changed from Preview 1 to Preview 2 is that we're trying to make fewer assumptions about the structure and layout of your application. One such example is that in Preview 1 we required your controller name to be of the format "{namespace}.{controller}Controller". While that works most of the time, there are advanced scenarios where it does not. For example, with some dependency injection libraries there is no type name to map to.
However, to keep things simple for the most common scenarios, by default we do make some assumptions, such as the naming conventions for controllers. If you're extending the MVC (or Routing) frameworks then you typically have four options:
1. Use it as-is, without any changes.
2. Swap out an entire piece of the framework by implementing the base interface or base abstract class.
3. Derive from a concrete base class and override some methods to slightly change the behavior.
4. Use a combination of #2 and #3. Implement a base interface but delegate some of the hard work to an existing concrete class.
For the specific problem you're trying to solve, I recommend option #3 or #4. Implement your own IControllerFactory, but delegate some of the work to DefaultControllerFactory. It already has a method that takes in the "short controller name" and returns the matching Type. In Preview 1 that logic was hard-coded and not customizable at all (well, it was sort of customizable, but it was extremely cumbersome).
Thanks,
Eilon
jyoung1024
Member
14 Points
37 Posts
Re: Preview 2 IControllerFactory
Mar 10, 2008 03:35 AM|LINK
Hmm... did not know there ws a DefaultControllerFactory lurking around. [:D]
I took your advice and inherited from DefaultControllerFactory instead of IControllerFactory and simply overrode the GetControllerInstance method, which as you mentioned passes a type instead of a short name.
Thanks!
mikehadlow
Member
44 Points
20 Posts
Re: Preview 2 IControllerFactory
Mar 13, 2008 01:05 PM|LINK
Jeremy, There's a small problem with your hack in that the IWindsorContainer.Resolve() method is case sensitive but URLs are not. I've slightly changed your code here to get it working with some more notable hackery:
JeremyS
Member
506 Points
99 Posts
Re: Preview 2 IControllerFactory
Mar 13, 2008 03:50 PM|LINK
Good catch - I'll see about modifying MvcContrib's WindsorControllerFactory tomorrow.
mikehadlow
Member
44 Points
20 Posts
Re: Preview 2 IControllerFactory
Mar 13, 2008 04:32 PM|LINK
Wait there's more...
Unless I'm missing something, you really should use the AddComponentWithLifestyle method when registering the controllers and set the controller's lifestyle to 'Transient'. Otherwise they'll use the default singleton lifestyle and hang around for the life of the application. Here's my complete 'InitializeWindsor' method with a little bit of Linq goodness thrown in for good measure :)
protected virtual void InitializeWindsor()
{
if (container == null)
{
// create a new Windsor Container
container = new WindsorContainer(new XmlInterpreter("Configuration\\Windsor.config"));
// automatically register controllers
Assembly.GetExecutingAssembly().GetExportedTypes()
.Where(type => IsController(type))
.Each(type => container.AddComponentWithLifestyle(
type.Name.ToLower(),
type,
Castle.Core.LifestyleType.Transient));
// set the controller factory to the Windsor controller factory (in MVC Contrib)
ControllerBuilder.Current.SetControllerFactory(typeof(WindsorControllerFactory));
}
}
private bool IsController(Type type)
{
return typeof(IController).IsAssignableFrom(type);
}
Would it be nice to have a method on WindsorControllerFactory that does the controller registration? Or maybe an extension method so you can just write:
container.AddControllersFrom(assembly);
Mike
JeremyS
Member
506 Points
99 Posts
Re: Preview 2 IControllerFactory
Mar 13, 2008 05:15 PM|LINK
Yes, you're right. I'm actually using a custom Facility that automatically sets the lifestyle for all controllers to transient.
Good idea about adding some registration methods - I'll have a look at doing this.