Skip to content

Commit

Permalink
Merge pull request #165 from autofac/revert-164-release-7.0
Browse files Browse the repository at this point in the history
Revert "Release 7.0"
  • Loading branch information
tillig authored Mar 6, 2023
2 parents dc3f00a + a427ba9 commit 7f8ab44
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 337 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/update-version-tag.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
env:
# This is the tag that will be kept up to date with master.
DOC_VERSION: v7.0.0
DOC_VERSION: v6.0.0
# Don't update the tag at the same time another update is running.
concurrency: update-version-tag
steps:
Expand Down
27 changes: 0 additions & 27 deletions docs/advanced/delegate-factories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,30 +179,3 @@ Lifetime Scopes and Disposal
============================

Just as with the ``Func<T>`` relationships or calling ``Resolve<T>()`` directly, using delegate factories is resolving something from a lifetime scope. If the thing you're resolving is disposable, :doc:`the lifetime scope will track it and dispose of it when the scope is disposed <../lifetime/disposal>`. Resolving directly from the container or from a very long-lived lifetime scope when using disposable components may result in a memory leak as the scope holds references to all the disposable components resolved.

RegisterGeneratedFactory (Obsolete)
===================================

.. important::

``RegisterGeneratedFactory`` is now marked as obsolete as of Autofac 7.0. Delegate factories and the :doc:`function relationships <../resolve/relationships>` have superseded this feature.

The now-obsolete way to handle a loosely coupled scenario where the parameters are matched on type was through the use of ``RegisterGeneratedFactory()``. This worked very similar to delegate factories but required an explicit registration operation.

.. sourcecode:: csharp

public delegate DuplicateTypes FactoryDelegate(int a, int b, string c);

Then register that delegate using ``RegisterGeneratedFactory()``:

.. sourcecode:: csharp

builder.RegisterType<DuplicateTypes>();
builder.RegisterGeneratedFactory<FactoryDelegate>(new TypedService(typeof(DuplicateTypes)));

Now the function will work:

.. sourcecode:: csharp

var func = scope.Resolve<FactoryDelegate>();
var obj = func(1, 2, "three");
1 change: 0 additions & 1 deletion docs/advanced/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Advanced Topics
constructor-selection.rst
concurrency.rst
multitenant.rst
scopes-loadcontexts.rst
pipelines.rst
aggregate-services.rst
interceptors.rst
Expand Down
74 changes: 0 additions & 74 deletions docs/advanced/scopes-loadcontexts.rst

This file was deleted.

144 changes: 1 addition & 143 deletions docs/register/prop-method-injection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,120 +21,7 @@ To support :doc:`circular dependencies <../advanced/circular-dependencies>`, use

builder.Register(c => new A()).OnActivated(e => e.Instance.B = e.Context.Resolve<B>());

Required Properties
-------------------

From Autofac 7.0 onwards, for :ref:`reflection components <register-registration-reflection-components>`, all `required properties <https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required>`_ are automatically resolved at the time of object construction, and are generally treated in much the same way as mandatory constructor arguments.

For example, given the following type:

.. sourcecode:: csharp

public class MyComponent
{
public required ILogger Logger { get; set; }

public required IConfigReader ConfigReader { get; set; }

public IDatabaseContext Context { get; set; }
}

When the component is resolved, Autofac will populate the ``Logger`` and ``ConfigReader`` properties as if they were constructor parameters. The ``Context`` property will be treated like a standard property and will not be populated by default.

Required property injection also works automatically in all base classes with required properties:

.. sourcecode:: csharp

public class ComponentBase
{
public required ILogger Logger { get; set; }
}

public class MyComponent : ComponentBase
{
public required IConfigReader ConfigReader { get; set; }
}

In the above example, resolving ``MyComponent`` would populate ``Logger`` in the base class, as well as ``ConfigReader`` in the component itself.

.. important::

Autofac does *not* consider the nullability of the type of a required property to indicate any sort of "optional" required property. If the property is marked as ``required``,
then it is required, and must be injected, or provided via a parameter, regardless of its nullability.

Required Properties and Constructors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can mix-and-match constructors and required properties if you so wish:

.. sourcecode:: csharp

public class MyComponent
{
public MyComponent(ILogger logger)
{
Logger = logger;
}

private ILogger Logger { get; set; }

public required IConfigReader ConfigReader { get; set; }
}

When multiple constructors are available, by default Autofac selects the constructor with the most matching parameters (unless :doc:`custom constructor selection is used <../advanced/constructor-selection>`). This remains the case, and the set of required properties has no impact on the selected constructor.

Autofac has no idea whether or not you set a given required property inside a constructor. Take this example:

.. sourcecode:: csharp

public class MyComponent
{
public MyComponent()
{
}

public MyComponent(ILogger logger)
{
Logger = logger;
}

public required ILogger Logger { get; set; }
}

Here, the constructor that Autofac will pick is going to be the one that takes the ``ILogger`` parameter, which in turn sets the ``Logger`` property. However, since ``Logger`` is marked as a required property, Autofac will resolve ``ILogger`` a second time, and inject it into the required property.

To avoid this, mark constructors that set all your required properties with the `SetsRequiredMembers <https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.setsrequiredmembersattribute>`_ attribute:

.. sourcecode:: csharp

using System.Diagnostics.CodeAnalysis;

public class MyComponent
{
public MyComponent()
{
}

[SetsRequiredMembers]
public MyComponent(ILogger logger)
{
Logger = logger;
}

public required ILogger Logger { get; set; }
}

Since the constructor is marked as setting all required members, no required property injection will occur in Autofac, when *that constructor* is used to create an instance of the component.

Required Properties and Parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Any ``TypedParameter`` provided at :doc:`registration <parameters>` or :doc:`resolve <../resolve/parameters>` will be considered when injecting required properties. However, ``NamedParameter`` and ``PositionalParameter`` are not considered valid parameters for property injection, since they are considered to only apply to constructor parameters.

PropertiesAutowired
-------------------

You can use the ``PropertiesAutowired()`` modifier at registration time to inject properties on any component:
If the component is a :ref:`reflection component <register-registration-reflection-components>`, use the ``PropertiesAutowired()`` modifier to inject properties:

.. sourcecode:: csharp

Expand All @@ -156,41 +43,12 @@ You can use the ``PropertiesAutowired()`` modifier at registration time to injec
// is important!
builder.RegisterType<C>().PropertiesAutowired(new MyCustomPropSelector());

Manually Specifying Properties
------------------------------

If you have one specific property and value to wire up, you can use the ``WithProperty()`` modifier:

.. sourcecode:: csharp

builder.RegisterType<A>().WithProperty("PropertyName", propertyValue);

Overriding Required Properties
------------------------------

Any property values provided for required properties using the ``WithProperty`` method when registering a type will override the requirement to inject that property, and Autofac will use the provided value instead:

.. sourcecode:: csharp

public class MyComponent
{
public required ILogger Logger { get; set; }

public required IConfigReader ConfigReader { get; set; }
}

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>().WithProperty("Logger", new ConsoleLogger());

var container = builder.Build();

// This will not throw, despite ILogger not being registered.
// The Logger property is provided by WithProperty.
container.Resolve<MyComponent>();

Injecting Properties on an Existing Object
------------------------------------------

You can also populate *just the properties* on an object. Do this using the ``InjectUnsetProperties`` extension on a lifetime scope, which will resolve and populate properties that are *public, writable, and not yet set (null)*:

.. sourcecode:: csharp
Expand Down
38 changes: 0 additions & 38 deletions docs/register/registration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,44 +96,6 @@ Note that you will still need to have the requisite parameters available at reso

.. note:: You can find advanced methods of customising which constructor to use :doc:`here <../advanced/constructor-selection>`.

Required Properties
-------------------

Starting in Autofac 7.0, in a reflection-based component, all `required properties <https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required>`_ are automatically resolved, in the same manner as constructor parameters.

All required properties of the component *must* be resolvable services (or supplied as a :doc:`parameter <../resolve/parameters>`) otherwise an exception will be thrown when trying to resolve the component.

For example, consider a class with these properties:

.. sourcecode:: csharp

public class MyComponent
{
public required ILogger Logger { get; set; }

public required IConfigReader ConfigReader { get; set; }
}

You can register and use this class as you could if it had a constructor:

.. sourcecode:: csharp

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<ConfigReader>().As<IConfigReader>();
var container = builder.Build();

using(var scope = container.BeginLifetimeScope())
{
// Logger and ConfigReader will be populated.
var component = scope.Resolve<MyComponent>();
}

Required properties are also set automatically on all base classes (if they are present); this makes required properties useful with deep object hierarchies, because it allows you to avoid having to invoke base constructors with the set of services; Autofac will set the base class properties for you.

.. note:: For more details on required property injection, see the dedicated section in the :doc:`property injection documentation <prop-method-injection>`.

Instance Components
===================

Expand Down
Loading

0 comments on commit 7f8ab44

Please sign in to comment.