question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Enhance decorator support and syntax

See original GitHub issue

There are several issues around how we handle decorators and enhancements that are desired in that space. Updating the syntax and adding features sort of go hand-in-hand since, for the most part, it’ll all require some changes to the internals and will likely introduce some overall breaking changes.

This issue is to round up discussions about what we want decorator syntax to look like and what additional features we want to support. After that’s determined, let’s get it done. Fixing up decorator syntax has been a long time coming.

Issues:

  • #529: Handle .As<T> and .AsImplementedInterfaces correctly for decoration targets
  • #727: Conditional decorator registration
  • #776: Composite pattern support
  • #874: Decorator registration without keys

I think @alexmg had a spike going with some ideas on an updated decorator syntax. I’ll let him chime in as able. In the meantime, I’ll close these other issues and we can handle it here.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:12
  • Comments:41 (20 by maintainers)

github_iconTop GitHub Comments

3reactions
alexmgcommented, Mar 25, 2018

Thanks for the feedback everyone. I have taken it on board and made some changes.

I managed to remove the requirement for using RegisterDecorated as a marker registration. You can register the implementation service as normal and then use the new decorator syntax for registering your decorators.

builder.RegisterType<ImplementorA>().As<IDecoratedService>();
builder.RegisterDecorator<DecoratorA, IDecoratedService>();

When adding a registration in a child lifetime scope, if there is a decorator already registered in a parent scope, then it will be applied to the new registration.

var builder = new ContainerBuilder();
builder.RegisterDecorator<DecoratorA, IDecoratedService>();
var container = builder.Build();

var scope = container.BeginLifetimeScope(b => b.RegisterType<ImplementorA>().As<IDecoratedService>());
var instance = scope.Resolve<IDecoratedService>();

Assert.IsType<DecoratorA>(instance);

A decorator can be registered in a child lifetime scope and will only be applied to resolutions within that scope.

var builder = new ContainerBuilder();
builder.RegisterType<ImplementorA>().As<IDecoratedService>();
var container = builder.Build();

var scope = container.BeginLifetimeScope(b => b.RegisterDecorator<DecoratorA, IDecoratedService>());

var scopedInstance = scope.Resolve<IDecoratedService>();
Assert.IsType<DecoratorA>(scopedInstance);

var rootInstance = container.Resolve<IDecoratedService>();
Assert.IsType<ImplementorA>(rootInstance);

You can now register decorators using a lambda syntax as shown below, where c is IComponentContext, p is IEnumerable<Parameter>, and i is IDecoratedService.

builder.RegisterType<ImplementorA>().As<IDecoratedService>();
builder.RegisterDecorator<IDecoratedService>((c, p, i) => new DecoratorA(i));

I also introduced the same functionality for open generic decorators (with the exception of lambda based registrations). There is no need to call a RegisterDecorated method for open generics either. The registrations are made as normal using the existing RegisterGeneric method.

var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(ImplementorA<>)).As(typeof(IDecoratedService<>);
builder.RegisterGenericDecorator(typeof(DecoratorA<>), typeof(IDecoratedService<>));
var container = builder.Build();

Assert.IsType<DecoratorA<int>>(container.Resolve<IDecoratedService<int>>());

The decorators (regular and open generic) work in conjunction with the existing relationship types like Func, Owned, Lazy and IEnumerable.

var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(ImplementorA<>)).As(typeof(IDecoratedService<>));
builder.RegisterGeneric(typeof(ImplementorB<>)).As(typeof(IDecoratedService<>));
builder.RegisterGenericDecorator(typeof(DecoratorA<>), typeof(IDecoratedService<>));
var container = builder.Build();

var services = container.Resolve<IEnumerable<IDecoratedService<int>>>();

Assert.Collection(
    services,
    s =>
    {
        Assert.IsType<DecoratorA<int>>(s);
        Assert.IsType<ImplementorA<int>>(s.Decorated);
    },
    s =>
    {
        Assert.IsType<DecoratorA<int>>(s);
        Assert.IsType<ImplementorB<int>>(s.Decorated);
    });

Decorator services are tracked when added to the component registry so that when resolving a service decoration is only attempted if the service is known to be decorated.

The new decorator support lives alongside the existing decorator implementation and there are no breaking changes in the API.

You can test out the updates by grabbing the 4.6.2-decorators-00449 package from our MyGet feed.

https://www.myget.org/F/autofac/api/v3/index.json

I would love for people to take this for a test and drive and help lookout for edge cases. Depending on feedback I will merge the changes from the decorators feature branch into the dev branch.

The DecoratorTests and OpenGenericDecoratorTests fixtures in the decorator branch are the currently best place to find additional usage examples.

https://github.com/autofac/Autofac/tree/decorators/test/Autofac.Test/Features/Decorators

2reactions
alexmgcommented, Feb 10, 2019

This is now available in the 4.9.0 release. Thank you to everyone for your feedback and contributions.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Documentation - Decorators
Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members. Further Reading (stage 2): A Complete...
Read more >
Basic Syntax and Usage of Decorators in Python | by Jane
In this article, I want to discuss a powerful feature in Python called decorators and how they can be used to enhance your...
Read more >
Understanding Decorators in JavaScript: An Essential Tool ...
TypeScript, a superset of JavaScript, has native support for decorators. The syntax and behaviour of decorators in TypeScript closely ...
Read more >
Using modern decorators in TypeScript
Here's an example of a simple class method decorator, demonstrating the enhanced ergonomics of the new syntax: function debugMethod(_target: ...
Read more >
TypeScript Decorators: Enhance Your Code With Ease
TypeScript decorators are a powerful feature that allows developers to add extra functionality to classes, methods, properties, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found