Asp.Net开发架构设计(二)
上回说到,我们配置了一下UnityConfig层,在这个层中定义了一个IContainerAccessor的接口和一个返回IUnityContainer类型的方法,这个方法的主要作用就是把Service层中的接口类和Business层中的接口实现类装配到UnityContainer中并返回,也就是指定那个接口实现类去实现某个接口类(晕,好像有点绕口啊)。
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赋值给实现接口的全局静态属性。
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方法注入到页面中去。
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的泛型基类。
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控件上,具体代码如下:
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
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标记的方法,如下
[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中。如下
<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的方法我用了很久了,本来这次是写架构设计的,给扯到这上面来了,也许这也算是架构设计的一部分吧。
运行效果如下: