تزریق خودکار وابستگی‌ها در برنامه‌های ASP.NET MVC
هدف از این قسمت، ارائه راه حلی برای حالت تزریق وابستگی‌ها در سازنده‌های کنترلرهای ASP.NET MVC به صورت خودکار است.
به صورت پیش فرض، ASP.NET MVC به کنترلرهایی نیاز دارد که سازنده آن‌ها فاقد پارامتر باشند. از این جهت که بتواند به صورت خودکار آن‌ها را وهله سازی کرده و مورد استفاده قرار دهد. بنابراین به نظر می‌رسد که در اینجا نیز به همان روش معروف استفاده از الگوی Service locator و تکرار مدام کدهایی مانند ObjectFactory.GetInstance در سراسر برنامه خواهیم رسید که آنچنان مطلوب نیست.
اما ... در ASP.NET MVC می‌توان وهله ساز پیش فرض کنترلر‌ها را با پیاده سازی کلاس DefaultControllerFactory به طور کامل تعویض کرد. یعنی اگر در اینجا بجای وهله ساز پیش فرض، از وهله سازی انجام شده توسط IoC Container خود بتوانیم استفاده کنیم، آنگاه کار تزریق وابستگی‌ها در سازنده‌های کنترلرها نیز خودکار خواهد گردید.
    public class StructureMapControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
                throw new InvalidOperationException(string.Format("Page not found: {0}", requestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture)));
            return ObjectFactory.GetInstance(controllerType) as Controller;
        }
    }
در کدهای فوق نمونه‌ای از این پیاده سازی را با استفاده از امکانات StructureMap ملاحظه می‌کنید. به این ترتیب در زمان وهله سازی خودکار یک کنترلر، اینبار StructureMap وارد عمل شده و وابستگی‌های برنامه را مطابق تعاریف ObjectFactory.Initialize ذکر شده، به سازنده کلاس کنترلر تزریق می‌کند.
برای استفاده از این ControllerFactory جدید تنها کافی است بنویسیم:
   protected void Application_Start()
  {
     //Set current Controller factory as StructureMapControllerFactory  
     ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
  }
و ... همین!
اکنون نوشتن یک چنین کنترلرهایی که سازند‌ه آن‌ها دارای پارامتر است، مجاز خواهد بود و تزریق وابستگی‌ها در سازنده‌ها به صورت خودکار توسط IoC Container مورد استفاده انجام می‌شود.
public partial class LoginController : Controller
{
    readonly IUsersService _usersService;
    public LoginController(IUsersService usersService)
    {
       _usersService = usersService;
    }
بدیهی است سایر مسایل مانند تنظیمات اولیه IoC Container، تهیه لایه سرویس و غیره مانند قبل است و تفاوتی نمی‌کند.


روش دوم تزریق خودکار وابستگی‌ها در برنامه‌های ASP.NET MVC

روش پیاده سازی و تعویض DefaultControllerFactory پیش فرض، متداول‌ترین روش خودکار سازی تزریق وابستگی‌ها در ASP.NET MVC است. روش دیگری نیز بر اساس پیاده سازی اینترفیس توکار IDependencyResolver معرفی شده در ASP.NET MVC 3.0 به بعد، وجود دارد. این روش علاوه بر ASP.NET MVC در کنترلرهای مخصوص Web API نیز کاربرد دارد. حتی SignalR نیز دارای کلاس پایه‌ای به نام DefaultDependencyResolver با امضای مشابه IDependencyResolver است.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using StructureMap;

namespace Prog
{
    public class StructureMapDependencyResolver : IDependencyResolver
    {
        public object GetService(Type serviceType)
        {
            if (serviceType.IsAbstract || serviceType.IsInterface || !serviceType.IsClass)
                return ObjectFactory.TryGetInstance(serviceType);
            return ObjectFactory.GetInstance(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return ObjectFactory.GetAllInstances(serviceType).Cast<object>();
        }
    }
}
یک نمونه از پیاده سازی آن‌را به کمک StructureMap در اینجا ملاحظه می‌کنید. برای ثبت آن در برنامه خواهیم داشت:
protected void Application_Start()
{
   DependencyResolver.SetResolver(new StructureMapDependencyResolver());
}
در Web API باید GlobalConfiguration.Configuration.DependencyResolver تنظیم شود. البته IDependencyResolver آن در فضای نام دیگری به نام System.Web.Http.Dependencies قرار گرفته است؛ اما کلیات آن تفاوتی نمی‌کند. نمونه‌ی نهایی و تکمیل شده‌ی آن‌را در اینجا می‌توانید مطالعه کنید: «تزریق خودکار وابستگی‌ها در ASP.NET Web API به همراه رها سازی خودکار منابع IDisposable »   

دریافت مثال کامل بحث جاری:
DI06.zip
  • #
    ‫۱۱ سال و ۵ ماه قبل، یکشنبه ۸ اردیبهشت ۱۳۹۲، ساعت ۱۴:۴۳
     باسلام.
    هنگامی که آدرس http://localhost:2215/admin را در مرورگر وارد میکنم با خطای زیر روبرو می‌شوم.
    The IControllerFactory 'Server.Helpers.StructureMapControllerFactory' did not return a controller for the name 'admin'
    ساختار پروژه من هم بصورت زیر است:

    کد global.asax هم بصورت زیر است:

     public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                Database.SetInitializer<EshoppingContext>(null);
    
                #region Mvc
    
                AreaRegistration.RegisterAllAreas();
                WebApiConfig.Register(GlobalConfiguration.Configuration);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
                MvcHandler.DisableMvcResponseHeader = true;
    
                #endregion
    
                initStructureMap();
            }
            protected void Application_EndRequest(object sender, EventArgs e)
            {
                ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
            }
            static void initStructureMap()
            {
                ObjectFactory.Initialize(x =>
                {
                    x.For<IUnitOfWork>().HttpContextScoped().Use(() => new EshoppingContext());
                    x.For<IProductCategoryService>().Use<ProductCategoryService>();
                    x.For<IProductService>().Use<ProductService>();
                    x.For<IPersonService>().Use<PersonService>();
                    x.For<IPersonAttachmentService>().Use<PersonAttachmentService>();
                    x.For<IPostService>().Use<PostService>();
                    x.For<IPostCategoryService>().Use<PostCategoryService>();
                    x.For<ISliderItemService>().Use<SliderItemService>();
                });
                ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
            }
        }
    اطلاعات RouteConfig هم بصورت زیر است:

    public class RouteConfig
        {
            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    name: "Default",
                    url: "{controller}/{action}/{id}",
                    defaults: new
                    {
                        controller = MVC.Home.Name,
                        action = MVC.Home.ActionNames.Index,
                        id = UrlParameter.Optional
                    },
                    namespaces: new[] { "Server.Main.Controllers" }
                );
            }
        }
    و کد StructureMapControllerFactory بصورت زیر است:

    public class StructureMapControllerFactory : DefaultControllerFactory
        {
            protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
            {
                if (controllerType == null)
                {
                    return null;
                }
                return ObjectFactory.GetInstance(controllerType) as Controller;
            }
        }
    با تشکر.


    • #
      ‫۱۱ سال و ۵ ماه قبل، یکشنبه ۸ اردیبهشت ۱۳۹۲، ساعت ۱۶:۱۰
      - اگر مطلب را مطالعه کرده باشید، نحوه صحیح استفاده از Controller factory به این صورت است:
              if (controllerType == null)
                  throw new InvalidOperationException(string.Format("Page not found: {0}", requestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture)));
      به عمد این استثناء صادر می‌شود تا مشخص شود اگر controllerType نال است، حتما مسیری بوده که در سیستم وجود ندارد و این مورد برای خطایابی در دراز مدت حائز اهمیت است. به صورت خودکار هم توسط ELMAH لاگ می‌شود و برای بررسی‌های بعدی مورد استفاده خواهد بود.
      - اگر اینکار رو انجام بدید خواهید دید که مسیر ذکر شده توسط شما به صورت یافت نشد اعلام می‌گردد. چرا؟
      public override void RegisterArea(AreaRegistrationContext context)
              {
                  context.MapRoute(
                      "Admin_default",
                      "Admin/{controller}/{action}/{id}",
                      new { action = "Index", id = UrlParameter.Optional }
                  );
              }
      چون در مسیریابی پیش فرض یک Area نام کنترلر پیش فرض ذکر نشده (در فایل AdminAreaRegistration.cs). نام action هست اما نام controller نیست. به همین جهت یا باید این مسیریابی رو اصلاح کنید و یا اینکه مسیر کامل را به نحو زیر وارد نمائید:
      http://localhost:2215/admin/home
  • #
    ‫۱۱ سال و ۳ ماه قبل، دوشنبه ۲۴ تیر ۱۳۹۲، ساعت ۰۵:۴۲
    با سلام؛ من در استفاده از این روش در پروژه خودم به مشکل بر خوردم ممنون میشم راهنمایی بفرمایید.
    ساختار لایه بندی من به صورت زیر است.

    در پروژه وب یک کلاس ایجاد کردم و کد‌های زیر را در آن نوشتم

     public class StructureMapControllerFactory  : DefaultControllerFactory
        {
            protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
            {
                if (controllerType == null)
                    throw new InvalidOperationException(string.Format("Page not found: {0}", requestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture)));
                return ObjectFactory.GetInstance(controllerType) as Controller;
            }
        }
    و کدهای کنترلر من به صورت زیر است
     public class HomeController  : Controller
        {
            public ITABMPCREWService aa { get; set; }
    
            public ActionResult Index()
            {
    
                TABMPCREWS tt = new TABMPCREWS()
                {
                    DTLASTUPDATEDDATE = DateTime.Now,
                    INTOTRATE = 122,
                    INTRATE = 215,
                    VCCODEDESCRIPTION = "fff858699",
                    VCCODEVALUE = "fff858699",
                    VCLASTUSERID = "fff858699",
                    INTCREWCODE = 105652
                };
                aa.Add(tt);
    
    
                ViewBag.Message = "Welcome to ASP.NET MVC!";
    
                return View();
            }
    
            public ActionResult About()
            {
                ViewBag.Message = "Your app description page.";
    
                return View();
            }
    
    public ActionResult Contact()
            {
                ViewBag.Message = "Your contact page.";
    
                return View();
            }
        }
    و در فایل Global  در متد Application_Start() کد زیر را اضافه کردم
    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
    و در لایه Service کلاس TABMPCREWServiceبه صورت زیر است
     public class TABMPCREWService : ITABMPCREWService
        {
            private IUnitOfWork _uow;
            private IDbSet<TABMPCREW> _tabmpcrews;
            public TABMPCREWService(IUnitOfWork uow)
            {
                this._uow = uow;
                _tabmpcrews = uow.Set<TABMPCREW>();
            }
    
            public int Add(TABMPCREW personnel)
            {
                int rowEffect = 0;
    
                _tabmpcrews.Add(personnel);
                rowEffect = _uow.SaveChanges();
                return rowEffect;
            }
    
        }
    و زمانیکه پروژه را اجرا می‌کنم به این خطا بر می‌خورم

    همه چیز رو چک کردم ولی دلیلی برای این خطا پیدا نکردم . ممنون میشم راهنمایی کنید.
    • #
      ‫۱۱ سال و ۳ ماه قبل، دوشنبه ۲۴ تیر ۱۳۹۲، ساعت ۱۳:۱۲
      دو نوع تزریق وابستگی‌ها وجود دارد: الف) در سازنده کلاس ب) در خواص تعریف شده
      شما روش دوم را انتخاب کردید. نیازی به اینکار در MVC نیست و روش مرجح، روش الف است که نمونه‌ای از آن‌را در کلاس LoginController بحث فوق ملاحظه می‌کنید.
      اگر می‌خواهید تزریق وابستگی‌ها در خواص یک کلاس صورت گیرد، نیاز به یک سری تنظیمات اضافه‌تر وجود دارد که در بحث وب فرم‌ها مطرح شده (تنظیم SetAllProperties در متد initStructureMap آن).
      • #
        ‫۱۱ سال و ۳ ماه قبل، سه‌شنبه ۲۵ تیر ۱۳۹۲، ساعت ۰۴:۴۸
        من می‌خواهم از روش اول استفاده کنم برای همین  کنترلر خودم را به صورت زیر تغییر دادم
        public class HomeController  : Controller
            {
                public readonly ITABMPCREWService aa ;
                public HomeController(ITABMPCREWService tabmpcrewService)
                {
                    aa = tabmpcrewService;
                }
        
                public ActionResult Index()
                {
        
                    TABMPCREWS tt = new TABMPCREWS()
                    {
                        DTLASTUPDATEDDATE = DateTime.Now,
                        INTOTRATE = 122,
                        INTRATE = 215,
                        VCCODEDESCRIPTION = "fff858699",
                        VCCODEVALUE = "fff858699",
                        VCLASTUSERID = "fff858699",
                        INTCREWCODE = 105652
                    };
                    aa.Add(tt);
        
        
                    ViewBag.Message = "Welcome to ASP.NET MVC!";
        
                    return View();
                }
        
                public ActionResult About()
                {
                    ViewBag.Message = "Your app description page.";
        
                    return View();
                }
        
        public ActionResult Contact()
                {
                    ViewBag.Message = "Your contact page.";
        
                    return View();
                }
            }
        }
        ولی خطای زیر را دریافت می‌کنم
        StructureMap Exception Code:  202 No Default Instance defined for PluginFamily ServiceLayer.

        ممنون میشم من را راهنمایی کنید.
        • #
          ‫۱۱ سال و ۳ ماه قبل، سه‌شنبه ۲۵ تیر ۱۳۹۲، ساعت ۰۵:۰۵
          این خطا یعنی تنظیمات اولیه ناقصی دارید. مراجعه کنید به مطلب «استفاده از StructureMap به عنوان یک IoC Container» برای توضیحات بیشتر در مورد نحوه تعریف ObjectFactory.Initialize و ارتباط دادن اینترفیس‌ها به کلاس‌های متناظر.
          • #
            ‫۱۱ سال و ۳ ماه قبل، سه‌شنبه ۲۵ تیر ۱۳۹۲، ساعت ۰۵:۵۶
            من مطلب بالا را مطالعه کردم و با توجه به مطلب بالا کدهای خودم را به صورت زیر تغییر دادم

            لایه Service
            namespace ServiceLayer.EFServices
            {
                public class TABMPCREWService : ITABMPCREWService
                {
                    private IUnitOfWork _uow;
                    private IDbSet<TABMPCREWS> _tabmpcrews;
            
                    public TABMPCREWService(IUnitOfWork uow)
                    {
                        this._uow = uow;
                        _tabmpcrews = uow.Set<TABMPCREWS>();
                    }
            
                    public int Add(TABMPCREWS personnel)
                    {
                        int rowEffect = 0;
            
                        _tabmpcrews.Add(personnel);
                        rowEffect = _uow.SaveChanges();
                        return rowEffect;
                    }
            
                }
            }
            و اینترفیس
            namespace ServiceLayer.Interface
            {
                public interface ITABMPCREWService
                {
                    int Add(TABMPCREWS personnel);
                }
            }
            و فایل Global
             protected void Application_Start()
                    {
                        AreaRegistration.RegisterAllAreas();
            
                        RegisterGlobalFilters(GlobalFilters.Filters);
                        RegisterRoutes(RouteTable.Routes);
                        ObjectFactory.Initialize(x =>
                        {
                            x.For<ITABMPCREWService>().Use<TABMPCREWService>();
                           // x.For<IUsersService>().Use<UsersService>();
                        });
                        ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
                       // initStructureMap();
                    }
            و
             public class StructureMapControllerFactory  : DefaultControllerFactory
                {
                    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
                    {
                        if (controllerType == null)
                            throw new InvalidOperationException(string.Format("Page not found: {0}", requestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture)));
                        return ObjectFactory.GetInstance(controllerType) as Controller;
                    }
                }
            و کد کنترلر
            public class HomeController  : Controller
                {
                    public readonly ITABMPCREWService aa ;
                    public HomeController(ITABMPCREWService tabmpcrewService)
                    {
                        aa = tabmpcrewService;
                    }
            
                    public ActionResult Index()
                    {
            
                        TABMPCREWS tt = new TABMPCREWS()
                        {
                            DTLASTUPDATEDDATE = DateTime.Now,
                            INTOTRATE = 122,
                            INTRATE = 215,
                            VCCODEDESCRIPTION = "fff858699",
                            VCCODEVALUE = "fff858699",
                            VCLASTUSERID = "fff858699",
                            INTCREWCODE = 105652
                        };
                        aa.Add(tt);
            
            
                        ViewBag.Message = "Welcome to ASP.NET MVC!";
            
                        return View();
                    }
            
                    public ActionResult About()
                    {
                        ViewBag.Message = "Your app description page.";
            
                        return View();
                    }
            
            public ActionResult Contact()
                    {
                        ViewBag.Message = "Your contact page.";
            
                        return View();
                    }
                }
            اما زمان اجرا این خطا رو بهم میده و از این خط خطا می‌گیره


            • #
              ‫۱۱ سال و ۳ ماه قبل، سه‌شنبه ۲۵ تیر ۱۳۹۲، ساعت ۱۲:۲۸
              عرض کردم تعاریف ObjectFactory.Initialize و ارتباط دادن اینترفیس‌ها به کلاس‌های متناظر شما ناقص است. الان TABMPCREWService خودش دارای یک وابستگی تزریق شده در سازنده آن به نام IUnitOfWork است که تعاریف مرتبط با آن در قسمت ObjectFactory.Initialize ذکر نشدند.
              یعنی این IoC Container نمی‌دونه برای وهله سازی کلاس TABMPCREWService زمانیکه به IUnitOfWork رسید از چه کلاسی باید استفاده کند.
  • #
    ‫۱۱ سال و ۲ ماه قبل، سه‌شنبه ۱۵ مرداد ۱۳۹۲، ساعت ۱۸:۰۹
    یک نکته تکمیلی
    اگر به هر دلیلی یک کلاس پایه کنترلر را ایجاد کردید که در آن ExecuteCore تحریف شده است، این متد چه با تزریق وابستگی‌ها و چه بدون آن فراخوانی نخواهد شد. (روش صحیح مدیریت این مسایل در ASP.NET MVC استفاده از فیلترها است و نه ارث بری؛ چون در طراحی ASP.NET MVC مباحث AOP به صورت خودکار توسط فیلترها پیاده سازی می‌شوند)
        public class MyBaseController : Controller
        {
            /// <summary>
            /// from http://forums.asp.net/t/1776480.aspx/1?ExecuteCore+in+base+class+not+fired+in+MVC+4+beta
            /// </summary>
            protected override bool DisableAsyncSupport
            { 
                get { return true; } 
            }
    
            protected override void ExecuteCore()
            {
                base.ExecuteCore();
            }
        }
    برای حل این مشکل باید DisableAsyncSupport را اضافه کنید تا ExecuteCore تحریف شده، اجرا گردد (جزو تغییرات MVC4 است).
  • #
    ‫۱۰ سال و ۸ ماه قبل، سه‌شنبه ۲۲ بهمن ۱۳۹۲، ساعت ۲۲:۴۱
    سلام؛ کلاس  StructureMapControllerFactory  در کجای برنامه باید پیاده سازی شود؟
    • #
      ‫۱۰ سال و ۸ ماه قبل، سه‌شنبه ۲۲ بهمن ۱۳۹۲، ساعت ۲۲:۴۵
      - در یک کلاس یا یک کتابخانه کمکی.
      - محل فراخوانی و معرفی آن مهم است که در بحث هم ذکر شده: Application_Start 
  • #
    ‫۱۰ سال و ۵ ماه قبل، شنبه ۲۰ اردیبهشت ۱۳۹۳، ساعت ۰۱:۳۲
    سلام؛
    من در پروژه ام (یک برنامه ASP.NET MVC) یک کنترلر Web Api ایجاد کردم؛ در این حالت تنظیم DependencyResolver به چه صورت است؟ یعنی همزمان هم باید به این دو صورت تنظیم شود؟
    protected void Application_Start()
    {
       DependencyResolver.SetResolver(new StructureMapDependencyResolver());
       GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver(container);
    }
    در این حالت پیاده سازی StructureMapDependencyResolver به چه صورت خواهد بود؟
    ممنون.
    • #
      ‫۱۰ سال و ۵ ماه قبل، شنبه ۲۰ اردیبهشت ۱۳۹۳، ساعت ۰۳:۳۵
      مانند همان توضیحات انتهای بحث است. فقط در متد BeginScope باید this بازگشت داده شود:
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Web.Http.Dependencies;
      using StructureMap;
      
          public class StructureMapDependencyResolver : IDependencyResolver
          {
              public IDependencyScope BeginScope()
              {
                  return this;
              }
      
              public object GetService(Type serviceType)
              {
                  if (serviceType.IsAbstract || serviceType.IsInterface || !serviceType.IsClass)
                      return ObjectFactory.Container.TryGetInstance(serviceType);
                  return ObjectFactory.GetInstance(serviceType);
              }
      
              public IEnumerable<object> GetServices(Type serviceType)
              {
                  return ObjectFactory.GetAllInstances(serviceType).Cast<object>();
              }
      
              public void Dispose()
              { }
          }
    • #
      ‫۱۰ سال و ۵ ماه قبل، شنبه ۲۰ اردیبهشت ۱۳۹۳، ساعت ۰۶:۱۶
      یک نکته‌ی تکمیلی در مورد Web API
      Mark Seemann توصیه کرده‌است که از IDependencyResolver استفاده نکنید. روش دیگری برای کار با Web API مورد تائید ایشان است:
      Dependency Injection and Lifetime Management with ASP.NET Web API
      Dependency Injection in ASP.NET Web API with Castle Windsor
      پیاده سازی این نکته با StructureMap در اینجا:
      Better way to configure StructureMap in ASP.NET WebAPI
      • #
        ‫۱۰ سال و ۵ ماه قبل، شنبه ۲۰ اردیبهشت ۱۳۹۳، ساعت ۱۲:۵۰
        ممنون؛ مراحل رو به این صورت انجام دادم :
        static void InitStructureMap()
        {
                    ObjectFactory.Initialize(x =>
                    {
                        x.For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<MyContext>();
                        x.Scan(scan =>
                        {
                            scan.AssemblyContainingType<INewsService>();
                            scan.WithDefaultConventions();
                        });
                        
                    });
        
                    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
                    var container = ObjectFactory.Container;
                    GlobalConfiguration.Configuration.Services
                        .Replace(typeof(IHttpControllerActivator),
                        new StructureMapHttpControllerActivator(container));
                    
        }
        پیاده سازی StructureMapHttpControllerActivator به همان صورت که در لینک معرفی کردید انجام دادم.
        ممنون از شما.
  • #
    ‫۹ سال و ۱۰ ماه قبل، پنجشنبه ۲۲ آبان ۱۳۹۳، ساعت ۰۵:۰۴
    سلام و خسته نباشید.
    اگر از StructureMap.Mvc5  استفاده کنم  آیا برای Web Api هم پاسخگو خواهد بود ؟ یا اگر از StructureMap.WebApi استفاده کنم ، میتونه هم MVC   و هم Web Api رو ساپورت کنه؟
    با تشکر.
  • #
    ‫۹ سال و ۱۰ ماه قبل، سه‌شنبه ۲۷ آبان ۱۳۹۳، ساعت ۰۴:۵۷
    سلام و خسته نباشید.
    من تو پروژه ام از StructureMap.MVC5 استفاده کردم ولی به مشکل زیر برخوردم.اگر امکان داشته باشه راهنمایی کنید.ممنون
    کد‌های لایه‌ی سرویس:

    کدی که قرار است در هر درخواست از لایه سرویس برای چک کردن اینکه کاربر قفل شده یا نه استفاده میشود.

    • #
      ‫۹ سال و ۱۰ ماه قبل، سه‌شنبه ۲۷ آبان ۱۳۹۳، ساعت ۰۵:۳۵
      - در مورد خطای «No Default Instance defined » کمی بالاتر در نظرات بحث شده. یعنی تنظیمات اولیه ناقصی دارید. ادامه‌ی پیام آن هم بسیار واضح است. عنوان کرده تنظیمات اولیه مرتبط را پیدا نمی‌کند.
      در پروژه‌ی ثالث یاد شده، در کلاس IoC آن، کار <AddRegistry<DefaultRegistry انجام شده‌است و اگر در برنامه‌ی شما IoC.Initialize  فراخوانی نشود، کلاس DefaultRegistry جایی استفاده نخواهد شد و پس از آن پیام یافت نشدن تنظیمات را دریافت می‌کنید.
      همچنین این کتابخانه از  Microsoft.Web.Infrastructure.DynamicModuleHelper برای کارهای Service locator استفاده کرده‌است. به عبارتی اگر در حالت عادی جواب می‌گیرید (مثلا تزریق وابستگی در کنترلرها درست کار می‌کنند) اما با فراخوانی ObjectFactory مشکل دارید، از این جهت است که DefaultRegistry آن اصلا به ObjectFactory معرفی نشده‌است. اما DefaultRegistry و خروجی IoC.Initialize آن به صورت محدود در اختیار ماژول‌های این کتابخانه قرار گرفته‌اند.

      -  در کل روشی که در مطلب جاری عنوان شده، بررسی زیر ساخت این ابزارها است و با MVC 5 هم کار می‌کند. با 3 و 4 هم کار می‌کند.
      • #
        ‫۹ سال و ۱۰ ماه قبل، سه‌شنبه ۲۷ آبان ۱۳۹۳، ساعت ۱۹:۲۴
        از همین روش جاری استفاده کردم و دیگه مشکلی پیش نیومد .ممنون از راهنماییتان
  • #
    ‫۹ سال و ۱۰ ماه قبل، سه‌شنبه ۴ آذر ۱۳۹۳، ساعت ۱۶:۰۳

    با سلام

    در استفاده عادی و بدون structureMap مشکلی ندارم اما زمانی که از StructureMap  استفاده می‌کنم

    در کلاس DefaultControllerFactory در متد GetControllerInstance مقدار controllerType برای Area‌ها null برمی گردد

    همون طور کد خدمتتون عرض کردم در حالت عادی و بدون structormap مشکلی وجود ندارد و کلی مسیر دهی‌ها به درستی انجام می شود ولی در زمان استفاده از Ioc با Area‌ها مشکل دارم.

    در اینجا مطلبی دیدم که مسیردهی‌ها را از طریق یک Super کلاس انجام داده اند که فکر نمی‌کنم مسیردهی مجدد کار درستی باشد و از طرفی super کلاس من بین کنترل‌های Area و کنترل‌های خارج از Area مشترک است و نمی‌تونم همچین کاری انجام بدم.

    چرا در GetControllInstance مقدار controllerType در زمان استفاده از structoremap برای مسیرها و کنترل‌های موجود در Area‌ها null است در حالتی که بدون استفاده از آن مسیردهی به درستی انجام می‌شود

      • #
        ‫۹ سال و ۱۰ ماه قبل، سه‌شنبه ۴ آذر ۱۳۹۳، ساعت ۱۶:۲۸
        از روش آدرس دهی کامل استفاده می‌کنم اما باز هم controllerType مقدار null می‌گیرد !
        • #
          ‫۹ سال و ۱۰ ماه قبل، چهارشنبه ۵ آذر ۱۳۹۳، ساعت ۱۶:۰۹
          - نال بودن controllerType صرفا به معنای یافت نشدن آدرس درخواستی است. این مساله پیش از رجوع به StructureMap رخ می‌دهد و ارتباطی به آن ندارد.
          - مثال کامل بحث جاری که به همراه یک Area نیز هست؛ برای دریافت:
          DI06.zip
  • #
    ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۱۷ آذر ۱۳۹۳، ساعت ۱۵:۴۳
    آیا می‌شود سرویسی را به ویو تزریق کرد؟ به طور مثال درون یک ویو به سرویس SecurityService دسترسی داشته باشیم:
    @if (!SecurityService.IsAuthenticated)
    {
                                <li>@Html.ActionLink("ورود", "Login", "Main")</li>
    }
    برای اینکار یک ViewPage سفارشی ایجاد کردم ولی برای تزریق وابستگی‌ها چیز خاصی به ذهنم نمی‌رسه.
    public abstract class CustomViewPage : ViewPage
        {
            public CustomViewPage(IUserService userService, ISecurityService securityService, IUnitOfWork uow)
            {
                Uow = uow;
                SecurityService = securityService;
                UserService = userService;
            }
    
            public IUserService UserService { get; private set; }
            public ISecurityService SecurityService { get; private set; }
            public IUnitOfWork Uow { get; private set; }
            
        }
        public abstract class CustomViewPage<TModel> : ViewPage<TModel>
        {
            public CustomViewPage(IUserService userService, ISecurityService securityService, IUnitOfWork uow)
            {
                Uow = uow;
                SecurityService = securityService;
                UserService = userService;
            }
    
            public IUserService UserService { get; private set; }
            public ISecurityService SecurityService { get; private set; }
            public IUnitOfWork Uow { get; private set; }
    
            
        }
    آیا در زمان Initialize کردن وابستگی‌ها باید تنظیم خاصی صورت گیرد؟
    • #
      ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۱۷ آذر ۱۳۹۳، ساعت ۱۶:۱۱
      - بله می‌شود. از الگوی service locator استفاده کنید:
      @{
          ViewBag.Title = "Index";
      
          var categoryService = ObjectFactory.Container.GetInstance<ICategoryService>();
          var list = categoryService.GetAllCategories();
      }
      - اما ... اینکار برخلاف رویه MVC است. در الگوی MVC یک View نباید مستقیما کوئری بگیرد. View فقط باید اطلاعات مورد نیاز خود را از کنترلر مرتبط دریافت کند.
      اطلاعات بیشتر: Don’t Query from the View 
      - اگر نیاز است یک سری اطلاعات تکراری در هر اکشن متد به Viewها تزریق شود، روال کار AOP است. در MVC برای پیاده سازی AOP فقط کافی است یک ویژگی جدید از نوع ActionFilterAttribute تعریف کنید و خواص تکراری را به آن منتقل کنید:
      public class DuplicateInfo : ActionFilterAttribute
          {
              public override void OnActionExecuting(ActionExecutingContext filterContext)
              {
                  filterContext.Controller.ViewBag.CanonicalUrl = "redirectUrl ..... ";
      
                  base.OnActionExecuting(filterContext);
              }
          }
      در اینجا برای نمونه، نحوه‌ی دسترسی به ViewBag را مشاهده می‌کنید. اکنون اکشن متدی که به ویژگی DuplicateInfo مزین شود، به تمام اطلاعات تنظیم شده توسط آن هم دسترسی خواهد داشت.
      • #
        ‫۹ سال و ۱۰ ماه قبل، دوشنبه ۱۷ آذر ۱۳۹۳، ساعت ۱۶:۳۷
        ممنون، بله، تنها هدفم استفاده از کلاس SecurityService برای Authentication کاربران بود.
      • #
        ‫۹ سال و ۴ ماه قبل، جمعه ۱۵ خرداد ۱۳۹۴، ساعت ۱۷:۱۲
        در ASP.NET 5 یک دایرکتیو جدید با نام inject تدارک دیده شده است که امکان اینجکت کردن وابستگی‌ها را به صورت مستقیم داخل ویو را به ما میدهد:
        @inject ISecurityService SecurityService
         
        <h3>@ViewBag.Message</h3>
         
        @if (!SecurityService.IsAuthenticated)
        {
               <li>@Html.ActionLink("ورود", "Login", "Main")</li>
        }



  • #
    ‫۹ سال و ۹ ماه قبل، دوشنبه ۸ دی ۱۳۹۳، ساعت ۱۳:۰۰
    با سلام.
    من سرویسی به نام ISettingService در لایه سرویس خود دارم که شامل متدهایی برای بازیابی تنظیمات برنامه است. در برنامه بطور متناوب از این سرویس استفاده می‌کنم. آیا این سرویس باید بصورت singleton پیاده سازی شود؟ نحوه تعریف من نیز بصورت زیر است:
    x.For<ISettingService>().Singleton().Use(() => new SettingService());
    چون سازنده سرویس دارای پارامتر uow است، در نحوه مقداردهی آن در خطا بالا دچار خطا می‌شوم.
    سازنده این سرویس بصورت زیر است:
    public SettingService(IUnitOfWork uow)
    {
         _uow = uow;
    }
    سپاس.
  • #
    ‫۹ سال و ۹ ماه قبل، پنجشنبه ۱۸ دی ۱۳۹۳، ساعت ۰۲:۵۷
    جهت اطلاع
    به روز شده‌ی این مثال‌ها را بر اساس آخرین تغییرات وابستگی‌های آن‌ها از مخزن کد ذیل می‌توانید دریافت کنید:
    Dependency-Injection-Samples
     
  • #
    ‫۷ سال و ۵ ماه قبل، جمعه ۱ اردیبهشت ۱۳۹۶، ساعت ۰۵:۱۷
    سلام؛ اگر بخوایم از این روش در کنار UnitOfWork استفاده کنیم تکلیف تزریق وابستگی اینترفیس IUnitOfWork به سازنده کنترلرها چی میشه؟ آیا نیازی به تنظیمات اضافه هست یا همین مراحلی که گفته شده کافیه؟
    • #
      ‫۷ سال و ۵ ماه قبل، جمعه ۱ اردیبهشت ۱۳۹۶، ساعت ۰۶:۱۵
      تفاوتی نمی‌کند و یکی هست.
  • #
    ‫۷ سال و ۲ ماه قبل، سه‌شنبه ۲۰ تیر ۱۳۹۶، ساعت ۱۷:۳۳
    متد GetControllerInstance بهتره به صورت زیر اصلاح بشه:
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
    if (controllerType == null)
    {
      throw new HttpException(404, $"Resource not found : {requestContext.HttpContext.Request.Path}");
    }
    
    if (!typeof(IController).IsAssignableFrom(controllerType))
    {
      throw new ArgumentException($"{controllerType} is not a subclass of ControllerBase");
    }
    
    return ObjectFactory.Container.GetInstance(controllerType) as Controller;
    }

    چون با کدی مثل زیر به مشکل بر میخوره:
    <customErrors mode="On" defaultRedirect="/Content/GeneralErrorPage.html">
      <error statusCode="404" redirect="/Content/NotFound.html"/>
    </customErrors>

    و موجب میشه تا در صورت یافت نشدن منبع، به جای هدایت به صفحه‌ی NotFound.html، خطای Page not found رخ بده.
    رفتار صحیح در سورس خود MVC هم وجود داره که من کمی اصلاحش کردم.
  • #
    ‫۷ سال قبل، دوشنبه ۱۳ شهریور ۱۳۹۶، ساعت ۱۹:۴۴
    در برنامه‌های سازمانی Enterprise باید لایه سرویس مستقل باشد چون کد بیزینس برایشان مهم است و باید مستقل از تکنولوژی باشد.
    در مثال UoW-Sample-master که Context Per Request رو پیاده کردین لایه سرویس به لایه دیتا رفرنس دارد.
    اگر بخواهیم سرویس را مستقل کنیم راه حل چیست؟
    پروژه سمپلی در این باره معرفی می‌کنین؟ 

    • #
      ‫۷ سال قبل، دوشنبه ۱۳ شهریور ۱۳۹۶، ساعت ۱۹:۵۵
      لایه سرویس بر اساس لایه دیتا هست که قادر هست سرویسی را ارائه دهد. تامین کننده‌ی اصلی اینترفیس IUnitOfWork، لایه دیتا هست.
      درکل لایه سرویس در اینجا صرفا به دلیل دسترسی به IUnitOfWork هست که این ارجاع را دارد و کل کارکرد آن بر اساس این اینترفیس صورت می‌گیرد. یک مثال دیگر: « اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity ». در نهایت بالاترین لایه برنامه که همان کنترلرها هستند صرفا بر اساس تزریق وابستگی‌های اینترفیس‌های لایه سرویس کار می‌کنند.
      لایه سرویس فقط با اینترفیس IUnitOfWork کار می‌کند. این اینترفیس را در هر اسمبلی دیگری که خواستید قرار دهید و ارجاعی را به آن تعریف کنید. در این حالت بازهم برنامه کار می‌کند چون سیستم تزریق وابستگی‌ها در ابتدای کار تامین کننده‌ی این اینترفیس را مشخص خواهد کرد و اتصالات نهایی به خوبی برقرار می‌شوند.
      • #
        ‫۷ سال قبل، سه‌شنبه ۱۴ شهریور ۱۳۹۶، ساعت ۱۹:۲۵
        پس کاربرد اصلی DI در لایه‌ها ، قابلیت نوشتن تست است؟
        • #
          ‫۷ سال قبل، پنجشنبه ۱۶ شهریور ۱۳۹۶، ساعت ۱۸:۴۵
          نوشتن تست و Mocking
          خودکارسازی فرایند ایجاد اشیا
          نحوه ساخت اشیا با توجه به context و یا singleton و ...
          تغییر کلاس در آینده در تمامی بخش ها
  • #
    ‫۶ سال و ۱۰ ماه قبل، سه‌شنبه ۲۳ آبان ۱۳۹۶، ساعت ۱۸:۴۵
    سلام
    زمانی که در فایل global.asax خود کلاس MvcApplication به کلاس دیگری وابستگی داشته باشه (مثلا به کلاس Users از لایه Business بابت  اعتبارسنجی کاربران از طریق دیتابیس به جای کوکی) چطور می‌تونیم کلاس Users را وهله سازی کرده و به آن دسترسی داشته باشیم؟
        public class MvcApplication : HttpApplication
        {
            private readonly IUsers _users;
    
            protected MvcApplication(IUsers users)
            {
                _users = users;
            }
    
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
    
                ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
            }
    
            protected void Application_AuthenticateRequest()
            {
                var test = _users.Test();
                //...
            }
        }

    • #
      ‫۶ سال و ۱۰ ماه قبل، سه‌شنبه ۲۳ آبان ۱۳۹۶، ساعت ۲۱:۲۴
      چون وهله سازی این کلاس در اختیار شما نیست و همچنین مانند ASP.NET Core فاقد سیستم تزریق وابستگی‌های توکار در تمام قسمت‌های آن است، تنها راه باقیمانده استفاده از روش service locator است:
           var userService = SampleObjectFactory.Container.GetInstance<IUserService>();