简介
{A}讨论,应用程序的开发涉及到独立的应用程序类型的应用程序的类型和任务具体的任务。这是显著的任务分离,在处理策略,部署策略,测试策略,UI战略等基础与应用程序类型相关的特定技术,不仅帮助你决定,而且还帮助您解决应用型独立,又名常见的,如设计/扩展组件,系统和解决横切关注的任务。一些指南讨论这些常见任务的发展。动态的装饰是用来解决这些任务,按照这些准则。一个WinForms应用程序是用来作为一个例子。
在这篇文章中,我演示了如何的原则和准则,适用于不同的应用程序类型 - MVC / AJAX / REST应用程序 - 通过使用动态的装饰。选择应用程序类型
有NET世界中的几个应用程序类型的WinForms,ASP.NET,ASP.NET MVC的,和Silverlight。一个WinForms应用程序提供了丰富的的用户交互和客户端的处理能力。 ASP.NET应用程序已通过网络部署和维护的优势。 ASP.NET MVC提供了可测性,加上ASP.NET的优势。 Silverlight应用程序提供了一个丰富的用户界面,通过Web。不同类型的应用有不同的技术细节,可能会或可能不会满足你的要求。你应该选择你的应用程序类型的基础上处理策略,部署策略,维护策略,测试策略,UI战略等
此外,AJAX技术可以用来提高Web应用程序(ASP.NET,ASP.NET MVC,或Silverlight),以创造更多的互动的用户体验。
不同的应用类型也有不同的应用程序的编程模型,UI元素,状态管理,事件处理模型,等您需要解决相关的应用程序类型的任务,一旦你选择为您的应用程序的应用程序类型。解决共同任务
除了特定应用程序类型的任务,有独立的应用程序开发过程中的一个特定的应用程序类型的任务。不管应用程序类型,例如,你面对像设计,扩展组件,解决横切关注点的任务。他们是为所有应用程序类型的共同。
一套原则,给出了这些常见任务的发展{A}的文章。他们在这里列出如下:设计组件,以满足业务需求的一般方法作为全球方法的设计方面,在自己的模块添加方面需要的对象扩展需要的对象
请参阅{A}的有关这些原则的讨论的细节。您可能还需要阅读的文章理解方面编程使用动态装饰。范例
在下面的章节,讨论一个示例应用程序演示如何应用上述原则一个MVC / AJAX / REST应用程序通过使用动态的装饰。
讨论{A}为例这里使用的是同样的问题。这一次,我们选择一个应用程序的类型,而不是一个WinForms应用程序的MVC / AJAX / REST应用程序。为方便起见,我的状态问题如下。问题
显示的基础上选择一个部门的员工。组件
假设有两个组成部分,Employee和Department。对于雇员而言,还有相应的RepositoryEmployee组件包含一个Employee对象的集合。处,有一个相应的RepositoryDepartment组件包含一个处的对象的集合。这些组件的代码列出如下。
{C}public interface IEmployee
{
System.Int32? EmployeeID { get; set; }
System.String FirstName { get; set; }
System.String LastName { get; set; }
System.DateTime DateOfBirth { get; set; }
System.Int32? DepartmentID { get; set; }
System.String FullName();
System.Single Salary();
}
public class Employee : IEmployee
{
#region Properties
public System.Int32? EmployeeID { get; set; }
public System.String FirstName { get; set; }
public System.String LastName { get; set; }
public System.DateTime DateOfBirth { get; set; }
public System.Int32? DepartmentID { get; set; }
#endregion
public Employee(
System.Int32? employeeid
, System.String firstname
, System.String lastname
, System.DateTime bDay
, System.Int32? departmentID
)
{
this.EmployeeID = employeeid;
this.FirstName = firstname;
this.LastName = lastname;
this.DateOfBirth = bDay;
this.DepartmentID = departmentID;
}
public Employee() { }
public System.String FullName()
{
System.String s = FirstName + " " + LastName;
return s;
}
public System.Single Salary()
{
System.Single i = 10000.12f;
return i;
}
}
在这个应用程序,员工和部门的数据是硬编码在两个列表来简化我们的讨论。在一个真实的世界应用程序,这些数据通常保存在关系数据库中。然后,您将需要创建一个数据层来检索,并把它们在列表中。
值得注意的是,雇员名单是由插入顺序填充,没有任何一种排序到位。这是很难预测什么类型的排序此组件的支持,在这个时候。在实际应用中,一个组件对象可能需要按姓氏排序。另一种可能需要排序诞生的那一天。第三个可能不需要排序。因此,最好是推迟执行排序,直到组件是应用程序中使用。通过有关排序的有关组件RepositoryEmployee设计,原理",设计组件,以满足一般wayquot的业务需求;其次。通过这种方式,组件是稳定和封闭的。
HRMVCAjax
通常情况下,从Web应用程序刷新整个页面的响应。例如,选择一个部门时,整个页面被刷新,以显示与选定部门的相关员工。这是更可取的,只有一部分页面,employee表中,选择一个部门时被刷新。使用AJAX,这样就可以实现。
AJAX是一种在Web应用程序的客户端技术。您可以申请AJAX的Web应用程序的不同类型:ASP.NET,ASP.NET MVC,PHP等,对于这个例子,我们选择它作为我们的web应用程序的ASP.NET MVC的。此外,您在实现Ajax的几个选择:对于我们的例子的原生JavaScript,ASP.NET AJAX框架中,jQuery等,我们可以使用jQuery。最后,你必须决定如何为AJAX请求。对于这个例子,我们选择WCF REST服务,为AJAX请求。
HRMVCAjax是一个ASP.NET MVC应用程序中使用上述组件来显示的基础上选择一个部门的员工。因为它是一个ASP.NET MVC应用程序,它遵循ASP.NET MVC的应用程序编程模型和事件模型。控制器代码列示如下:public class DepEmployeesController : Controller
{
private IRepository<IEmployee> rpEmployee = null;
private IRepository<IDepartment> rpDepartment = null;
public DepEmployeesController()
{
rpEmployee = new RepositoryEmployee();
rpDepartment = new RepositoryDepartment();
}
public ActionResult Index()
{
rpDepartment.GetAll();
rpEmployee.GetAll();
List<SelectListItem> depList = new List<SelectListItem>();
SelectListItem sli = null;
sli = new SelectListItem();
sli.Value = "";
sli.Text = "";
depList.Add(sli);
foreach (IDepartment d in rpDepartment.RepList)
{
sli = new SelectListItem();
sli.Value = d.DepartmentID.Value.ToString();
sli.Text = d.Name;
depList.Add(sli);
}
ViewData["depSel"] =
new SelectList(depList, "Value", "Text");
return View(rpEmployee.RepList);
}
}
视图代码如下:<%@ Page Title="" Language="C#"
MasterPageFile=" /Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% Html.BeginForm("Index", "DepEmployees"); %>
<span>Department</span><br />
<%= Html.DropDownList("depSel")%>
<br /> <br />
<span>Employees</span><br />
<div id="grid">
<table cellspacing="0" rules="all" border="1"
id="GridView1" style="border-collapse:collapse;">
<tr>
<th>EmployeeID</th><th>FirstName</th>
<th>LastName</th><th>DateOfBirth</th><th>DepartmentID</th>
</tr>
<% foreach (var itm in Model) { %>
<tr>
<td><%= itm.EmployeeID %></td><td><%= itm.FirstName%></td>
<td><%= itm.LastName%></td><td><%= itm.DateOfBirth%></td>
<td><%= itm.DepartmentID%></td>
</tr>
<% } %>
</table>
</div>
<% Html.EndForm(); %>
<script type='text/javascript'>
$(document).ready(function () {
$("select").change(function () {
$.get("HRRestService?dep=" + $(this).val(), function (data) {
$("#grid").html(
'<table cellspacing="0" rules="all" border="1" id="emp"
style="border-collapse:collapse;">' +
'<tr>' +
'<th>EmployeeID</th><th>FirstName</th><th>LastName</th>
<th>DateOfBirth</th><th>DepartmentID</th>' +
'</tr>');
$(data).find("Employee").each(function() {
$("#emp").append(
'<tr>' +
'<td>' + $(this).find("EmployeeID").text() + '</td><td>' +
$(this).find("FirstName").text() + '</td><td>' +
$(this).find("LastName").text() + '</td><td>' +
$(this).find("DateOfBirth").text() + '</td><td>' +
$(this).find("DepartmentID").text() + '</td>' +
'</tr>'
);
});
$("#grid").append('</table>');
});
});
})
</script>
</asp:Content>
注意以上的JavaScript代码写在jQuery。当选择一个部门,一个AJAX请求被发送到服务HRRestService选定的部门ID作为查询字符串。服务返回的XML数据文件,其中包含与选定部门的相关员工的名单。回调函数解析XML数据,并使用它来更新页面中的雇员表。
HRRestService服务的代码如下:[ServiceContract]
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class HRRestService
{
private static int iStaticDep = 0;
[WebGet(UriTemplate = "?dep={dep}")]
public List<Employee> EmployeesByDepartment(string dep)
{
IRepository<IEmployee> rpEmployee = null;
IRepository<IDepartment> rpDepartment = null;
rpEmployee = new RepositoryEmployee();
rpDepartment = new RepositoryDepartment();
rpDepartment.GetAll();
rpEmployee.GetAll();
IDepartment dpSel = rpDepartment.RepList[Convert.ToInt16(dep) - 1];
iStaticDep = dpSel.DepartmentID.Value;
List<IEmployee> empSel = null;
if (rpEmployee.RepList != null)
{
empSel = rpEmployee.RepList.FindAll(
(IEmployee emp) => { return emp.DepartmentID.Value == iStaticDep; });
}
List<Employee> empObj = new List<Employee>();
foreach (IEmployee i in empSel)
{
empObj.Add((Employee)i);
}
return empObj;
}
}
这是一个WCF REST服务。它接受一个部门ID的员工名单,并返回到客户端。
当浏览器第一次访问应用程序,MVC控制器创建两个部门的清单和雇员表,并将响应返回给客户端。然后,选择一个部门时,jQuery代码使一个AJAX请求,与选定的部门ID的REST服务。一旦服务响应,回调函数会刷新employee表显示选定部门的员工。
当它运行时,所有的员工都显示如下:
{S0}
选择当一个部门,该部门的员工都显示。
{S1}
请注意,只有在雇员表刷新而不是部门下拉控件,因此仍然突出。排序的员工列表
现在,让我们说你想要的对象rpEmployee,RepositoryEmployee组件的一个实例,有员工按姓氏排序的功能。下面是你需要做的的。
首先,你创建一个排序比较器类,如下:internal class EmployeeLastnameComparer : IComparer<IEmployee>
{
public int Compare(IEmployee e1, IEmployee e2)
{
return String.Compare(e1.LastName, e2.LastName);
}
}
然后,调用之前rpEmployee.GetAll(DepEmployeesController行动指数)的动态装饰,具体如下:rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
rpEmployee,
new String[] { "GetAll" },
null,
new Decoration((x, y) =>
{
object target = x.Target;
if (target.GetType().ToString() == "ThirdPartyHR.RepositoryEmployee")
{
List<IEmployee> emps = ((IRepository<IEmployee>)target).RepList;
IEnumerable<IEmployee> query = emps.OrderByDescending(emp => emp,
new EmployeeLastnameComparer()).ToList<IEmployee>();
((IRepository<IEmployee>)target).RepList = (List<IEmployee>)query;
}
}, null));
上面的代码需要被应用之前rpEmployee.GetAll()的HRRestService EmployeesByDepartment方法。
就是这样。现在,您的HRMVCAjax显示按姓氏排序的员工。建立并运行它。你会看到,显示雇员和他们的姓氏下令,如下。
{S2}
当你选择一个部门,与本部门有关的的雇员,显示姓氏下令。
{S3}
注意,Lambda表达式是用来提供一个匿名方法,为这名员工库对象添加排序功能。当然,你可以使用正常的方法排序逻辑。然而,由于这个排序逻辑,特别是对雇员库对象rpEmployee,而不是由其他对象共享,它是保持在一个匿名方法更简洁。
这里值得注意的有几点。首先,原则quot;扩展为neededquot对象,其次是。当我们设计的组件RepositoryEmployee,排序要求尚不清楚。我们使用的应用程序中的对象rpEmployee的时间,很显然,我们需要员工的姓来排序的员工列表。所以,我们这个对象进行排序的员工列表员工的姓。二,排序功能,而无需修改其组件或从它派生的对象rpEmployee。三,对象rpEmployee是一个RepositoryEmployee组件,它具有排序功能,独立于其他实例RepositoryEmployee创建只有一个实例。设计方面的问题
说,你希望你的HRMVCAjax应用程序,以解决横切关注点的进入/退出记录和安全检查。通过以下原则quot;作为全球在他们自己的modulesquot的方法设计方面;,这些方面都提出作为单独的公共方法的类SysConcerns,并装在自己的模块。以下是对这些关注的代码。public class SysConcerns
{
public static void EnterLog(AspectContext ctx, object[] parameters)
{
StackTrace st = new StackTrace(new StackFrame(4, true));
Console.Write(st.ToString());
IMethodCallMessage method = ctx.CallCtx;
string str = "Entering " + ctx.Target.GetType().ToString() +
"." + method.MethodName + "(";
int i = 0;
foreach (object o in method.Args)
{
if (i > 0)
str = str + ", ";
str = str + o.ToString();
}
str = str + ")";
Console.WriteLine(str);
Console.Out.Flush();
}
public static void ExitLog(AspectContext ctx, object[] parameters)
{
IMethodCallMessage method = ctx.CallCtx;
string str = "Exiting " + ctx.Target.GetType().ToString() +
"." + method.MethodName + "(";
int i = 0;
foreach (object o in method.Args)
{
if (i > 0)
str = str + ", ";
str = str + o.ToString();
}
str = str + ")";
Console.WriteLine(str);
Console.Out.Flush();
}
public static void AdminCheck(AspectContext ctx, object[] parameters)
{
Console.WriteLine("Has right to call");
return;
}
}
EnterLog写入进入日志而ExitLog退出日志写入。 AdminCheck写入日志并返回。
您可能需要修改这些方法,您的系统要求的基础上。您还可以增强他们的背景下,目标,和输入参数的访问各种信息。要看到的背景下,目标,并输入参数如何用来增强你的方面,请参阅{A5}。使用方面
定义的使用方面,您可以将它们添加到应用程序所需的的对象。
说你要添加的安全检查方面,前调用组件RepositoryDepartment库对象rpDepartment getAll方法。你也想进入登录和退出日志添加到同一个对象。您添加的DepEmployeesController方法指数前rpDepartment.GetAll下面的代码的权利()。
然后,假设您要添加登录进入和退出日志RepositoryEmployee组件对象rpEmployee getAll方法,只是之前rpEmployee.GetAll插入以下代码(DepEmployeesController方法指数)。rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
rpEmployee,
new String[] { "GetAll" },
new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
new Decoration(new DecorationDelegate(SysConcerns.ExitLog), null));
最后,假设您要跟踪访问是由哪个部门,你可以使用前部组件iStaticDep = dpSel.DepartmentID.Value选定的对象dpSel部门ID属性只是添加下面的代码在HRRestService方法EmployeesByDepartment 。dpSel = (IDepartment)ObjectProxyFactory.CreateProxy(
dpSel,
new String[] { "get_DepartmentID" },
new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
null);
现在,横切关注点处理的HRMVCAjax应用程序。
注意的方面是在需要时添加到对象。有没有在组件类的变化。而只有与动态装饰装修的对象方面,独立的组件类的其他对象。另外一个方面可以适用于不同的对象,是同类型或不同类型的。例如,SysConcerns.EnterLog rpDepartment(RepositoryDepartment对象),rpEmployee(RepositoryEmployee对象),并dpSel(处对象)。HRMVCAjax扩展
,由于所有的新功能(排序和方面)是MVC控制器或WCF REST服务添加到服务器端,客户端jQuery代码没有变化。
为了方便,DepEmployeesController后扩展代码列出如下:public class DepEmployeesController : Controller
{
internal class EmployeeLastnameComparer : IComparer<IEmployee>
{
public int Compare(IEmployee e1, IEmployee e2)
{
return String.Compare(e1.LastName, e2.LastName);
}
}
private IRepository<IEmployee> rpEmployee = null;
private IRepository<IDepartment> rpDepartment = null;
public DepEmployeesController()
{
rpEmployee = new RepositoryEmployee();
rpDepartment = new RepositoryDepartment();
}
public ActionResult Index()
{
rpDepartment = (IRepository<IDepartment>)ObjectProxyFactory.CreateProxy(
rpDepartment,
new String[] { "GetAll" },
new Decoration(new DecorationDelegate(SysConcerns.AdminCheck),
new object[] { Thread.CurrentPrincipal }),
null);
rpDepartment = (IRepository<IDepartment>)ObjectProxyFactory.CreateProxy(
rpDepartment,
new String[] { "GetAll" },
new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
new Decoration(new DecorationDelegate(SysConcerns.ExitLog), null));
rpDepartment.GetAll();
rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
rpEmployee,
new String[] { "GetAll" },
null,
new Decoration((x, y) =>
{
object target = x.Target;
if (target.GetType().ToString() == "ThirdPartyHR.RepositoryEmployee")
{
List<IEmployee> emps = ((IRepository<IEmployee>)target).RepList;
IEnumerable<IEmployee> query = emps.OrderByDescending(emp => emp,
new EmployeeLastnameComparer()).ToList<IEmployee>();
((IRepository<IEmployee>)target).RepList = (List<IEmployee>)query;
}
}, null));
rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
rpEmployee,
new String[] { "GetAll" },
new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
new Decoration(new DecorationDelegate(SysConcerns.ExitLog), null));
rpEmployee.GetAll();
List<SelectListItem> depList = new List<SelectListItem>();
SelectListItem sli = null;
sli = new SelectListItem();
sli.Value = "";
sli.Text = "";
depList.Add(sli);
foreach (IDepartment d in rpDepartment.RepList)
{
sli = new SelectListItem();
sli.Value = d.DepartmentID.Value.ToString();
sli.Text = d.Name;
depList.Add(sli);
}
ViewData["depSel"] = new SelectList(depList, "Value", "Text");
return View(rpEmployee.RepList);
}
}
HRRestService延长后的代码如下:[ServiceContract]
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class HRRestService
{
internal class EmployeeLastnameComparer : IComparer<IEmployee>
{
public int Compare(IEmployee e1, IEmployee e2)
{
return String.Compare(e1.LastName, e2.LastName);
}
}
private static int iStaticDep = 0;
[WebGet(UriTemplate = "?dep={dep}")]
public List<Employee> EmployeesByDepartment(string dep)
{
IRepository<IEmployee> rpEmployee = null;
IRepository<IDepartment> rpDepartment = null;
rpEmployee = new RepositoryEmployee();
rpDepartment = new RepositoryDepartment();
rpDepartment.GetAll();
rpEmployee = (IRepository<IEmployee>)ObjectProxyFactory.CreateProxy(
rpEmployee,
new String[] { "GetAll" },
null,
new Decoration((x, y) =>
{
object target = x.Target;
if (target.GetType().ToString() == "ThirdPartyHR.RepositoryEmployee")
{
List<IEmployee> emps = ((IRepository<IEmployee>)target).RepList;
IEnumerable<IEmployee> query = emps.OrderByDescending(emp => emp,
new EmployeeLastnameComparer()).ToList<IEmployee>();
((IRepository<IEmployee>)target).RepList = (List<IEmployee>)query;
}
}, null));
rpEmployee.GetAll();
IDepartment dpSel = rpDepartment.RepList[Convert.ToInt16(dep) - 1];
dpSel = (IDepartment)ObjectProxyFactory.CreateProxy(
dpSel,
new String[] { "get_DepartmentID" },
new Decoration(new DecorationDelegate(SysConcerns.EnterLog), null),
null);
iStaticDep = dpSel.DepartmentID.Value;
List<IEmployee> empSel = null;
if (rpEmployee.RepList != null)
{
empSel = rpEmployee.RepList.FindAll(
(IEmployee emp) => { return emp.DepartmentID.Value == iStaticDep; });
}
List<Employee> empObj = new List<Employee>();
foreach (IEmployee i in empSel)
{
empObj.Add((Employee)i);
}
return empObj;
}
}
一件事要注意的是ObjectProxyFactory.CreateProxy返回的对象被分配回最初提出的目标变量。例如,rpEmployee最初被分配RepositoryEmployee对象 - 目标。调用ObjectProxyFactory.CreateProxy后,它被分配返回的对象,这是一个代理的目标。这是微妙而重要的。 ObjectProxyFactory.CreateProxy返回的对象是一个代理的目标。通过使用相同的目标和其代理变量,原代码是机智。这意味着,目标和其代理是可以互换的。如果该变量是指向目标,目标是为使用。如果变量是指出目标的代理,附加功能是执行之前或之后的目标是使用。其实,如果你删除所有的代码是调用ObjectProxyFactory.CreateProxy,你的原代码前扩展对象和对象方面。
最后,运行应用程序之前,你需要修改控制台输出重定向到一个文件hrlog.txt中的Global.asax方法。这些变化是这个应用程序进入/退出日志方面才开始使用控制台。您的应用程序可以使用不同的日志记录机制。在这种情况下,您可能需要作出相应的变化。下面列出了修改后的应用程序类。public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new ServiceRoute("HRRestService",
new WebServiceHostFactory(), typeof(Services.HRRestService)));
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "DepEmployees", action = "Index", id = UrlParameter.Optional }
// Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
FileStream fileStream = null;
string path = Path.GetDirectoryName(Server.MapPath(" "));
if (!File.Exists(path + "\\hrlog.txt"))
{
fileStream = new FileStream(path + "\\hrlog.txt", FileMode.Create);
}
else
fileStream = new FileStream(path + "\\hrlog.txt", FileMode.Truncate);
TextWriter tmp = Console.Out;
Application["origOut"] = tmp;
StreamWriter sw1 = new StreamWriter(fileStream);
Console.SetOut(sw1);
Application["logStream"] = sw1;
}
protected void Application_End(object sender, EventArgs e)
{
TextWriter origStrm = (TextWriter)Application["origOut"];
Console.SetOut(origStrm);
StreamWriter tmp = (StreamWriter)Application["logStream"];
Stream fileStream = tmp.BaseStream;
tmp.Close();
fileStream.Close();
}
}
应用程序运行时,你会看到在该文件hrlog.txt中的下列输出。
兴趣点 at HRMVCAjax.Controllers.DepEmployeesController.Index()
in C:\CBDDynDecoratorAJAX\HRMVCAjax\Controllers\DepEmployeesController.cs:line 47
Entering ThirdPartyHR.RepositoryDepartment.GetAll()
Has right to call
Exiting ThirdPartyHR.RepositoryDepartment.GetAll()
at HRMVCAjax.Controllers.DepEmployeesController.Index()
in C:\CBDDynDecoratorAJAX\HRMVCAjax\Controllers\DepEmployeesController.cs:line 72
Entering ThirdPartyHR.RepositoryEmployee.GetAll()
Exiting ThirdPartyHR.RepositoryEmployee.GetAll()
at HRMVCAjax.Services.HRRestService.EmployeesByDepartment(String dep)
in C:\CBDDynDecoratorAJAX\HRMVCAjax\Services\HRRestService.cs:line 73
Entering ThirdPartyHR.Department.get_DepartmentID()
at HRMVCAjax.Services.HRRestService.EmployeesByDepartment(String dep)
in C:\CBDDynDecoratorAJAX\HRMVCAjax\Services\HRRestService.cs:line 73
Entering ThirdPartyHR.Department.get_DepartmentID()
的哲学quot;添加方面的对象neededquot;和"扩展为neededquot对象;是一个MVC / AJAX / REST应用程序。 MVC / AJAX / REST的开发人员可能会发现为解决共同的任务与动态装饰有用的以下原则:设计组件,以满足业务需求的一般方法
作为全球方法的设计方面,在自己的模块添加方面需要的对象扩展需要的对象