मैंने एक ऐसी परियोजना पर काम किया है, जिसमें आपके द्वारा वर्णित समान प्लग-योग्य आर्किटेक्चर था और इसमें उन्हीं तकनीकों का उपयोग किया गया था। ASP.NET MVC और MEF। हमारे पास एक होस्ट ASP.NET MVC एप्लिकेशन था जिसने प्रमाणीकरण, प्राधिकरण और सभी अनुरोधों को संभाला। हमारे प्लगइन्स (मॉड्यूल) को एक उप-फ़ोल्डर में कॉपी किया गया था। प्लगइन्स ASP.NET MVC एप्लिकेशन भी थे जिनके अपने मॉडल, नियंत्रक, विचार, सीएसएस और जेएस फाइलें थीं। ये कदम हैं जो हमने इसे काम करने के लिए अनुसरण किए हैं:
MEF की स्थापना
हमने एमईएफ पर आधारित इंजन बनाया जो सभी कंपार्टेबल पार्ट्स को एप्लिकेशन स्टार्ट पर डिस्क्राइब करता है और कंपोजेबल पार्ट्स का कैटलॉग बनाता है। यह एक ऐसा कार्य है जो केवल एक बार आवेदन शुरू होने पर किया जाता है। इंजन को सभी प्लग करने योग्य भागों की खोज करने की आवश्यकता है, कि हमारे मामले bin
में मेजबान एप्लिकेशन के Modules(Plugins)
फ़ोल्डर में या फ़ोल्डर में स्थित थे ।
public class Bootstrapper
{
private static CompositionContainer CompositionContainer;
private static bool IsLoaded = false;
public static void Compose(List<string> pluginFolders)
{
if (IsLoaded) return;
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")));
foreach (var plugin in pluginFolders)
{
var directoryCatalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", plugin));
catalog.Catalogs.Add(directoryCatalog);
}
CompositionContainer = new CompositionContainer(catalog);
CompositionContainer.ComposeParts();
IsLoaded = true;
}
public static T GetInstance<T>(string contractName = null)
{
var type = default(T);
if (CompositionContainer == null) return type;
if (!string.IsNullOrWhiteSpace(contractName))
type = CompositionContainer.GetExportedValue<T>(contractName);
else
type = CompositionContainer.GetExportedValue<T>();
return type;
}
}
यह उस वर्ग का नमूना कोड है जो सभी MEF भागों की खोज करता है। फ़ाइल में विधि Compose
से कक्षा की विधि को कहा जाता है । कोड सादगी के लिए कम किया गया है।Application_Start
Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var pluginFolders = new List<string>();
var plugins = Directory.GetDirectories(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules")).ToList();
plugins.ForEach(s =>
{
var di = new DirectoryInfo(s);
pluginFolders.Add(di.Name);
});
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
Bootstrapper.Compose(pluginFolders);
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
ViewEngines.Engines.Add(new CustomViewEngine(pluginFolders));
}
}
यह माना जाता है कि सभी प्लगइन्स को फ़ोल्डर के एक अलग उप-फ़ोल्डर में कॉपी किया Modules
जाता है जो होस्ट एप्लिकेशन की जड़ में स्थित है। प्रत्येक प्लगइन सबफ़ोल्डर में प्रत्येक Views
फ़ोल्डर से उप-फ़ोल्डर और DLL शामिल हैं। Application_Start
ऊपर की विधि में भी कस्टम नियंत्रक कारखाने और कस्टम व्यू इंजन को इनिशियलाइज़ किया गया है जिसे मैं नीचे परिभाषित करूंगा।
नियंत्रक कारखाना बनाना जो एमईएफ से पढ़ता है
यहाँ कस्टम नियंत्रक कारखाने को परिभाषित करने के लिए कोड है जो अनुरोध को संभालने के लिए नियंत्रक की खोज करेगा:
public class CustomControllerFactory : IControllerFactory
{
private readonly DefaultControllerFactory _defaultControllerFactory;
public CustomControllerFactory()
{
_defaultControllerFactory = new DefaultControllerFactory();
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
var controller = Bootstrapper.GetInstance<IController>(controllerName);
if (controller == null)
throw new Exception("Controller not found!");
return controller;
}
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
}
इसके अतिरिक्त प्रत्येक नियंत्रक को Export
विशेषता के साथ चिह्नित किया जाना चाहिए :
[Export("Plugin1", typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Plugin1Controller : Controller
{
public ActionResult Index()
{
return View();
}
}
Export
विशेषता निर्माता का पहला पैरामीटर अद्वितीय होना चाहिए क्योंकि यह अनुबंध नाम निर्दिष्ट करता है और विशिष्ट रूप से प्रत्येक नियंत्रक की पहचान करता है। PartCreationPolicy
NonShared करने के लिए सेट किया जाना चाहिए क्योंकि नियंत्रकों से अधिक अनुरोध के लिए पुन: उपयोग नहीं किया जा सकता।
व्यू इंजन बनाना जो प्लगइन्स से विचारों को जानना जानता है
कस्टम व्यू इंजन के निर्माण की आवश्यकता है क्योंकि कन्वेंशन द्वारा व्यू इंजन केवल Views
होस्ट एप्लिकेशन के फ़ोल्डर में विचारों के लिए दिखता है । चूंकि प्लगइन्स अलग Modules
फ़ोल्डर में स्थित हैं , इसलिए हमें वहां भी देखने के लिए व्यू इंजन को बताने की आवश्यकता है।
public class CustomViewEngine : RazorViewEngine
{
private List<string> _plugins = new List<string>();
public CustomViewEngine(List<string> pluginFolders)
{
_plugins = pluginFolders;
ViewLocationFormats = GetViewLocations();
MasterLocationFormats = GetMasterLocations();
PartialViewLocationFormats = GetViewLocations();
}
public string[] GetViewLocations()
{
var views = new List<string>();
views.Add("~/Views/{1}/{0}.cshtml");
_plugins.ForEach(plugin =>
views.Add("~/Modules/" + plugin + "/Views/{1}/{0}.cshtml")
);
return views.ToArray();
}
public string[] GetMasterLocations()
{
var masterPages = new List<string>();
masterPages.Add("~/Views/Shared/{0}.cshtml");
_plugins.ForEach(plugin =>
masterPages.Add("~/Modules/" + plugin + "/Views/Shared/{0}.cshtml")
);
return masterPages.ToArray();
}
}
प्लगइन्स में दृढ़ता से टाइप किए गए विचारों के साथ समस्या को हल करें
केवल उपरोक्त कोड का उपयोग करके, हम अपने प्लगइन्स (मॉड्यूल) में दृढ़ता से टाइप किए गए विचारों का उपयोग नहीं कर सकते, क्योंकि मॉडल bin
फ़ोल्डर के बाहर मौजूद थे । इस समस्या को हल करने के लिए निम्न लिंक का अनुसरण करें ।