My. IOC sample code -- avoid circular dependency

Source: Internet
Author: User

The purpose of this article is to use some examples to explain the types of dependencies supported by my. IOC. That is to say, designing objects does not result in circular dependency.

In the IOC world, circular dependency is a stubborn enemy. This is not only because it will cause the IOC container to throw an exception, but also because it is unpredictable, although careful configuration can avoid this problem as much as possible.

When users register an object in the IOC container, they do not know the dependency between the object and other objects in advance, because the dependency is managed by the IOC container. This dependency can be determined only when the user calls container. Resolve (contracttype) for the first time. In addition, it may change with the registration/cancellation of the dependent object.

Therefore, to avoid circular dependency, it is important to carefully consider the dependency between the object and other objects when designing the object. Of course, the object design is out of the IOC container, and we have no way to constrain it. However, even if you do not use the IOC container, Object design is a big problem that deserves your consideration. Once you choose to use IOC, it also means that your object design needs to be more focused on IOC.

In fact, to avoid circular dependency in my. IOC, you only need to follow one principle, that is [Do not introduce circular dependencies during object construction.]. The following is an example of code.

using System;using System.Diagnostics;using My.Ioc;using My.Ioc.Exceptions;namespace HowToAvoidCyclicDependency{    #region Test clazz    #region Case 3    public class Grade3    {        private readonly Room3 _room;        public Grade3(Room3 room)        {            _room = room;        }        public Room3 Room        {            get { return _room; }        }    }    public class Room3    {        public Grade3 Grade { get; set; }    }    #endregion    #region Case 2    public class Grade2    {        private readonly Room2 _room;        public Grade2(Room2 room)        {            _room = room;            room.Grade = this;        }        public Room2 Room        {            get { return _room; }        }    }    public class Room2    {        public Grade2 Grade { get; set; }    }    #endregion    #region Case 1    public class Grade1    {        public Room1 Room { get; set; }    }    public class Room1    {        public Grade1 Grade { get; set; }    }    #endregion    #endregion    class Program    {        static void Main(string[] args)        {            var container = new ObjectContainer(false);            Register(container);            Resolve(container);            Console.WriteLine("HowToAvoidCyclicDependency success...");            Console.ReadLine();        }        static void Register(IObjectContainer container)        {            container.Register<Grade3>()                .In(Lifetime.Transient());            container.Register<Room3>()                .WithPropertyAutowired("Grade")                .In(Lifetime.Transient());            container.Register<Grade2>()                .In(Lifetime.Transient());            container.Register<Room2>()                .In(Lifetime.Transient());            container.Register<Grade1>()                .WithPropertyAutowired("Room")                .In(Lifetime.Transient());            container.Register<Room1>()                .WithPropertyAutowired("Grade")                .In(Lifetime.Transient());            container.CommitRegistrations();        }        static void Resolve(IObjectContainer container)        {            var grade1 = container.Resolve<Grade1>();            Debug.Assert(grade1 == grade1.Room.Grade);            var room1 = container.Resolve<Room2>();            Debug.Assert(room1 == room1.Grade.Room);            // No cyclic dependency in constructor            var grade2 = container.Resolve<Grade2>();            Debug.Assert(grade2 == grade2.Room.Grade);            try            {                // Cyclic dependency in constructor                // The call path is: Grade => Room => Grade                var grade3 = container.Resolve<Grade3>();            }            catch (Exception ex)            {                Debug.Assert(ex is CyclicDependencyException);            }        }    }}
View code

In the sample code, we designed three pairs of grade/room classes to demonstrate three different situations.

Let's take a look at grade1/room1.

public class Grade1{    public Room1 Room { get; set; }}public class Room1{    public Grade1 Grade { get; set; }}

We can see that the attributes of these two classes reference each other, but they do not define constructors. Here, we take grade1 as an example to analyze their construction process.

When we need a grade1 object, the container first calls its constructor to create a grade1 object for us. After the grade1 object is created, we can see that it needs to inject a room1 type attribute. In this way, the container creates a room1 object for us. Then, we can see that room1 also needs to inject a grade1 type attribute. At this point, because a grade1 object has been created before, the container will reuse this grade1 object here (this is critical) to complete the creation of the room1 object. After the room1 object is created, the container injects the object into the room attribute of grade1, so that grade1 is created. Therefore, loop dependency is not caused here, So running the following code will not cause exceptions:

var grade1 = container.Resolve<Grade1>();Debug.Assert(grade1 == grade1.Room.Grade);var room1 = container.Resolve<Room2>();Debug.Assert(room1 == room1.Grade.Room);

Next, let's look at grade2/room2.

public class Grade2{    private readonly Room2 _room;    public Grade2(Room2 room)    {        _room = room;        room.Grade = this;    }    public Room2 Room    {        get { return _room; }    }}public class Room2{    public Grade2 Grade { get; set; }}

When we construct the grade2 object, it does not cause circular dependency. As we can see, although grade2 requires room2 In the constructor, grade2 is not required in the process of constructing the room2 (because room2 does not define the constructor, it also does not need to inject the grade2 attribute in its own construction process), so there is no circular dependency problem.

Finally, let's look at grade3/room3.

public class Grade3{    private readonly Room3 _room;    public Grade3(Room3 room)    {        _room = room;    }    public Room3 Room    {        get { return _room; }    }}public class Room3{    public Grade3 Grade { get; set; }}

We can see that grade3 requires a room3 type parameter in the constructor. Although the room3 type does not define the constructor, it requires injecting a grade3 type attribute value during the constructor process. This causes the problem. Because when grade3 requires a room3 object, the room3 object has not been created, so the container needs to create a room3 object first. However, when creating a room3 object, the container needs to inject a grade3 object. However, the initial grade3 has not been created yet (its constructor has been called but has not been completed) and cannot be reused here. Therefore, the container needs to create a new grade3 object to meet the construction needs of room3. Later, this new grade3 requires a new room3, and later this new room3 requires a new grade3... in this way, the construction process will never be completed. If you use a concise expression, the dependency path of the above creation process is as follows: grade3 => room3 => grade3 => room3 => grade3 ....

In this way, we understand that as long as we design the class, we should avoid generating the dependency path described above, which can effectively prevent the issue of circular dependency. In fact, that is, follow the above principle :[Do not introduce circular dependencies during object construction.].

 

The sample code in this article and the source code of my. IOC framework can be obtained here.

My. IOC sample code -- avoid circular dependency

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.