上回说到 ,我们配置了一下UnityConfig层,在这个层中定义了一个IContainerAccessor的接口和一个返回IUnityContainer类型的方法,这个方法的主要作用就是把Service层中的接口类和Business层中的接口实现类装配到UnityContainer中并返回,也就是指定那个接口实现类去实现某个接口类(晕,好像有点绕口啊)。
Xiaozhuang.UnityConfig
namespace Xiaozhuang.UnityConfig { public interface IContainerAccessor { IUnityContainer Container { get ; } } public class UnityContainerConfig { public IUnityContainer GetIUnityContainer() { IUnityContainer container = new UnityContainer(); container.RegisterType < IQueryEmployeeService, QueryEmployeeBusiness > (); return container; } } }
好了,现在终于轮到Web层了,要实现在Asp.Net页面中直接调用能够服务接口而不用从Unity容器中再去取出来,就要把Unity容器中的接口注入到页面中去,分两步走:第一步,在Global.Asax.cs中实现UnityConfig层中的IContainerAccessor接口,并把UnityConfig层返回的IUnityContainer赋值给实现接口的全局静态属性。
Code
namespace Xiaozhuang.Web { public class Global : System.Web.HttpApplication, IContainerAccessor { Members #region Members private static IUnityContainer _container; #endregion Properties #region Properties /**/ /// <summary> /// The Unity container for the current application /// </summary> public static IUnityContainer Container { get { return _container; } set { _container = value; } } #endregion IContainerAccessor Members #region IContainerAccessor Members /**/ /// <summary> /// Returns the Unity container of the application /// </summary> IUnityContainer IContainerAccessor.Container { get { return Container; } } #endregion Application Events #region Application Events protected void Application_Start( object sender, EventArgs e) { BuildContainer(); } protected void Session_Start( object sender, EventArgs e) { } protected void Application_BeginRequest( object sender, EventArgs e) { } protected void Application_AuthenticateRequest( object sender, EventArgs e) { } protected void Application_Error( object sender, EventArgs e) { } protected void Session_End( object sender, EventArgs e) { } protected void Application_End( object sender, EventArgs e) { CleanUp(); } #endregion Methods #region Methods private static void BuildContainer() { UnityContainerConfig config = new UnityContainerConfig(); Container = config.GetIUnityContainer(); } private static void CleanUp() { if (Container != null ) { Container.Dispose(); } } #endregion } }
接下来要把UnityContainer中的接口注入到页面中去。建立一个BasePage的泛型类,先获取到从Gloab.Asax传过来的应用程序实例,转化为UnityContainer,利用BuildUp方法注入到页面中去。
BasePage
namespace Foresee.Web { public abstract class BasePage < T > : Page where T : class { protected override void OnPreInit(EventArgs e) { InjectDependencies(); base .OnPreInit(e); } protected virtual void InjectDependencies() { var context = HttpContext.Current; if (context == null ) { ClientScript.RegisterClientScriptBlock( this .GetType(), " context " , " <script>alert('当前Http上下文为空,请与系统管理员联系!');</script> " ); } var accessor = context.ApplicationInstance as IContainerAccessor; if (accessor == null ) { ClientScript.RegisterClientScriptBlock( this .GetType(), " context " , " <script>alert('当前应用程序实例为空,请与系统管理员联系!');</script> " ); } var container = accessor.Container; if (container == null ) { ClientScript.RegisterClientScriptBlock( this .GetType(), " context " , " <script>alert('未找到依赖注入容器,请与系统管理员联系!');</script> " ); } container.BuildUp( this as T); } } }
我们不止在页面中要调用接口,也要在UserControl中调用,那么我们就参照上面的页面基类建立一个UserControl的泛型基类。
BaseUserControl
namespace Foresee.Web { public abstract class BaseUserControl < T > : UserControl where T : class { protected override void OnInit(EventArgs e) { InjectDependencies(); base .OnInit(e); } protected virtual void InjectDependencies() { var context = HttpContext.Current; if (context == null ) { this .Page.ClientScript.RegisterClientScriptBlock( this .Page.GetType(), " context " , " <script>alert('当前Http上下文为空,请与系统管理员联系!');</script> " ); } var accessor = context.ApplicationInstance as IContainerAccessor; if (accessor == null ) { this .Page.ClientScript.RegisterClientScriptBlock( this .Page.GetType(), " context " , " <script>alert('当前应用程序实例为空,请与系统管理员联系!');</script> " ); } var container = accessor.Container; if (container == null ) { this .Page.ClientScript.RegisterClientScriptBlock( this .Page.GetType(), " context " , " <script>alert('未找到依赖注入容器,请与系统管理员联系!');</script> " ); } container.BuildUp( this as T); } } }
接下来我们建立一个UserControl文件,在里面调用查询雇员的服务接口,并绑定到ListView控件上,具体代码如下:
Code
namespace Xiaozhuang.Web { public partial class EmployeeList : BaseUserControl < EmployeeList > { #region Properties [Dependency] public IQueryEmployeeService instance { set ; get ; } public QueryEntry queryentry { set ; get ; } #endregion protected void Page_Load( object sender, EventArgs e) { if ( ! IsPostBack) { try { ListView1.DataSource = instance.QueryEmployee(queryentry); ListView1.DataBind(); } catch { Response.Write( " 系统运行错误,请与管理员联系! " ); } } } } }
这个EmployeeList继承自BaseUserControl<T>.UserControl基类,这样这个用户控件就可以实现注入了,我们只需要在属性上增加Dependency标记就可以用属性注入的方式来调用接口方法,当然你也可以通过方法注入的方式来实现。 接下来我们要用Asp.net Ajax调用这个UserControl来生成HTML,给页面上使用,我们先建立两个类ControlPage和ViewManager
Code
namespace Xiaozhuang.Web { public class ControlPage : Page { public override void VerifyRenderingInServerForm(Control control) { // base.VerifyRenderingInServerForm(control); } } } namespace Xiaozhuang.Web { /// <summary> /// A generic user control rendering helper, basically you initialise the view manager and /// call render to render that control, but the benifit of this version is you can access the control /// the view manager is rendering and can set custom properties etc. /// </summary> /// <typeparam name="T"> The type of the control you are rendering </typeparam> public class ViewManager < T > where T : Control { #region Properties private T _control = default (T); /// <summary> /// Gives you access to the control you are rendering allows /// you to set custom properties etc. /// </summary> public T Control { get { return _control; } } // Used as a placeholder page to render the control on. private ControlPage _holder = null ; #endregion #region Constructor /// <summary> /// Default constructor for this view manager, pass in the path for the control /// that this view manager is render. /// </summary> /// <param name="inPath"></param> public ViewManager( string path) { // Init the holder page _holder = new ControlPage(); // Create an instance of our control _control = (T)_holder.LoadControl(path); // Add it to our holder page. _holder.Controls.Add(_control); } #endregion #region Rendering /// <summary> /// Renders the current control. /// </summary> /// <returns></returns> public string Render() { StringWriter sw = new StringWriter(); // Execute the page capturing the output in the stringwriter. HttpContext.Current.Server.Execute(_holder, sw, false ); // Return the output. return sw.ToString(); } #endregion } }
ControlPage类是一个简单的继承Page的类,里面重载VerifyRenderingInServerForm方法的作用是防止在UserControl生成HTML的时候如果UserControl中有服务器控件而出现的“服务器控件必须放在Form ruanat=‘server’”的错误!ViewManager类的作用是把在服务器端UserControl装在ControlPage页面中用Excute方法执行一遍并用Render方法获取到执行后输出的HTML字符串。 接下来我们到页面中去,在页面类中建立一个输出HTML的静态带WebService标记的方法,如下
Code
[WebMethod()] public static string GetDataPage( int page, string departmentID, string EmpName, string EmpAge) { // Create an instance of our viewmanager. ViewManager < EmployeeList > man = new ViewManager < EmployeeList > ( " ~/EmployeeList.ascx " ); QueryEntry queryentry = new QueryEntry(); queryentry.DepartmentID = departmentID; queryentry.EmployeeName = EmpName; queryentry.EmployeeAge = EmpAge; man.Control.queryentry = queryentry; return man.Render(); }
这个方法的作用是吧查询的参数传递给EmployeeList用户控件,通过ViewManager执行并输出HTML字符串,在Aspx页面中用Asp.Net Ajax代码来调用这个方法,并把返回的html填充到相应的Div中。如下
Code
< script type = " text/javascript " > var currentPage = 0 ; function LoadPage(page) { var departmentID = document.getElementById( " txtDept " ).value; var empName = document.getElementById( " txtName " ).value; var empAge = document.getElementById( " txtAge " ).value; PageMethods.GetDataPage(page,departmentID,empName,empAge, function (result) { // We loaded our data populate our div. document.getElementById( " DivContent " ).innerHTML = result; }, function (error) { alert(error.get_message()); }); } Sys.Application.add_load(LoadPage); < / script>
至此写完,其实这个生成html的方法我用了很久了,本来这次是写架构设计的,给扯到这上面来了,也许这也算是架构设计的一部分吧。 运行效果如下: