Skip to content

Coolicky/Revit.DependencyInjection

Repository files navigation

Revit Dependency Injection

This repository borrows heavily from Onbox framework to create dependency inject with Revit Plugins. Instead of using custom container created by Onbox Team this is using Unity Container.

I've removed some libraries and utilities (like their mvc like packages). Packed all of it into a single dll for simplicity.

Installation

The package can be downloaded from Nuget

Application

Make sure that the application class that would normally inherit from IExternalApplication inherits from Revit App

[ContainerProvider("{{GUID}}")] // This attribute is crucial
public class App : RevitApp
{
	public override void OnCreateRibbon(IRibbonManager  ribbonManager)
	{
		// Here you can create Ribbon tabs, panels and buttons
	}
	public override Result OnStartup(IUnityContainer container,  UIControlledApplication application)
	{
		// Add Container registrations here
    
		return Result.Succeeded;
	}
	public override Result OnShutdown(IUnityContainer container, UIControlledApplication application)
	{
		// The container will be disposed automatically
    
		return Result.Succeeded;
	}
}

Command

Make sure that the command class that would normally inherit from IExternalCommand inherits RevitAppCommand<App> where "App" refers to the application class

[Transaction(TransactionMode.Manual)] // Important to every command
public class SampleCommand : RevitAppCommand<App>
{
	public override Result Execute(IUnityContainer container, ExternalCommandData commandData, 
                                 ref string message, ElementSet elements)
	{
		// Your logic here
	}
}

Container Registration

Registration follows standard Unity procedures below just the basics from their documentation.

Instance Registration

var instance = new Service();
container.RegisterInstance(instance);

// Named Registration
container.RegisterInstance("UniqueName", instance);

// Mapped to type
container.RegisterInstance<IService>(instance);
container.RegisterInstance<IService>("UniqueName", instance);
or
container.RegisterInstance(typeof(IService), instance)
container.RegisterInstance(typeof(IService), "UniqueName", instance)

Lifetime

// Per container (Default)
container.RegisterInstance("UniqueName", instance, InstanceLifetime.PerContainer);
container.RegisterInstance<IService>("UniqueName", instance, InstanceLifetime.PerContainer);

// Singleton
container.RegisterInstance("UniqueName", instance, InstanceLifetime.Singleton);
container.RegisterInstance<IService>("UniqueName", instance, InstanceLifetime.Singleton);

// External (Unity will not control the instance lifetime)
container.RegisterInstance<IService>("Some Name", instance, InstanceLifetime.External);

Factory Registration

container.RegisterFactory<IService>(f => new Service());

Type Registration

// Standard
container.RegisterType<IService, Service>();

// Named
container.RegisterType<IService, MailService>("Email");

// Lifetime
container.RegisterType<IService, MailService>("Email", TypeLifetime.Singleton);

You can either register required services directly in the OnStartup method

Or create extension methods

public static class SamplePipeline
{
	public static IUnityContainer RegisterSampleServices(this IUnityContainer container)
	{
		container.RegisterType<IService, Service>();
		return container;
	}
}

public class App : RevitApp
{
	[...]
	public override Result OnStartup(IUnityContainer container,  UIControlledApplication application)
	{
		// Direct
		container.RegisterType<IService, Service>();
		// Extension
		container.RegisterSampleServices();
		...
		return Result.Succeeded;
	}
	[...]
}

Resolving

For more detailed explanation refer to Unity Documentation

Generally speaking you can get instance of a class/service by

var instance = Container.Resolve<Service>();

Unity framework should automatically resolve services required by a constructor of a class

  • static constructors are not supported
  • private and protected constructors are not accessible
  • Constructors with ref and out parameters are not supported

Constructors are selected as below:

  • If present, use registered Injection Constructor
  • If present, annotated with an attribute
  • Automatically select constructor
    • Get all accessible constructors
    • Process constructors in ascending order from most complex to the default
      • Filter out restricted constructors
      • Loop through parameters and check if
        • Is primitive
          • Is registered with the container
          • Has default value
        • Is resolvable type
        • Is registered with container
      • Select the first constructor the container can create

Sample

// Default
public class Service
{
	private  readonly  IDependency _dependency;
	
	public Service(IDependency dependency)
	{
		_dependency = dependency;
	}
}

// Constructor Annotation
public class Service
{
	private  readonly  IDependency _dependency;
	
	[InjectionConstructor]
	public Service(IDependency dependency)
	{
		_dependency = dependency;
	}
	
	public Service()
	{
	}
}

// Or Registering a specific constructor
Container.RegisterType<Service>(Invoke.Constructor());

Project Template

For convenience I've created a project templates

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages