๐Ÿ˜Š๋‚˜๋งŒ์˜ MVVM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ œ์ž‘ ๊ณผ์ •. ConvMVVM #1 - IoCContainer

๋‚˜๋งŒ์˜ MVVM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋งŒ๋“ค๊ธฐ #1 IOC Container

MVVM Toolkit๊ณผ Prism์˜ ์žฅ์ ์„ ์„ž๊ธฐ ์œ„ํ•œ ๊ณ ๋‚œ๊ณผ ์—ญ๊ฒฝ ใ… 

ConvMVVM ์†Œ์Šค์ฝ”๋“œ

๋ธ”๋กœ๊ทธ ๊ธ€ ๋งํฌ

๋™๊ธฐ

์ €๋Š” ์‚ฌ์‹ค C#๊ฐœ๋ฐœ ๊ฒฝ๋ ฅ์ด ์งง์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜๋„ ์š”์ฆ˜ ์žฌ๋ฏธ๊ฐ€ ๋“ค๋ ค์„œ ์ด๊ฒƒ์ €๊ฒƒ ๋ณด๊ณ ์žˆ๋Š”๋ฐ ๋ฌธ๋“ ๋“œ๋Š” ์ƒ๊ฐ์ดโ€ฆ ์ด๋Ÿฐ๊ฒƒ๋“ค์„ ์“ฐ๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๊ธฐ๋Šฅ๋“ค์„ ๋‚ด๊ฐ€ ๊ตฌํ˜„์„ ๋ชปํ•˜๋ฉด ๋‚ด๊ฒƒ์ด ์•„๋‹ˆ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค๋”๊ตฐ์š”
๋Šฆ์—ˆ์ง€๋งŒ ์กฐ๊ธˆ์”ฉ ๋‚˜๋งŒ์˜ MVVM๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ ๊ธฐํšŒ๊ฐ€ ๋œ๋‹ค๋ฉด ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š” ์—†๋Š” ์„œ๋น„์Šค๋‚˜ ๊ฐ์ดˆ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๋„ฃ์–ด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ๋ณ€ ์ง€์ธ๋ถ„๋“ค ๋ง์„ ๋“ค์–ด๋ณด๋ฉด IOC์ปจํ…Œ์ด๋„ˆ ์ข…๋ฅ˜๋„ ๋‹ค์–‘ํ•˜๋‹ค๊ณ  ๋“ค์—ˆ๋Š”๋ฐ ๋งŽ์€ ๊ฒƒ๋“ค์„ ์ ‘ํ•˜์ง„ ๋ชปํ–ˆ๊ณ โ€ฆ ์ œ๊ฐ€ ๊ฐœ๋ฐœํ• ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋Š” IOC ์ปจํ…Œ์ด๋„ˆ๋Š” Microsoft Extensions DependencyInjection ์ž…๋‹ˆ๋‹ค
์ œ๊ฐ€ ๊ตฌํ˜„ํ•˜๊ณ ์žํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ IOC์˜ ๊ธฐ๋Šฅ์€ Microsoft Extensions DependencyInjection๊ณผ ๋‹ฎ์•„์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ธฐ๋Šฅ

  1. Cache๊ฐ์ฒด์™€ NoneCache๊ฐ์ฒด ๋“ฑ๋ก (Singleton, Transient)
  2. Interface์™€ ๊ฐ™์ด ๊ฐ์ฒด๋ฅผ ๋“ฑ๋ก
  3. ์ƒ์„ฑ์ž ์ฃผ์ž…
  4. ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ ๋“ฑ๋ก
  5. ๋žŒ๋‹ค๋ฅผ ์ด์šฉํ•œ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฃจํ‹ด ๋“ฑ๋ก

์ฐธ๊ณ ์ž๋ฃŒ

Carlos Blanco๋‹˜์˜ ํฌ์ŠคํŠธ๋ฅผ ์ฐธ๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ณ ๋ คํ•˜์ง€ ์•Š์€ ์‚ฌํ•ญ ๋ฐ ํ•œ๊ณ„

  1. ์ •๋Ÿ‰์ ์ธ ์ˆ˜์น˜๋ฅผ ๊ฐ€์ง€๊ณ  ๋‹ค๋ฅธ ์œ ๋ช…ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๊ณผ ์„ฑ๋Šฅ๋“ฑ์„ ๋น„๊ตํ•˜์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹คโ€ฆ ๊ฐํžˆโ€ฆ์ œ๊ฐ€โ€ฆ
  2. ์ฒ˜์Œ์—๋„ ์–ธ๊ธ‰๋“œ๋ ธ๋‹ค์‹œํ”ผ ์ดˆ๋ณด๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ์ง€์ €๋ถ„ํ•ฉ๋‹ˆ๋‹ค
  3. ๋ฒ„๊ทธ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ ๊ณผ์ •

1. ์„œ๋น„์Šค ๋“ฑ๋ก์„ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค

namespace ConvMVVM.Core.IOC
{
    public interface IServiceCollection
    {
        public void RegisterCache<TInterface, TImplementation>() where TImplementation : TInterface;
				//์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ตฌ์ƒ์ฒด ํด๋ž˜์Šค๋ฅผ ๊ฐ™์ด ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜

        public void RegisterNoneCache<TInterface, TImplementation>() where TImplementation : TInterface;

        public void RegisterCache<TImplementation>() where TImplementation : class;
				//๊ตฌ์ƒ์ฒด ํด๋ž˜์Šค๋งŒ์œผ๋กœ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜

        public void RegisterCache<TImplementation>(TImplementation implementation) where TImplementation : class;
				//๋ฏธ๋ฆฌ ์ƒ์„ฑํ•œ ํด๋ž˜์Šค๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜

        public void RegisterNoneCache<TImplementation>() where TImplementation : class;

        public void RegisterCache<TInterface, TImplementation>(TImplementation implementation) where TImplementation : TInterface;

        public void RegisterCache<TInterface, TImplementation>(Func<IContainer, TInterface> factory) where TImplementation : TInterface;
				//๋žŒ๋‹ค๋ฅผ ์ด์šฉํ•œ ์„œ๋น„์Šค ์ƒ์„ฑ ๋ฃจํ‹ด ๋“ฑ๋ก์„ ์œ„ํ•œ ํ•จ์ˆ˜

        public void RegisterNoneCache<TInterface, TImplementation>(Func<IContainer, TInterface> factory) where TImplementation : TInterface;

        public void RegisterCache<TImplementation>(Func<IContainer, TImplementation> factory) where TImplementation : class;
        public void RegisterNoneCache<TImplementation>(Func<IContainer, TImplementation> factory) where TImplementation : class;


        public bool CheckType(Type type);
				//Container์—์„œ ํƒ€์ž… ์กด์žฌ ์œ ๋ฌด ํŒ๋ณ„์šฉ ํ•จ์ˆ˜


        public Tuple<Type, bool, object, object> GetType(Type type);
				//ServiceCollection์— ํƒ€์ž…์„ ํ‚ค๋กœ ๋“ฑ๋ก๋œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜ 

        public IContainer CreateContainer();
				//์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ์„ ์œ„ํ•œ ํ•จ์ˆ˜ 
    }
}
public void RegisterCache<TInterface, TImplementation>() where TImplementation : TInterface;

public void RegisterCache<TImplementation>() where TImplementation : class;

๊ฐœ์ธ์ ์œผ๋กœ ๋งŒ๋“ค๋ฉด์„œ ํŠนํžˆ๋‚˜ ํฅ๋ฏธ๋กœ์› ๋˜ ๋ถ€๋ถ„์€ ์ด ๋ถ€๋ถ„์ธ๋ฐ. generic TImplementation์ด TInterface์˜ ์ƒ์†์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์ œํ•œ์„ ๊ฑธ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
์Œโ€ฆโ€ฆ. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ์™€ ๋™์‹œ์— TInterface๊ฐ€ interface๋ฉด์„œ TImplementation์ด class์—ฌ์•ผํ•จ์„ ์ œํ•œํ•˜๋Š” ๊ฒƒ์€ ์•ˆ๋˜๋Š” ๊ฒƒ์ธ๊ฐ€?โ€ฆ
๊ณต๋ถ€๋ฅผ ๋œํ•œ ๊ฒƒ์ธ์ง€ ์›๋ž˜ ์•ˆ๋˜๋Š” ๊ฒƒ์ธ์ง€โ€ฆ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

2. ์„œ๋น„์Šค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค

namespace ConvMVVM.Core.IOC
{
    public interface IContainer
    {
        public TInterface GetService<TInterface>() where TInterface: class;
    }
}

๋ถ์–ด๊ตญ ๋งˆ๋ƒฅ ๋‹ด๋ฐฑํ•˜๊ฒŒ ํ•˜๋‚˜์˜ ํ•จ์ˆ˜๋งŒ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค. :expressionless:

3. ServiceCollection ๊ตฌํ˜„

namespace ConvMVVM.Core.IOC
{
    public class ServiceCollection : IServiceCollection
    {
        #region Private Property
        private readonly Dictionary<Type, Tuple<Type, bool, object, object>> Types = new Dictionary<Type, Tuple<Type, bool, object, object>>();
        #endregion

        #region Constructor
        internal ServiceCollection()
        {

        }
        #endregion

        #region Static Functions
        public static IServiceCollection Create()
        {
            return new ServiceCollection();
        }
        #endregion

        #region Private Functions
        private Tuple<Type, bool, object, object> CreateTypeInfo(Type type, bool cache, object cacheObject, object callback )
        {
            return new Tuple<Type, bool, object, object>(type, cache, cacheObject, callback);
        }
        #endregion

        #region Public Functions
        public void RegisterCache<TInterface, TImplementation>() where TImplementation : TInterface
        {
            this.Types[typeof(TInterface)] = this.CreateTypeInfo(typeof(TImplementation), true, null, null);
        }

        public void RegisterCache<TImplementation>() where TImplementation : class
        {
            this.Types[typeof(TImplementation)] = this.CreateTypeInfo(typeof(TImplementation), true, null, null);
        }

        public void RegisterCache<TImplementation>(TImplementation implementation) where TImplementation : class
        {
            this.Types[typeof(TImplementation)] = this.CreateTypeInfo(typeof(TImplementation), true, implementation, null);
        }

        public void RegisterNoneCache<TInterface, TImplementation>() where TImplementation : TInterface
        {
            this.Types[typeof(TInterface)] = this.CreateTypeInfo(typeof(TImplementation), false, null, null);
        }

        public void RegisterNoneCache<TImplementation>() where TImplementation : class
        {
            this.Types[typeof(TImplementation)] = this.CreateTypeInfo(typeof(TImplementation), false, null, null);
        }

        public void RegisterCache<TInterface, TImplementation>(TImplementation implementation) where TImplementation : TInterface
        {
            this.Types[typeof(TInterface)] = this.CreateTypeInfo(typeof(TImplementation), true, implementation, null);
        }

        public void RegisterCache<TInterface, TImplementation>(Func<IContainer, TInterface> factory) where TImplementation : TInterface
        {
            this.Types[typeof(TInterface)] = this.CreateTypeInfo(typeof(TImplementation), true, null, factory);
        }

        public void RegisterNoneCache<TInterface, TImplementation>(Func<IContainer, TInterface> factory) where TImplementation : TInterface
        {
            this.Types[typeof(TInterface)] = this.CreateTypeInfo(typeof(TImplementation), false, null, factory);
        }

        public void RegisterCache<TImplementation>(Func<IContainer, TImplementation> factory) where TImplementation : class
        {
            this.Types[typeof(TImplementation)] = this.CreateTypeInfo(typeof(TImplementation), true, null, factory);
        }
        public void RegisterNoneCache<TImplementation>(Func<IContainer, TImplementation> factory) where TImplementation : class
        {
            this.Types[typeof(TImplementation)] = this.CreateTypeInfo(typeof(TImplementation), false, null, factory);
        }

        public bool CheckType(Type type)
        {
            if (this.Types.ContainsKey(type)) return true;
            else return false;
        }

        public IContainer CreateContainer()
        {
            return new Container(this);
        }

        public Tuple<Type, bool, object, object> GetType(Type type)
        {
            if (!CheckType(type))
            {
                throw new InvalidOperationException("Invalid key type");
            }

            return this.Types[type];
        }


        #endregion
    }
}
#region Private Property
												//ํƒ€์ž…ํ‚ค,  ํŠœํ”Œ(๊ตฌ์ƒ์ฒด ํƒ€์ž…, cache์œ ๋ฌด, cache์˜ค๋ธŒ์ ํŠธ, ์ƒ์„ฑ๋ฃจํ‹ด ์ฃผ์ž…์„ ์œ„ํ•œ ๋žŒ๋‹ค ์ฝœ๋ฐฑ)
private readonly Dictionary<Type, Tuple<Type, bool, object, object>> Types = new Dictionary<Type, Tuple<Type, bool, object, object>>();
#endregion

Dictionary๋กœ ํƒ€์ž…ํ‚ค๋ฅผ ํ‚ค๋กœ ํŠœํ”Œ๋กœ ์ƒ์„ฑ์— ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๋ฌถ์–ด์„œ ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก Types๋ผ๋Š” Dictionary๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

#region Private Functions
																												//๊ฐ์ฒด ํƒ€์ž…, cache ์œ ๋ฌด, cache ์˜ค๋ธŒ์ ํŠธ, ์ƒ์„ฑ๋ฃจํ‹ด lambda
private Tuple<Type, bool, object, object> CreateTypeInfo(Type type, bool cache, object cacheObject, object callback )
{
    return new Tuple<Type, bool, object, object>(type, cache, cacheObject, callback);
}
#endregion

๊ฐ ์ธํ„ฐํŽ˜์ด์Šค ํ•จ์ˆ˜์— ๋งž๊ฒŒ ํŠœ๋ธ” ์ •๋ณด๋ฅผ ๋งŒ๋“ค์–ด์„œ Dictionary์— ๋“ฑ๋ก์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ Container์—์„œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ• ๊ฒƒ์ž…๋‹ˆ๋‹ค.

//๋‚ด๊ฐ€ ๋งŒ๋“ค ํƒ€์ž…์ด ์žˆ๋Š”์ง€ ํ™•์ธ
public bool CheckType(Type type)
{
    if (this.Types.ContainsKey(type)) return true;
    else return false;
}

public IContainer CreateContainer()
{
    return new Container(this);
}

//ํƒ€์ž…์ด ์žˆ๋‹ค๋ฉด ๊ทธ ํƒ€์ž…์˜ ์ƒ์„ฑ์„ ์œ„ํ•ด ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ. 
public Tuple<Type, bool, object, object> GetType(Type type)
{
    if (!CheckType(type))
    {
        throw new InvalidOperationException("Invalid key type");
    }

    return this.Types[type];
}

๊ทธ ์™ธ ๊ธฐํƒ€ ๊ฐ์ฒด ์ƒ์„ฑ์„ ์œ„ํ•ด ํ•„์š”ํ•œ ํƒ€์ž… ์กด์žฌ ์œ ๋ฌด ์ฒดํฌ ํ•จ์ˆ˜์™€, ํƒ€์ž…์˜ ๋“ฑ๋ก์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜๋„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

4. Container ๊ตฌํ˜„

namespace ConvMVVM.Core.IOC
{
    public class Container : IContainer
    {
        #region Private Property
        private readonly IServiceCollection serviceCollection;
        private readonly Dictionary<Type, object> cacheObjects = new Dictionary<Type, object>();
        #endregion

        #region Constructor
        public Container(IServiceCollection services)
        {
            if(services == null)
            {
                throw new InvalidOperationException("Invalid service collection");
            }
            this.serviceCollection = services;
        }
        #endregion


        #region Private Functions
        public object Create(Type type)
        {
            if (!this.serviceCollection.CheckType(type))
            {
                throw new InvalidOperationException("There is no service key");
            }
            try
            {

                var typeInfo = this.serviceCollection.GetType(type);

                var objectType = typeInfo.Item1;
                var cacheOrNot = typeInfo.Item2;
                var cacheObject = typeInfo.Item3;
                var callback = typeInfo.Item4;
                var defaultConstructors = objectType.GetConstructors();
                if(defaultConstructors.Count() <= 0 && cacheObject == null)
                {
                    throw new InvalidOperationException("There is no constructor");
                }

                if (cacheOrNot == false && callback == null && cacheObject == null)
                {
                    var defaultConstructor = defaultConstructors[0];
                    var defaultParams = defaultConstructor.GetParameters();
                    var parameters = defaultParams.Select(param => Create(param.ParameterType)).ToArray();
                    var service = defaultConstructor.Invoke(parameters);
                    return service;
                }
                
                if(cacheOrNot == false && callback != null && cacheObject == null)
                {
                    var methodInfo = callback.GetType().GetMethod("Invoke");
                    var service = methodInfo.Invoke(callback, new[] { this });
                    return service;
                }

                if(cacheOrNot == true && callback == null && cacheObject == null)
                {
                    if (this.cacheObjects.ContainsKey(type) == true)
                        return this.cacheObjects[type];

                    var defaultConstructor = defaultConstructors[0];
                    var defaultParams = defaultConstructor.GetParameters();
                    var parameters = defaultParams.Select(param => Create(param.ParameterType)).ToArray();
                    var service = defaultConstructor.Invoke(parameters);
                    this.cacheObjects[type] = service;
                    return service;
                }

                if(cacheOrNot == true && callback != null && cacheObject == null)
                {
                    if (this.cacheObjects.ContainsKey(type) == true)
                        return this.cacheObjects[type];

                    var methodInfo = callback.GetType().GetMethod("Invoke");
                    var service = methodInfo.Invoke(callback, new[] { this });
                    this.cacheObjects[type] = service;
                    return service;
                }

                if (cacheOrNot == true && callback == null && cacheObject != null)
                {
                    if (this.cacheObjects.ContainsKey(type) == true)
                        return this.cacheObjects[type];

                    this.cacheObjects[type] = cacheObject;
                    return cacheObject;
                }

                throw new InvalidOperationException("Invalid service collection infomation");
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("Unknown", ex);
            }
        }
        #endregion

        #region Functions

        public TInterface GetService<TInterface>() where TInterface : class
        {
            try
            {
                return (TInterface)Create(typeof(TInterface));
            }catch(Exception ex)
            {
                throw new InvalidOperationException("Unknown", ex);
            }
        }

        #endregion
    }
}

๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ public object Create(Type type) ์ด ํ•จ์ˆ˜์ธ๋ฐ ๊ฐ ๊ฐ์ฒด ์ƒ์„ฑ์— ํ•„์š”ํ•œ ์ •๋ณด์— ๋งž์ถฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
์ด 5๊ฐ€์ง€์ •๋„์˜ ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ

  1. cache==false, cache์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์—†์œผ๋ฉฐ ์ƒ์„ฑ๋ฃจํ‹ด callback์ด ์—†๋‹ค๋ฉด โ†’ reflection์„ ์ด์šฉํ•˜์—ฌ NoneCache๋กœ ๊ฐ์ฒด ์ƒ์„ฑ
  2. cache==false, cache์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์—†์œผ๋ฉฐ ์ƒ์„ฑ๋ฃจํ‹ด callback์ด ์žˆ๋‹ค๋ฉด โ†’ reflection์„ ์ด์šฉํ•˜์—ฌ NoneCache๋ฅผ callback์œผ๋กœ ์ƒ์„ฑ
  3. cache==true, cache์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์—†์œผ๋ฉฐ ์ƒ์„ฑ๋ฃจํ‹ด callback์ด ์—†๋‹ค๋ฉด โ†’ reflection์„ ์ด์šฉํ•˜์—ฌ Cache๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  Cache๊ฐ์ฒด ํ…Œ์ด๋ธ”์— ๋“ฑ๋กํ•˜์—ฌ ์ˆ˜๋ช…๊ด€๋ฆฌ
  4. cache==true, cache์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์žˆ์œผ๋ฉฐ ์ƒ์„ฑ๋ฃจํ‹ด callback์ด ์—†๋‹ค๋ฉด โ†’ cache์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ Cache๊ฐ์ฒด ํ…Œ์ด๋ธ”์— ๋“ฑ๋กํ•˜์—ฌ ์ˆ˜๋ช…๊ด€๋ฆฌ
  5. cache==true, cache์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์—†์œผ๋ฉฐ ์ƒ์„ฑ๋ฃจํ‹ด callback์ด ์žˆ๋‹ค๋ฉด โ†’ reflection์„ ์ด์šฉํ•˜์—ฌ Cache๊ฐ์ฒด๋ฅผ callback์œผ๋กœ ์ƒ์„ฑ, Cache๊ฐ์ฒด ํ…Œ์ด๋ธ”์— ๋“ฑ๋กํ•˜์—ฌ ์ˆ˜๋ช…๊ด€๋ฆฌ

์œ„์™€ ๊ฐ™์€ ๋กœ์ง์œผ๋กœ ์ƒ์„ฑ๋˜๋ฉฐโ€ฆ ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์œ„ํ•ด์„œ Reflection๊ณผ Createํ•จ์ˆ˜๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ Linq์˜ Select์™€ ๊ฐ™์ด ์„ž์–ด์„œ >ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋ฉด ๊ฐ ์ƒ์„ฑ์ž์— ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž…ํ•œ ์ƒํƒœ๋กœ ์ƒ์„ฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

IOC์ปจํ…Œ์ด๋„ˆ ์‚ฌ์šฉ

๋งŒ๋“ค์–ด๋ดค์œผ๋‹ˆ ์ด์ œ๋Š” ์จ๋ด์•ผ ํ•ฉ๋‹ˆ๋‹คโ€ฆ
IOC์ปจํ…Œ์ด๋„ˆ๋ฅผ ์“ฐ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๊ฒ ์ง€๋งŒโ€ฆ ์ œ๊ฐ€ ๊ฐœ์ธ์ ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ๋Š๊ผˆ๋˜ ์“ฐ์ž„์ƒˆ๋ฅผ ์ •๋ฆฌํ•˜์—ฌ ์•„๋ž˜์™€ ๊ฐ™์€ ์ผ€์ด์Šค๋กœ ๋‚˜๋ˆ ๋ดค์Šต๋‹ˆ๋‹ค.

  1. ์‚ฌ๋ก€ #1์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ƒ์†๋œ, ์„œ๋น„์Šค๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž… ๊ฐ์ฒด
  2. ์‚ฌ๋ก€ #2 A Model์„ ํ’ˆ๊ณ  ์žˆ๋Š” A ViewModel ์ƒ์„ฑ ํ•˜์ง€๋งŒ A Model์€ Ioc๋“ฑ๋ก์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„๋•Œ
  3. ์‚ฌ๋ก€ #3 A Model์„ ํ’ˆ๊ณ  ์žˆ๋Š” A ViewModel ๋ฌถ์Œ์„ ์ƒ์„ฑ ํ•˜์ง€๋งŒ A Model์€ Ioc๋“ฑ๋ก์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„๋•Œ
  4. ์‚ฌ๋ก€ #4 A, B, C Model์„ Property ์ฃผ์ž…์„ ๋ฐ›์•„์„œ D ViewModel ์ƒ์„ฑ. ํ•˜์ง€๋งŒ A, B, C๋ชจ๋ธ์€ IoC๋“ฑ๋ก์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„๋•Œ

์‚ฌ๋ก€ #1

์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ƒ์†๋œ, ์„œ๋น„์Šค๋ฅผ ์ƒ์„ฑ์ž ์ฃผ์ž…ํ•œ ๊ฐ์ฒด

//TestModel 
namespace IOCContainerExample.Model
{
    public class TestModel : ITestModel
    {
        #region Private Property
        private readonly TestService testService;
        #endregion

        #region Constructor
        public TestModel(TestService _testService) { 
            this.testService = _testService;
        }
        #endregion

        #region Functions
        public string Test()
        {
            return this.testService.Test();
        }
        #endregion
    }
}

TestModel ํด๋ž˜์Šค ๊ตฌํ˜„๋ถ€

namespace IOCContainerExample.Service
{
    public class TestService
    {

        #region Consturctor
        public TestService() { }
        #endregion

        #region Public Property
        public string Test()
        {
            return "there is no cow level";
        }
        #endregion
    }
}

TestService ํด๋ž˜์Šค ๊ตฌํ˜„๋ถ€

var collection = ServiceCollection.Create();
collection.RegisterCache<TestService>();
collection.RegisterCache<ITestModel, TestModel>();

var container = collection.CreateContainer();
var tesetModel = container.GetService<ITestModel>();

System.Console.WriteLine(tesetModel.Test());

IoC์ปจํ…Œ์ด๋„ˆ ๋“ฑ๋ก ๋ฐ ๊ฐ์ฒด ์ƒ์„ฑ ์‚ฌ์šฉโ€ฆ ์ด์ •๋„๋Š” IoC ์“ฐ์‹œ๋Š” ๋ถ„์ด๋ผ๋ฉด ์ž˜ ์•Œ๊ณ  ๊ณ„์‹ค๊ฒ๋‹ˆ๋‹ค.

์‚ฌ๋ก€ #2

A Model์„ ํ’ˆ๊ณ  ์žˆ๋Š” A ViewModel ์ƒ์„ฑ ํ•˜์ง€๋งŒ A Model์€ IoC๋“ฑ๋ก์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„๋•Œ.

namespace IOCContainerExample.Model
{
    public class AModel
    {
        #region Constructor
        public AModel() { }
        #endregion
    }
}

AModel ํด๋ž˜์Šค ๊ตฌํ˜„๋ถ€

namespace IOCContainerExample.ViewModel
{
    public class AViewModel
    {
        #region Constructor
        public AViewModel()
        {

        }
        #endregion

        #region Public Property
        public AViewModel AModel { get; set; }
        #endregion
    }
}

AViewModel ํด๋ž˜์Šค ๊ตฌํ˜„๋ถ€

var collection = ServiceCollection.Create();
collection.RegisterCache<AViewModel>();
collection.RegisterNoneCache<Func<AModel, AViewModel>>((container)=>
{
    return (model) =>
    {
        var vm = container.GetService<AViewModel>();
        vm.AModel = model;
        return vm;
    };
});

var container = collection.CreateContainer();
var converter = container.GetService<Func<AModel, AViewModel>>();
var model = new AModel();
var viewModel = converter(model);

System.Console.WriteLine("test");

์ด ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•ด์„œ Microsoft Extensions DependencyInjection ์—์„œ ๋žŒ๋‹ค ์ƒ์„ฑ์„ ์ง€์›ํ•˜๋‚˜ ๋ด…๋‹ˆ๋‹คโ€ฆ

์‚ฌ๋ก€ #3

A Model์„ ํ’ˆ๊ณ  ์žˆ๋Š” A ViewModel ๋ฌถ์Œ์„ ์ƒ์„ฑ ํ•˜์ง€๋งŒ A Model์€ Ioc๋“ฑ๋ก์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„๋•Œ

var collection = ServiceCollection.Create();
collection.RegisterCache<AViewModel>();
collection.RegisterNoneCache<Func<AModel, AViewModel>>((container) =>
{
    return (model) =>
    {
        var vm = container.GetService<AViewModel>();
        vm.AModel = model;
        return vm;
    };
});
collection.RegisterNoneCache<Func<List<AModel>, List<AViewModel>>>((container) =>
{
    return (models) =>
    {
        var converter = container.GetService<Func<AModel, AViewModel>>();
        var vms = models.Select(model => converter(model)).ToList();
        return vms;
    };
});

var container = collection.CreateContainer();
var converter = container.GetService<Func<List<AModel>, List<AViewModel>>>();

var models = new List<AModel>()
{
    new AModel(),
    new AModel(),
    new AModel()
};
var viewModels = converter(models);

์‚ฌ๋ก€ #4

A, B, C Model์„ Property ์ฃผ์ž…์„ ๋ฐ›์•„์„œ D ViewModel ์ƒ์„ฑ. ํ•˜์ง€๋งŒ A, B, C๋ชจ๋ธ์€ IoC๋“ฑ๋ก์„ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์„๋•Œ

var collection = ServiceCollection.Create();
collection.RegisterCache<DViewModel>();
collection.RegisterNoneCache<Func<AModel, BModel, CModel, DViewModel>>((container) =>
{
    return (amodel, bmodel, cmodel) =>
    {
        var vm = container.GetService<DViewModel>();
        vm.AModel = amodel;
        vm.BModel = bmodel;
        vm.CModel = cmodel;
        return vm;
    };
});

var container = collection.CreateContainer();


var amodel = new AModel();
var bmodel = new BModel();
var cmodel = new CModel();

var converter = container.GetService<Func<AModel, BModel, CModel, DViewModel>>();
var viewModel = converter(amodel, bmodel, cmodel);

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ Property Injection์„ ์ง€์›ํ•˜์ง€์•Š๋”๋ผ๋„ ๋žŒ๋‹ค ์ฃผ์ž…์„ ํ†ตํ•ด์„œ ์–ด๋Š์ •๋„ ๊ทน๋ณต์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ์ถ”๊ฐ€ ํ˜น์€ ๊ฐœ์„ ๋˜์–ด์•ผ ๋  ์‚ฌํ•ญ

์œ„ ์˜ˆ์ œ์—์„œ๋„ ๋ณด๋‹ค์‹œํ”ผ Property Injection์„ ์ง€์›ํ•˜๋Š” Attribute๊ฐ€ ์žˆ์œผ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
๋‹ค๋ฅธ IoC ์ปจํ…Œ์ด๋„ˆ์—์„œ๋Š” ์ง€์›์„ ํ•˜๋Š”๊ฒƒ ๊ฐ™๋˜๋ฐโ€ฆ
Microsoft Extensions DependencyInjection ์—์„œ ์ง€์›์„ ์•ˆํ•˜๋Š” ์ด์œ ๋Š” ๋ชจ๋ฅด๊ฒ ์œผ๋‚˜ ์•„๋งˆ๋„ ๋žŒ๋‹ค๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ด ์•„๋‹๊ฐ€์š”?

12๊ฐœ์˜ ์ข‹์•„์š”

ํ›Œ๋ฅญํ•˜๋„ค์š”.
MSDI์— ๋Œ€ํ•œ ๊ถ๊ธˆ์ ์€ ๋‹ค์Œ์ฑ…์„ ๋ณด์‹œ๋ฉด ์ข‹์„ ๊ฑฐ ๊ฐ™์•„์š”.

6๊ฐœ์˜ ์ข‹์•„์š”

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ •๋…ํ•ด๋ด์•ผ๊ฒ ๋„ค์š” ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

5๊ฐœ์˜ ์ข‹์•„์š”