欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【.Net架构】BIM软件架构03:Web管控平台MVC架构

程序员文章站 2022-04-26 21:49:24
一、前言 上一篇讲述的是将BIM平台后台架构CoreService.sln,该解决方案主要作用是对管控平台的核心业务进行封装,然后让前端的ApiController去调用该解决方案中的对应的dll方法。说到MVC前端架构,可能很多人都有一个误区,觉得都是按照微软的例子去写即可。其实在中大型的项目中按 ......

一、前言

       上一篇讲述的是将BIM平台后台架构CoreService.sln,该解决方案主要作用是对管控平台的核心业务进行封装,然后让前端的ApiController去调用该解决方案中的对应的dll方法。说到MVC前端架构,可能很多人都有一个误区,觉得都是按照微软的例子去写即可。其实在中大型的项目中按照微软MVC的demo样例去实施会对今后的维护工作带来一定难度。我在BIM平台部门的时候经历过多次MVC架构的修改,在熟悉了整个Web开发一个月之后,我与另一位Tech Leader准备再次进行改造,奠定了以后项目的基调,这大概算是我留给老东家最后的礼物吧。该架构功能清晰,方便复制开发,模块化相对于之前有了质的飞越。

 

二、架构简图

       整个MVC的架构很简单,就三个csproj:

  1. PlatForm.WebApp.Infrastructure —— BIM平台的基础,用于定义基类、定义基本Config等等。该dll应该是以后很少改动的。
  2. PlatForm.WebApp.ApiBusiness  —— BIM平台中的ApiController中的Action引用该dll中的方法,该dll再引用之前CoreService.sln中相关的方法。
  3. PlatForm.WebApp —— BIM平台的前端(ASP.NET MVC)

        图示:

【.Net架构】BIM软件架构03:Web管控平台MVC架构

三、详解

       依照上图中的调用关系,我们首先来看Infrastructure它有哪些内容:

        a. Application文件夹:

        该文件夹包含Config文件夹与MvcApplication.cs。

        Config文件夹主要包含:BundleConfig(css、js的路由控制)、FilterConfig(过滤器控制)、RouteConfig(controller路由控制)、WebApiConfig(apicontroller路由控制)

        MvcApplication:

 public abstract class BaseMvcApplication : System.Web.HttpApplication
 {
        protected void Application_Start()
        {
            RegisterAreas();

            if (!ApplicationService.Instance.IsInitialized)
                ApplicationService.Instance.Initialize();

            IUnityContainer container = ServiceLocator.Instance.GetContainer();

            // WebApi
            GlobalConfiguration.Configuration.DependencyResolver = new Microsoft.Practices.Unity.WebApi.UnityDependencyResolver(container);

            // MVC
            DependencyResolver.SetResolver(new Microsoft.Practices.Unity.Mvc.UnityDependencyResolver(container));
           
            InitializeMvcFactory();

            RegisterApiConfig();
            RegisterGlobalFilter();
            RegisterRoutes();
            RegisterBundles();
        }

        protected virtual void RegisterAreas()
        {
            AreaRegistration.RegisterAllAreas();
        }

        protected virtual void RegisterBundles()
        {
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }

        protected virtual void RegisterRoutes()
        {
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }

        protected virtual void RegisterGlobalFilter()
        {
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        }

        protected virtual void RegisterApiConfig()
        {
            WebApiConfig.Register(GlobalConfiguration.Configuration);
        }
        
        protected virtual void InitializeMvcFactory()
        {
            // Customize MVC controller
            BIMPlatformControllerFactory controllerFactory = GetControllerFactory();
            ControllerBuilder.Current.SetControllerFactory(controllerFactory);

            BIMPlatformHttpControllerSelector httpControllerSelector = GetHttpControllerSelector();
            GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), httpControllerSelector);
        }

        protected virtual BIMPlatformHttpControllerSelector GetHttpControllerSelector()
        {
            // Customize API Controller
            return new BIMPlatformHttpControllerSelector(GlobalConfiguration.Configuration);
        }

        protected virtual BIMPlatformControllerFactory GetControllerFactory()
        {
            return new BIMPlatformControllerFactory();
        }        public override void Init()
        {
            this.PostAuthenticateRequest += (sender, e) => HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);

            base.Init();
        }

        /// <summary>
        /// Get the EF DB Context when begin the Http Request so that the application could use one context per request
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            //Avoid Error("Response for preflight has invalid HTTP status code 405") from H5 App
            if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
            {
                Response.End();
            }
        }

        /// <summary>
        /// Dispose the EF DB Context when begin the Http Request
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Application_EndRequest(object sender, EventArgs e)
        {
            try
            {
                // Singleton
                IEnumerable<IRepositoryContext> lcolContexts = ServiceLocator.Instance.GetServices<IRepositoryContext>();
                if (lcolContexts != null)
                {
                    foreach (IRepositoryContext lobjContext in lcolContexts)
                    {
                        ServerLogger.Info("Start to dispose DB context");
                        lobjContext.Dispose();
                        ServerLogger.Info("Disposed DB context");
                    }
                }
            }
            catch (Exception ex)
            {
                // Log error
                ServerLogger.Error("Failed to dispose DB Context", ex);
            }
        }
  }

       b. Controller文件夹:主要放置BaseController类,该类继承Controller类并根据项目进行自定义改写

       c. Filters文件夹:该文件夹主要放置一些自定义的Attribute类

       d. Integration文件夹:该文件夹中的类主要是继承DefaultControllerFactory、继承DefaultHttpControllerSelector、继承Attribute并根据项目进行自定义改写


 

       ApiBusiness 这个csproj不用多说,它就是纯写Apicontroller/Action,没啥花招,相应的api服务引用CoreService中相应的dll就可以了。

 


      

      WebApp 这个csproj是整个Web前端平台的关键,其使用Areas将各个功能进行模块化区分:

      【.Net架构】BIM软件架构03:Web管控平台MVC架构

     a. AppStart文件夹:主要重写BundleConfig与RouteConfig,供Global.asax进行覆盖使用。

public class BundleConfig
{
     public static void RegisterBundles(BundleCollection bundles)
     {
          #region layout
          bundles.Add(new ScriptBundle("~/Content/layout/js").Include(
                "~/Content/Common/plug-in/JQuery/jquery-2.1.0.js",
                 "~/Content/Common/plug-in/BootStrap/bootstrap.js",
                 "~/Content/Common/plug-in/SweetAlert/sweet-alert.js",
          ));
          bundles.Add(new StyleBundle("~/Content/layout/css").Include(
           "~/Content/Common/plug-in/BootStrap/bootstrap.css",
             "~/Content/Common/fonts/fontawesome/font-awesome.css",
             "~/Content/Common/plug-in/SweetAlert/sweet-alert.css",
             "~/Content/Layout/css/layout.css"
         ));
         #endregion
         ... ... 
     }

}
 public class RouteConfig
 {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            string defaultsAppSettings = ConfigurationManager.AppSettings["DefaultPage"];
            string[] defaultArray = defaultsAppSettings.Split('/');

            routes.MapRoute(
                name: "login_default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = defaultArray[0], action = defaultArray[1], id = UrlParameter.Optional },
                namespaces: new string[] { "Platform.WebApp.Controllers" }
            );
        }
 }
public class MvcApplication : BaseMvcApplication
{
        protected override void RegisterBundles()
        {
            base.RegisterBundles();
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
}

     b. Content文件夹,这个不用多说,一张图就懂了,主要放置全局性的前端文件:

    【.Net架构】BIM软件架构03:Web管控平台MVC架构

    c. Controllers与Views文件夹:只放关于Home与Account相关的东西,注意Controller中的命名空间必须为:Platform.WebApp.Controllers

    d. Areas文件夹:放置各个模块。假设有一个模块是BIMModel,其架构如下:

    【.Net架构】BIM软件架构03:Web管控平台MVC架构

   其关键在于AreaRegistration类:

public class BIMModleAreaRegistration : AreaRegistration
{
        public override string AreaName
        {
            get { return "BIMModel"; }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "bimmodel_default",
                "bimmodel/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional },
                new string[] { "Platform.WebApp.Areas.BIMModel.Controllers" }
            );
       }
}

   注意其controller路由为"bimmodel/{controller}/{action}/{id}",其Controller的命名空间为"Platform.WebApp.Areas.BIMModel.Controllers"

 

四、总结

       经过一番折腾,BIM平台就这样可以搭建起来了,当然要搞的好还是需要很多功夫的。

 

五、后记

       这个月7号就要去新公司报到了。在找下家的时候,按照惯例HR总会问你为什么要换工作?我的答案大概和很多人一样:可爱的领导与同事都走了,钱也没给够。我原来的公司大领导和各小组领导都是非常可爱的人,他们从一个公司出来一起创业,把自己知道的所有技能都毫无保留的教授给新人,整个公司欣欣向荣,团结一致。

       但是被完全收购之后却是另一番景象,集团空降领导,外行指挥内行,军心涣散,原来的大领导也被架空出走。空降大领导画大饼可厉害了,开会的时候喊出:“三年之后咱分公司独立上市”,真是想起了无间道里的 “说好了三年,三年之后又三年,三年之后又三年,都快十年了,老大!”。空降大领导想搞大事还不肯加大薪资投入,各组核心都纷纷离开公司,连刚进几个月的实习都想着离开,各小组领导们也很伤心。

       领导们都是很可爱的人,和我吃中饭的时候他们对我说到,看到咱公司培养出的怪物们都去了非常棒的企业,他们也很欣慰,圈子很小,以后合作机会还多,记得回来看看!

       祝福老东家,希望领导们与同事们都能一切顺利!

       进入新公司后自己一个人负责一个产品,重新架构一个BIM软件,也祝福自己,希望努力工作,带给家人幸福!