طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت اول
نویسنده: وحید نصیری
تاریخ: ۱۳۹۴/۰۱/۲۷ ۱۳:۱۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
PM> Install-Package RazorGenerator.Mvc
PM> Enable-RazorGenerator
Copy "$(ProjectDir)$(OutDir)$(TargetName).*" "$(SolutionDir)MvcPluginMasterApp\bin\"
using System.Web.Mvc;
namespace MvcPluginMasterApp.Plugin1.Areas.NewsArea
{
public class NewsAreaAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "NewsArea";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"NewsArea_default",
"NewsArea/{controller}/{action}/{id}",
// تکمیل نام کنترلر پیش فرض
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// مشخص کردن فضای نام مرتبط جهت جلوگیری از تداخل با سایر قسمتهای برنامه
namespaces: new[] { string.Format("{0}.Controllers", this.GetType().Namespace) }
);
}
}
} using System.Web.Mvc;
using System.Web.Routing;
namespace MvcPluginMasterApp
{
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 = "Home", action = "Index", id = UrlParameter.Optional },
// مشخص کردن فضای نام مرتبط جهت جلوگیری از تداخل با سایر قسمتهای برنامه
namespaces: new[] { string.Format("{0}.Controllers", typeof(RouteConfig).Namespace) }
);
}
}
} using System;
using System.Reflection;
using System.Web.Optimization;
using System.Web.Routing;
using StructureMap;
namespace MvcPluginMasterApp.PluginsBase
{
public interface IPlugin
{
EfBootstrapper GetEfBootstrapper();
MenuItem GetMenuItem(RequestContext requestContext);
void RegisterBundles(BundleCollection bundles);
void RegisterRoutes(RouteCollection routes);
void RegisterServices(IContainer container);
}
public class EfBootstrapper
{
/// <summary>
/// Assemblies containing EntityTypeConfiguration classes.
/// </summary>
public Assembly[] ConfigurationsAssemblies { get; set; }
/// <summary>
/// Domain classes.
/// </summary>
public Type[] DomainEntities { get; set; }
/// <summary>
/// Custom Seed method.
/// </summary>
//public Action<IUnitOfWork> DatabaseSeeder { get; set; }
}
public class MenuItem
{
public string Name { set; get; }
public string Url { set; get; }
}
} PM> install-package EntityFramework PM> install-package Microsoft.AspNet.Web.Optimization PM> install-package structuremap.web
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using MvcPluginMasterApp.PluginsBase;
using StructureMap;
namespace MvcPluginMasterApp.Plugin1
{
public class Plugin1 : IPlugin
{
public EfBootstrapper GetEfBootstrapper()
{
return null;
}
public MenuItem GetMenuItem(RequestContext requestContext)
{
return new MenuItem
{
Name = "Plugin 1",
Url = new UrlHelper(requestContext).Action("Index", "Home", new { area = "NewsArea" })
};
}
public void RegisterBundles(BundleCollection bundles)
{
//todo: ...
}
public void RegisterRoutes(RouteCollection routes)
{
//todo: add custom routes.
}
public void RegisterServices(IContainer container)
{
// todo: add custom services.
container.Configure(cfg =>
{
//cfg.For<INewsService>().Use<EfNewsService>();
});
}
}
} using System;
using System.IO;
using System.Threading;
using System.Web;
using MvcPluginMasterApp.PluginsBase;
using StructureMap;
using StructureMap.Graph;
namespace MvcPluginMasterApp.IoCConfig
{
public static class SmObjectFactory
{
private static readonly Lazy<Container> _containerBuilder =
new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
public static IContainer Container
{
get { return _containerBuilder.Value; }
}
private static Container defaultContainer()
{
return new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssembliesFromPath(
path: Path.Combine(HttpRuntime.AppDomainAppPath, "bin"),
// یک اسمبلی نباید دوبار بارگذاری شود
assemblyFilter: assembly =>
{
return !assembly.FullName.Equals(typeof(IPlugin).Assembly.FullName);
});
scanner.WithDefaultConventions(); //Connects 'IName' interface to 'Name' class automatically.
scanner.AddAllTypesOf<IPlugin>().NameBy(item => item.FullName);
});
});
}
}
} PM> install-package EntityFramework PM> install-package structuremap.web
using System.Linq;
using System.Web.Optimization;
using System.Web.Routing;
using MvcPluginMasterApp;
using MvcPluginMasterApp.IoCConfig;
using MvcPluginMasterApp.PluginsBase;
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(PluginsStart), "Start")]
namespace MvcPluginMasterApp
{
public static class PluginsStart
{
public static void Start()
{
var plugins = SmObjectFactory.Container.GetAllInstances<IPlugin>().ToList();
foreach (var plugin in plugins)
{
plugin.RegisterServices(SmObjectFactory.Container);
plugin.RegisterRoutes(RouteTable.Routes);
plugin.RegisterBundles(BundleTable.Bundles);
}
}
}
} @using MvcPluginMasterApp.IoCConfig
@using MvcPluginMasterApp.PluginsBase
@{
var plugins = SmObjectFactory.Container.GetAllInstances<IPlugin>().ToList();
}
@foreach (var plugin in plugins)
{
var menuItem = plugin.GetMenuItem(this.Request.RequestContext);
<li>
<a href="@menuItem.Url">@menuItem.Name</a>
</li>
} <div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("MvcPlugin Master App", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Master App/Home", "Index", "Home", new {area = ""}, null)</li>
@{ Html.RenderPartial("_PluginsMenu"); }
</ul>
</div>
</div>
</div>
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"BlogArea_default",
"BlogArea/{controller}/{action}/{id}",
// تکمیل نام کنترلر پیش فرض
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// مشخص کردن فضای نام مرتبط جهت جلوگیری از تداخل با سایر قسمتهای برنامه
namespaces: new[] { string.Format("{0}.Controllers", this.GetType().Namespace) }
);
} Url = new UrlHelper(requestContext).Action("Index", "Home",new{area="BlogArea"}) routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { string.Format("{0}.Controllers", typeof(RouteConfig).Namespace) }
); <HintPath>..\..\..\packages\T4MVCExtensions.3.15.0\lib\net40\T4MVCExtensions.dll</HintPath>
<HintPath>..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll</HintPath>
Copy "$(ProjectDir)$(OutDir)*.*" "$(SolutionDir)RabbalShopCMS.Web\bin\"
ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new RazorViewEngine ());
PM> update-package
ViewEngines.Engines.Insert(0, engine);
ViewEngines.Engines.Add(engine);
ممنون از شما
برای اضافه نمودن قابلیت چند زبانه (Globalization) به این سیستم نکته خاصی وجود داره ؟
اگه قرار باشه یک سایت سه بخش مجزا داشته باشه که هرکدوم دارای پلاگینهای خودش باشه اونوقت باید هرکدوم IplugIn جداگانه داشته باشه؟
مثلا سه بخش Root ، User و Admin اونوقت افزونه نویسی برای این بخش ها به چه شکل خواهد بود؟
public MenuItem GetMenuItem(RequestContext requestContext)
{
return new MenuItem
{
Name = "بیوگرافی",
Url = new UrlHelper(requestContext).Action("Index", "Home", new { area = "Biography"}),
Icon = "fa fa-child"
};
} public class BiographyAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Biography";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Biography_default",
"Biography/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces:new []{$"{this.GetType().Namespace}.Controllers"}
);
} The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched: ~/Areas/Biography/Views/My/Index.cshtml ~/Areas/Biography/Views/Shared/Index.cshtml ~/Views/My/Index.cshtml ~/Views/Shared/Index.cshtml ~/Areas/Biography/Views/My/Index.vbhtml ~/Areas/Biography/Views/Shared/Index.vbhtml ~/Views/My/Index.vbhtml ~/Views/Shared/Index.vbhtml
Exception calling "GetItem" with "1" argument(s): "Value does not fall within the expected range."
At F:\projects\MvcProject\PPU\packages\RazorGenerator.Mvc.2.3.6\tools\RazorGenerator.psm1:63 char:21
+ ... $solutionExplorer.GetItem("$SolutionName\$ProjectName$rel ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentException این خطار را اینجا پیگیری کنید: https://razorgenerator.codeplex.com/workitem/116
اگر با اجرای دستور Redo-RazorGenerator حل نشد، اینجا گزارشش کنید: https://github.com/RazorGenerator/RazorGenerator/issues
PM> install-package structuremap
public class AutoMapperRegistry : Registry
{
public AutoMapperRegistry()
{
Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.AddAllTypesOf<Profile>();
});
For<MapperConfiguration>().Use("", ctx =>
{
var profiles=ctx.GetAllInstances<Profile>().ToList();
var config = new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
});
return config;
});
For<IMapper>().Use(ctx => ctx.GetInstance<MapperConfiguration>().CreateMapper(ctx.GetInstance));
}
} Method not found: 'Void StructureMap.Graph.IAssemblyScanner.AssembliesFromPath(System.String, System.Func`2<System.Reflection.Assembly,Boolean>)'.'