Nice3point / RevitTemplates

Templates for creating Revit add-ins
MIT License
208 stars 31 forks source link

How to add Dependency Injection support #39

Closed cesarecaoduro closed 5 months ago

cesarecaoduro commented 1 year ago

It will be extremely useful to add a mechanism for integrating dependency injection in the current template, similar to what has been done by OnBox . Services like telemetry or logs should be instantiated in the application and made available to all the commands.

Nice3point commented 1 year ago

Can I have an example of what you want from this library? After all, DI and other services can be added simply by installing the Nuget package if necessary

cesarecaoduro commented 1 year ago

In a complex application, there are services that you may want to have instantiated as singleton. Services like logs and telemetry are required across the entire application, and you want to make sure you decouple those from their implementation inside the different commands. Example: I would like to instantiate a telemetry client in the application entry point as singleton (using some sort of builder), and then have that available in the constructor of the command.

[Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    [Journaling(JournalingMode.NoCommandData)]
    public class SimpleCommand: IExternalCommand
    {
        private readonly ITelemetryService _telmetryService;

        public SimpleCommand(ITelmetryService telemetryService, ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            //Code here
            _telemetryService = telemetryService;
        }
}

Very common pattern in web development with netcore, but so far I haven't seen a lot of these implementation in the Revit world. The only one is the OnBox framework that I pointed out, but I see I am not sure that has been taken care of in the last couple of year. You have done an amazing work in pulling together this toolset for Revit developers, and this would just be ice on the cake 😄

Nice3point commented 8 months ago

Here's how you can do it:

  1. Create services using Microsoft.Extensions.DependencyInjection
public static class Host
{
    private static IServiceProvider _serviceProvider;

    public static void Start()
    {
        var services = new ServiceCollection();
        services.AddTransient<RevitAddin.Commands.HostedCommand>();
        services.AddTransient<RevitAddinView>();
        services.AddTransient<IRevitAddinViewModel, RevitAddinViewModel>();

        _serviceProvider = services.BuildServiceProvider();
    }

    public static T GetService<T>() where T : class
    {
        return _services.GetService<T>();
    }
}

Or create a host using Microsoft.Extensions.Hosting

public static class Host
{
    private static IHost _host;

    public static void Start()
    {
        var builder = Microsoft.Extensions.Hosting.Host.CreateApplicationBuilder(new HostApplicationBuilderSettings
        {
            ContentRootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly()!.Location),
            DisableDefaults = true
        });

        builder.Services.AddTransient<RevitAddin.Commands.HostedCommand>();
        builder.Services.AddTransient<RevitAddinView>();
        builder.Services.AddTransient<IRevitAddinViewModel, RevitAddinViewModel>();

        _host = builder.Build();
        _host.Start();
    }

    public static T GetService<T>() where T : class
    {
        return _host.Services.GetService(typeof(T)) as T;
    }
}
  1. Create an intefrace. It is required so that the host can create an instance of the new class with resolved dependencies

    public interface IHostedCommand
    {
    void Execute(ExternalCommand shell);
    }
  2. Create a command and implement the IHostedCommand interface. In the class constructor specify all the dependencies you need. You can use the primary constructor available in C#12

    public class HostedCommand (RevitAddinView view, ILogger logger, MyDependency dependency) : IHostedCommand
    {
    public void Execute(ExternalCommand shell)
    {
        logger.Debug("Hello from DI");
        view.ShowDialog();
    }
    }
  3. In the ExternalCommand that attaches to the Revit ribbon, get a new instance of the previously created HostedCommand and call the Execute method

[UsedImplicitly]
[Transaction(TransactionMode.Manual)]
public class RibbonCommand : ExternalCommand
{
    public override void Execute()
    {
        Host.GetService<RevitAddin.Commands.HostedCommand>().Execute(this);
    }
}
ricaun commented 8 months ago

@cesarecaoduro I create this package ricaun.DI with the core DI implementation based on the Onbox code.

And here are some extensions for Revit Api: ricaun.Revit.DI

Here is a full example: https://github.com/ricaun-io/RevitAddin.DI.Example

Nice3point commented 8 months ago

@ricaun why did you decide to make your own container instead of Microsoft's implementation ? What's the benefit ?)

ricaun commented 8 months ago

I already used the Onbox framework when Thiago released, and I needed a simple DI package to add in any project without adding any random dependency. Microsoft.Extensions.Hosting have too many dependencies, if you only need DI does not worth. https://www.nuget.org/packages/Microsoft.Extensions.Hosting#dependencies-body-tab

Nice3point commented 8 months ago

If you need only DI, you can use this package without any problems https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection

Nice3point commented 6 months ago

@cesarecaoduro preview release with DI implementation and samples: https://github.com/Nice3point/RevitTemplates/releases/tag/4.0.0-preview.1.0

cesarecaoduro commented 6 months ago

This is amazing... I am on it to do some testing.

On Sun, Mar 10, 2024 at 10:29 PM Roman @.***> wrote:

@cesarecaoduro https://github.com/cesarecaoduro preview release with DI implementation and samples: https://github.com/Nice3point/RevitTemplates/releases/tag/4.0.0-preview.1.0

— Reply to this email directly, view it on GitHub https://github.com/Nice3point/RevitTemplates/issues/39#issuecomment-1987193294, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGJG6AY55VWQCL64UBWBBITYXQ7YXAVCNFSM6AAAAAA2672P2WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSOBXGE4TGMRZGQ . You are receiving this because you were mentioned.Message ID: @.***>

Nice3point commented 6 months ago

@cesarecaoduro great, if you have any suggestions let me know, we still have half a month before public release

cesarecaoduro commented 6 months ago

Maybe a stupid question: how do I install the templates if they are in preview? Are you also looking at the possibility of having a similar solution for Civil3D and other Autocad based products?

On Mon, Mar 11, 2024 at 11:00 AM Roman @.***> wrote:

@cesarecaoduro https://github.com/cesarecaoduro great, if you have any suggestions let me know, we still have half a month before public release

— Reply to this email directly, view it on GitHub https://github.com/Nice3point/RevitTemplates/issues/39#issuecomment-1988026515, or unsubscribe https://github.com/notifications/unsubscribe-auth/AGJG6A4XY27VWFUK2RC22DLYXV6CBAVCNFSM6AAAAAA2672P2WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSOBYGAZDMNJRGU . You are receiving this because you were mentioned.Message ID: @.***>

Nice3point commented 6 months ago

@cesarecaoduro Nuget generates a command to install) изображение dotnet new install Nice3point.Revit.Templates::4.0.0-preview.1.0

There are no plans for Autocad/Civil, I abandoned them a long time ago and moved to Revit. Autodesk are similarly uninterested as well

Nice3point commented 5 months ago

@cesarecaoduro released. Tooltips with description you will see when creating a project in IDE. Description: https://github.com/Nice3point/RevitTemplates/wiki/Templates Samples: https://github.com/Nice3point/RevitTemplates/tree/main/samples

cesarecaoduro commented 5 months ago

Great job! Thanks for your commitment to the open source community.

Cesare Caoduro


From: Roman @.> Sent: Thursday, April 4, 2024 12:12:08 AM To: Nice3point/RevitTemplates @.> Cc: Cesare Caoduro @.>; Mention @.> Subject: Re: [Nice3point/RevitTemplates] How to add Dependency Injection support (Issue #39)

@cesarecaodurohttps://github.com/cesarecaoduro released Description: https://github.com/Nice3point/RevitTemplates/wiki/Templates Samples: https://github.com/Nice3point/RevitTemplates/tree/main/samples

— Reply to this email directly, view it on GitHubhttps://github.com/Nice3point/RevitTemplates/issues/39#issuecomment-2034569347, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AGJG6AZ6WXVBIQT7KQN6HRTY3P52RAVCNFSM6AAAAAA2672P2WVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDAMZUGU3DSMZUG4. You are receiving this because you were mentioned.Message ID: @.***>