AUTOFAC Official document (ix) "Parsing the implicit relationship type of a service"

Source: Internet
Author: User

AUTOFAC supports the implicit automatic resolution of specific types to support special relationships between components and services. To take full advantage of these relationships, simply register the component, but either change the constructor parameter in the component or resolve the type in resolve () calls to use the specified relationship type.

For example, when AUTOFAC injects a constructor parameter of type ienumerable<itask>, it will not look for components that provide IEnumerable <ITask>. Instead, the container will find all implementations of ITask and inject all implementations.

(Don't worry--The following example shows the various types of usage and what they mean.) )

Note: To override this default behavior, you can still register an explicit implementation of these types. types of relationships supported

The following table summarizes each of the types of relationships that are supported in AUTOFAC, and shows the types that can be used to use these relationships. NET type. Each relationship type has a more detailed description and use case.

Relationship Type meaning
A needs B B Direct Dependency
A needs B at some point in the future Lazy<b> Delayed instantiation
A needs B until some point in the future Owned<b> Controlled Lifetime
A needs to create instances of B Func<b> Dynamic instantiation
A provides parameters of types X and Y to B Func<x,y,b> Parameterized instantiation
A needs all the kinds of B Ienumerable<b>, Ilist<b>, icollection<b> Enumeration
A needs to know X about B Meta<b> and Meta<b,x> Metadata interrogation
A needs to choose B based on X Iindex<x,b> Keyed Service Lookup
Relationship Type Details
    Direct Dependency (B)
    delayed instantiation (lazy<b>)
    controlled Lifetime (owned<b>)
    Dynamic Instantiation (func<b>)
    parameterized instantiation (func<x, Y, b>)
    enumeration (IEnumerable< B>, Ilist<b>, icollection<b>)
    Metadata Interrogation (meta<b>, Meta<b, x>)
    Keyed Service Lookup (iindex<x, b>)
Direct Reliance (B)

Direct dependencies are the most basic relationships supported-component a requires service B. This is handled automatically through standard constructors and property injection:

public Class A
{public
  A (B dependency) {...}}
}

Register A and B components, and then resolve:

var builder = new Containerbuilder ();
Builder. Registertype<a> ();
Builder. Registertype<b> ();
var container = Builder. Build ();

using (var scope = container. Beginlifetimescope ())
{
  //b is automatically injected into a.
  var a = scope. Resolve<a> ();
}
deferred instantiation (lazy<b>)

Lazy dependencies are not instantiated until the first time they are used. This is now dependent on infrequently used or expensive to build places. To take advantage of this, use lazy <B> in the constructor of a

public class A
{
  lazy<b> _b;

  Public A (lazy<b> B) {_b = b} public

  void M ()
  {
      //implementation B's component is the
      _b.value.dosomething () that was created the first time the ' M () ' was invoked;
  }
}

If you have a lazy dependency, you also need meta data, you can use lazy<b,m> instead of longer meta<lazy<b>, m>. Controlled life cycle (owned<b>)

All dependencies of the owner are no longer needed. The dependencies that are owned typically correspond to a unit of work that the dependent component performs.

The type of this relationship is particularly interesting when using components that implement IDisposable. AUTOFAC will automatically process a one-time component at the end of the lifecycle, but this may mean that the component has been shelved for too long; Or you might just want to control what you're doing with the object. In this case, you will use the owning dependencies.

public class A
{
  owned<b> _b;

  Public A (owned<b> b) {_b = b;}

  public void M ()
  { 
      //_b is used for a task
      _b.value.dosomething ();

      Here _b is no longer needed, so it is released
      _b.dispose ();
  }

Internally, AUTOFAC creates a small lifecycle range in which the B service is parsed, and the lifecycle scope is freed when you call Dispose (). This means that releasing B will also handle its dependencies, unless these dependencies are shared (for example, a single case pattern).

This also means that if you have Instanceperlifetimescope () registration and you parse it into OWNED<B>, then you may not get the same instance that you used elsewhere in the same lifecycle. This example shows the problem:

var builder = new Containerbuilder ();
Builder. Registertype<a> (). Instanceperlifetimescope ();
Builder. Registertype<b> (). Instanceperlifetimescope ();
var container = Builder. Build ();

using (var scope = container. Beginlifetimescope ())
{
  //Here we parse a instanceperlifetimescope () b;
  var B1 = scope. Resolve<b> ();
  B1. DoSomething ();

  This will be the same as the above B1
  var b2 = scope. Resolve<b> ();
  B2. DoSomething ();

  The b used in a is not the same as other B.
  var a = scope. Resolve<a> ();
  A.M ();
}

This is the reason for the design, because you don't want a component to release B from anything else. However, if you do not know, it can cause some confusion.

If you prefer to control B at your own discretion, register B as externallyowned (). Dynamic Instantiation (func<b>)

Using an automatically generated factory allows you to effectively invoke resolve<t> () without having to bind the component to AUTOFAC. Use this relationship type if you need to create multiple instances of a given service, or if you are unsure whether a service is required and you want to make a decision at run time. This relationship is also useful in the case of WCF integration, and you need to create a new service agent after an error occurs.

The lifecycle scope is the value of using this type of relationship. If you register an object as Instanceperdependency () and call Func<b> multiple times, you will get a new instance each time. However, if you register an object as SingleInstance () and call func<b> to resolve the object more than once, you get the same object instance each time.

Examples of this relationship are as follows:

public class A
{
  func<b> _b;

  Public A (func<b> b) {_b = b;}

  public void M ()
  {
      var b = _b ();
      B.dosomething ();
  }
parameter instantiation (func<x, Y, b>)

You can also use the Auto-generated factory to pass strongly typed parameters to the parse function. This is an alternate way to pass parameters during registration or during manual resolution:

public class A
{
    func<int, string, b> _b;

    Public A (Func<int, String, b> B) {_b = b} public

    void M ()
    {
        var B = _b (n, "Http://hel.owr.ld"); 
  b.dosomething ();
    }

Internally, AUTOFAC treats these as parameters that you type. This means that the automatically generated function factory cannot have duplicate types in the input parameter list. For example, suppose you have such a type:

public class Duplicatetypes
{public
  duplicatetypes (int A, int b, string c)
  {
    //...
  }
}

You may need to register the type and automatically generate a function factory for it. You will be able to work around this function, but you will be unable to execute it.

var func = scope. Resolve<func<int, int, string, duplicatetypes>> ();

Throws a dependencyresolutionexception
var obj = func (1, 2, "three");

In a loosely coupled scenario where the parameters match on a type, you should not really understand the parameter order of a particular object constructor. If you need to do this, you should use a custom delegate type:

Public delegate duplicatetypes factorydelegate (int A, int b, string c);

Then use Registergeneratedfactory () to register the delegate:

Builder. Registertype<duplicatetypes> ();
Builder. Registergeneratedfactory<factorydelegate> (New Typedservice (typeof (Duplicatetypes)));

Now this function will work:

var func = scope. Resolve<factorydelegate> ();
var obj = func (1, 2, "three");

You have another option to use a commissioned factory that you can read in the Advanced Topics section.

If you decide to use the built-in auto-generated factory behavior (func<x, Y, b>) and resolve the factory using only one of each type, then it will work, but you will get the same type of all constructor parameters as input.

var func = container. Resolve<func<int, String, duplicatetypes>> ();

This is the same call
//New Duplicatetypes (1, 1, "three")
var obj = func (1, "three");

You can read more about the delegate factory and the Registergeneratedfactory () method in the Advanced Topics section.

The lifecycle scope is valued when you use this relationship type and when you use a delegate factory. If you register an object as Instanceperdependency () and call Func<x, Y, b> multiple times, you will get a new instance each time. However, if you register an object as SingleInstance () and call Func<x, Y, and b> to parse the object multiple times, the same object instance will be given each time, regardless of the different parameters you pass in. Different parameters do not break the reuse of the lifecycle scope. enumeration (IENUMERABLE<B>, Ilist<b>, icollection<b>)

The dependencies of an enumerable type provide multiple implementations of the same service (interface). This is useful for message handlers (messages are passed in and registered with multiple handlers to process messages).

Let's say you have a dependency interface defined like this:

Public interface Imessagehandler
{
  void handlemessage (message m);
}

In addition, you have a consumer dependency relationship, such as you need to have multiple registrations and consumers need all registered dependencies:

public class Messageprocessor
{
  private ienumerable<imessagehandler> _handlers;

  Public Messageprocessor (ienumerable<imessagehandler> handlers)
  {
    this._handlers = handlers;
  }

  public void ProcessMessage (message m)
  {
    foreach (var handler in this._handlers)
    {
      handler. Handlemessage (M);}}

You can easily do this with an implicitly enumerable relationship type. As long as you register all the dependencies and consumers, when you parse the consumer, all the matching dependencies will be parsed into an enumeration.

var builder = new Containerbuilder ();
Builder. Registertype<firsthandler> (). As<imessagehandler> ();
Builder. Registertype<secondhandler> (). As<imessagehandler> ();
Builder. Registertype<thirdhandler> (). As<imessagehandler> ();
Builder. Registertype<messageprocessor> ();
var container = Builder. Build ();

using (var scope = container. Beginlifetimescope ())
{

  //processor is resolved, it passes all registered handlers to the constructor.
  var processor = scope. Resolve<messageprocessor> ();
  Processor. ProcessMessage (m);
}

Enumeration support Returns an empty set if no matching items are registered in the container. In other words, using the example above, if you do not register any Imessagehandler implementations, this will break:

This throws an exception-there is no registration.
scope. Resolve<imessagehandler> ();

However, this can be worked on:

This will return an empty list, not an exception:
scope. Resolve<ienumerable<imessagehandler>> ();

If you use this relationship to inject something, this may create a "gotcha" and you may think you will get a null value. Instead, you'll get an empty list. meta-data inquiries (META<B>, Meta<b, x>)

The AUTOFAC metadata feature allows you to associate arbitrary data with services that can be used to troubleshoot problems. If you want to make these decisions in a consumer component, use the meta<b> relationship, which will provide you with a string/object dictionary of all object metadata:

public class A
{
  meta<b> _b;

  Public A (meta<b> b) {_b = b;}

  public void M ()
  {
    if (_b.metadata["somevalue"] = = "Yes")
    {
      _b.value.dosomething ()
    ;
}}}

You can also use strongly typed metadata by specifying the metadata type in Meta<b, x> relationships:

public class A
{
  meta<b, bmetadata> _b;

  Public A (Meta<b, bmetadata> b) {_b = b;}

  public void M ()
  {
    if (_b.metadata.somevalue = = "Yes")
    {
      _b.value.dosomething ()
    ;
  }
}}

If you have a deferred dependency, you also need meta data, you can use lazy<b,m> instead of longer meta<lazy<b>, m>. with key service lookup (iindex<x, b>)

If you have a number of specific projects (such as ienumerable<b> relationships), but you want to select an item based on a service item, you can use Iindex<x to b> relationships. First, use the key to register your service:

var builder = new Containerbuilder ();
Builder. Registertype<derivedb> (). Keyed<b> ("a");
Builder. Registertype<anotherderivedb> (). Keyed<b> ("second");
Builder. Registertype<a> ();
var container = Builder. Build ();

Then use Iindex<x, b> to get a dictionary with Key services:

public class A
{
  iindex<string, b> _b;

  Public A (iindex<string, b> b) {_b = b;}

  public void M ()
  {
    var b = this._b["a"];
    B.dosomething ();
  }
Combining relationship Types

Relationship types can be grouped, so:

Ienumerable<func<owned<itask>>>

Correctly interpreted as:

    All implementations,
    factories, return
    lifecycle control
    itask Services
relationship types and container independence

Based on standards. NET type AUTOFAC does not force you to bind the application more tightly to AUTOFAC. They provide you with a container-configured programming model that is consistent with the way you write other components (without having to know many specific container extension points and potentially centrally configured APIs).

For example, you can still create a custom itaskfactory in your core model, but if you want, you can provide a Autofactaskfactory implementation based on func<owned<itask>>.

Note that some relationships are based on types in AUTOFAC (for example, Iindex<x, b>). Even if you choose to use a different IOC container to implement a service decision, using these relationship types will also give you at least a reference to AUTOFAC.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.