在当前的软件开发实践中,模块化架构是一种常见的设计模式,它能够提高系统的可维护性和可扩展性。对于电商系统而言,模块化尤为重要,因为电商系统往往需要频繁地更新和扩展新的功能。本文将介绍如何在ASP.NET Core1.0环境下构建一个模块化的电商系统。
在.NET Core环境下,构建模块化应用面临一些挑战。例如,如何让MVC框架识别位于不同类库中的控制器,如何让视图引擎找到模块中的视图,如何注册模块中使用的服务,以及如何为模块服务静态文件等。这些问题都是构建模块化电商系统时需要解决的关键问题。
为了解决上述问题,采取了以下策略:
以下是实现这些策略的具体步骤:
为了动态加载模块中的程序集,首先需要扫描每个模块的bin目录下的DLL文件。以下是实现这一功能的代码示例:
var moduleRootFolder = new DirectoryInfo(Path.Combine(_hostingEnvironment.ContentRootPath, "Modules"));
var moduleFolders = moduleRootFolder.GetDirectories();
foreach (var moduleFolder in moduleFolders)
{
var binFolder = new DirectoryInfo(Path.Combine(moduleFolder.FullName, "bin"));
if (!binFolder.Exists) continue;
foreach (var file in binFolder.GetFileSystemInfos("*.dll", SearchOption.AllDirectories))
{
Assembly assembly = null;
try
{
assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file.FullName);
}
catch (FileLoadException ex)
{
if (ex.Message == "Assembly with same name is already loaded")
{
assembly = Assembly.Load(new AssemblyName(Path.GetFileNameWithoutExtension(file.Name)));
}
else
{
throw;
}
}
if (assembly.FullName.Contains(moduleFolder.Name))
{
modules.Add(new ModuleInfo { Name = moduleFolder.Name, Assembly = assembly, Path = moduleFolder.FullName });
}
}
}
为了使视图引擎能够找到模块中的视图,使用了自定义的视图位置扩展器。以下是实现这一功能的代码示例:
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new ModuleViewLocationExpander());
});
每个模块都包含一个ModuleInitializer.cs文件,用于注册该模块的服务。以下是实现这一功能的代码示例:
var moduleInitializerInterface = typeof(IModuleInitializer);
foreach (var module in modules)
{
var moduleInitializerType = module.Assembly.GetTypes().Where(x => typeof(IModuleInitializer).IsAssignableFrom(x)).FirstOrDefault();
if (moduleInitializerType != null && moduleInitializerType != typeof(IModuleInitializer))
{
var moduleInitializer = (IModuleInitializer)Activator.CreateInstance(moduleInitializerType);
moduleInitializer.Init(services);
}
}
为了为模块服务静态文件,遍历每个模块,并使用StaticFileOptions来配置静态文件服务。以下是实现这一功能的代码示例:
foreach (var module in modules)
{
var wwwrootDir = new DirectoryInfo(Path.Combine(module.Path, "wwwroot"));
if (!wwwrootDir.Exists) continue;
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(wwwrootDir.FullName),
RequestPath = new PathString("/mod/" + module.SortName)
});
}
private static void RegisterEntities(ModelBuilder modelBuilder, IEnumerable<Type> typeToRegisters)
{
var entityTypes = typeToRegisters.Where(x => x.GetTypeInfo().IsSubclassOf(typeof(Entity)) && !x.GetTypeInfo().IsAbstract);
foreach (var type in entityTypes)
{
modelBuilder.Entity(type);
}
}