MVC开发模式下的WebApp架构尝试

第一次尝试搭建项目架构,当时遇到的情况是这样,公司搞了一个webapp的项目,这个项目会随着版本更新多出很多新的模块,但是项目代码本身初期设计的时候没有考虑到版本更新的问题,于是就碰到了一旦发布的版本遇到了重大的BUG,开发机上的项目不能直接更新Bin目录下的DLL,因为开发机上的Bin包含了下个版本需要发布的内容,会与现在版本的冲突,其实就是UI和BO层的耦合度太高了。于是接到的第二个webapp项目就要求能够切断这种耦合,任务就交到了我的手上。

任务开始,头脑风暴时刻

当初设想的方案有以下几个

  1. 在站点下面根据版本建立虚拟目录,用VSS控制版本的代码。乍一听之下,这个办法挺好的,完全可以满足需求,但是站在开发者角度一想,如果发布了10个版本,发现第一个版本里的一个DAO或者BO或者随便哪里的代码有巨大漏洞,尼玛我这得改10个地方,虽然在小项目中几率比较小,但是作为攻城狮我得避免这个情况
  2. 用类似于webpart的方式进行配件式的开发。这个已经可以满足需求了,但是想想,我们做的这个是展示类的网站,如果要划分成配件式的,得有20来个,每个配件的BO和DAO其实都是一样的架构方式,但是要搞20多个,工作量有点大啊,只能再想想
  3. 搭一套全新的架构,自己想办法完成这个需求。。。哎~~缺点很明显,没人搭过架构,谁知道搭出来的好不好用啊,但是项目经理下了决心要有一套自己的架构,那就动工吧

需求分析

  1. 架构要能做到根据版本号的不同返回不同的View,调用不同的Bo
  2. 每个调用的Bo要做到可配置,如果新上的Bo出现问题,可以不用发布Bin修改配置改用老的Bo方法

架构设想

其实也没什么设想,听到需求已经能肯定了,简单的依赖注入就可以解决,但是依赖注入我也没怎么玩过,网上查查看到的大多不能完全满足需求,算了自己写,我决定用反射去实现注入,虽然效率可能低了点,但是无所谓啊~如果效率低,我再发反射实现的方法改成工厂返回的那种模式就行了嘛

下面是我实现的代码

首先是分层,既然要切断BO与UI的耦合,肯定要定义一个BO的接口层,然后是BO

 然后,我先写了一个BaseController

  1. 重写了View()和View(Object Model)这2个方法,每次retrun View的时候自动获取到配置中具体View的Name当然可能将来还得多重写几个

 

public new ActionResult View()
        {
            return base.View(GetViewName(RouteData.Values["Controller"].ToString(), RouteData.Values["Action"].ToString()));
        }

        public new ActionResult View(Object Model)
        {
            return base.View(GetViewName(RouteData.Values["Controller"].ToString(), RouteData.Values["Action"].ToString()),Model);
        }

  2.写了一个方法将Request中的参数序列化为对象,因为我的UI层中不准备写任何的逻辑,否则版本没法控制了

 /// <summary>
        /// 获取所有参数集合
        /// </summary>
        /// <returns></returns>
        public RequestParameter GetRequestParameter()
        {
            RequestParameter rtn=new RequestParameter();
            Dictionary<string, string> PostDic = new Dictionary<string, string>();
            if (Request.Form != null)
            {
                foreach (string key in Request.Form)
                {
                    PostDic.Add(key, Request.QueryString[key]);
                }
            }
            rtn.PostParameter = PostDic;

            Dictionary<string, string> GetDic = new Dictionary<string, string>();
            if (Request.QueryString != null)
            {

                foreach (string key in Request.QueryString)
                {
                    GetDic.Add(key, Request.QueryString[key]);
                }
            }
            rtn.GetParameter = GetDic;

            Dictionary<string, RequestFile> FileDic = new Dictionary<string, RequestFile>();
            for (int i = 0; i < this.Request.Files.Count; i++)
            {
                RequestFile _file = new RequestFile();
                _file.FileName = this.Request.Files[i].FileName;
                _file.ContentType = this.Request.Files[i].ContentType;
                _file.ContentLength = this.Request.Files[i].ContentLength;
                _file.FileStream = this.Request.Files[i].InputStream;
            }
            rtn.FileParameter = FileDic;
            return rtn;
        }

  3.反射的方法用于根据版本号获取对应的BO

 /// <summary>
        /// 获取指定名称的BLL对象
        /// </summary>
        /// <param name="ControllerName"></param>
        /// <returns></returns>
        public Object GetBll()
        {
            string ControllerName = RouteData.Values["Controller"].ToString();
            string Path = ConfigurationManager.AppSettings["BllXmlConfigPath"] + BllConfigXmlName;
            var query = from a in XElement.Load(Path).Elements()
                        where a.Attribute("Controller").Value == ControllerName
                        select new BoConfig
                            {
                                Controller = a.Attribute("Controller").Value,
                                BllName = a.Attribute("BllName").Value
                            };
            Assembly assembly = Assembly.Load("BLL");
            return assembly.CreateInstance(query.First().BllName);
        }

        /// <summary>
        /// 获取指定名称的BLL对象
        /// </summary>
        /// <param name="ControllerName"></param>
        /// <returns></returns>
        public Object GetBll(string ControllerName)
        {
            string Path = ConfigurationManager.AppSettings["BllXmlConfigPath"] + BllConfigXmlName;
            var query = from a in XElement.Load(Path).Elements()
                        where a.Attribute("Controller").Value == ControllerName
                        select new BoConfig
                        {
                            Controller = a.Attribute("Controller").Value,
                            BllName = a.Attribute("BllName").Value
                        };
            Assembly assembly = Assembly.Load("BLL");
            return assembly.CreateInstance(query.First().BllName);
        }

  下面是我2个配置的XML格式

<?xml version="1.0" encoding="utf-8" ?>
<Root>
  <Config Controller="Home" BllName="BLL.HomeBll10"/>
</Root>

<?xml version="1.0" encoding="utf-8" ?>
<Root>
  <Config Controller="Home" BllName="BLL.HomeBll10"/>
</Root>

  最后就是Controller里如何使用了

public class HomeController : BaseController
    {
        //
        // GET: /Home/
        [RequestLog(ViewName = "公共自行车", Action = SysEnum.ActionType.Retrieve, Module = "交通出行")]//访问日志记录
        [UserLoginRole]//登录拦截
        public ActionResult Index()
        {
            //传入Controller的名称获取对应的BLL对象
            IBLL.IHome BLL = (IBLL.IHome)this.GetBll();
            ViewBag.Ttt= BLL.Test(this.GetRequestParameter())+BLL.Test2();
            return View("111");
        }

 

 

 

UI层介绍完毕之后是我BO曾的实现

以HomeController调用的BO为例,首先是一个BaseHomeBo,这个类实现IBO的接口所有方法,但是全部都以抛出异常为结尾

using IBLL;

namespace BLL
{
    public class BaseHomeBll:IHome
    {

        public virtual string Test(COMMON.RequestParameter parameter)
        {
            throw new System.NotImplementedException();
        }

        public virtual string Test2()
        {
            throw new System.NotImplementedException();
        }
    }
}

接着根据版本,每个版本一个类,继承上一个版本的类,这里我写了10和20两个版本的类

namespace BLL
{
    public class HomeBll10:BaseHomeBll
    {
        public override string Test(COMMON.RequestParameter parameter)
        {
            return "111";
        }

        public override string Test2()
        {
            return "222";
        } 
    }
}

  

namespace BLL
{
    public class HomeBll20:HomeBll10
    {
        public override string Test(COMMON.RequestParameter parameter)
        {
            return "333";
        }
    }
}

很简单,高版本重写低版本内容,没有重写就是以前版本的内容,就是一个多态的策略模式

 

 

 

好了结束了,请各位大牛指点下,这个模式还能优化吗?

 

 

听了大神的建议,修改了一些内容

对BO的反射进行了缓存

 /// <summary>
        /// 获取指定名称的BLL对象
        /// </summary>
        /// <param name="ControllerName"></param>
        /// <returns></returns>
        public Object GetBll(string ControllerName)
        {
            Cache cache = HttpRuntime.Cache;

            if (cache.Get("System_BoCache_" + ControllerName + "_" + CurrentUserVersion) == null && !IsBoCache)
            {
                string Path = BllXmlConfigPath + BllConfigXmlName;
                var query = from a in XElement.Load(Path).Elements()
                            where a.Attribute("Controller").Value == ControllerName
                            select new BoConfig
                                {
                                    Controller = a.Attribute("Controller").Value,
                                    BllName = a.Attribute("BllName").Value
                                };
                Assembly assembly = Assembly.LoadFile(BoPath);
                object obj = assembly.CreateInstance(query.First().BllName);
                cache.Insert("System_BoCache_" + ControllerName + "_" + CurrentUserVersion, obj, null, DateTime.Now.AddHours(1),
                             TimeSpan.Zero);
                return obj;
            }
            else
            {
                return cache["System_BoCache_" + ControllerName + "_" + CurrentUserVersion];
            }
        }

  

posted on 2015-03-04 10:35  !!!!!!!!!!!!!!!  阅读(595)  评论(0)    收藏  举报

导航