2015年8月5日星期三

EF6 CodeFirst+Repository+Ninject+MVC4+EasyUI实践(八) - wangweimutou

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
EF6 CodeFirst+Repository+Ninject+MVC4+EasyUI实践(八) - wangweimutou  阅读原文»

前言

  • 本篇幅将对系统的菜单管理模块进行说明,系统的菜单采用树形结构,这样可以更好地方便层级设计和查看。本示例将说明如何通过EntityFramework读取递归的菜单树形结构,以及结合EasyUI的treegrid在Asp.net MVC上显示树形菜单和管理操作。

Easyui-treegrid的使用方法

  • 首先我们来看一下treegrid的基本使用方法。很简单,和easyui-datagrid差不多。
<table title="Folder Browser" class="easyui-treegrid" style="width:700px;height:250px"
data
-options="
url: 'treegrid_data1.json',
method:
'get',
rownumbers:
true,
idField:
'id',
treeField:
'name' ">
<thead>
<tr>
<th data-options="field:'name'" width="220">Name</th>
<th data-options="field:'size'" width="100" align="right">Size</th>
<th data-options="field:'date'" width="150">Modified Date</th>
</tr>
</thead>
</table>

  • 之前说过data-options可以通过GET方式发送异步请求,读取json数据后就可以加载数据呢。Treegrid中的data-options有两个关键参数idField(主键字段)、treeField(显示字段),上面很明显是利id作为主键字段,name作为显示字段。我们查看一下treegrid_data1.json文件究竟是怎样的格式。 
[{
"id":1,
"name":"C",
"size":"",
"date":"02/19/2010",
"children":[{
"id":2,
"name":"Program Files",
"size":"120 MB",
"date":"03/20/2010",
"children":[{
"id":21,
"name":"Java",
"size":"",
"date":"01/13/2010",
"state":"closed",
"children":[{
"id":211,
"name":"java.exe",
"size":"142 KB",
"date":"01/13/2010"
},{
"id":212,
"name":"jawt.dll",
"size":"5 KB",
"date":"01/13/2010"
}]
},{
解析大型.NET ERP系统 分布式应用模式设计与实现 - James Li  阅读原文»

C/S架构的应用程序,将一些复杂的计算逻辑由客户端转移到服务器端可以改善性能,同时也为了其它方面的控制。.NET Remoting在局域网内调用的性能相当不错。ERP系统中基于.NET Remoting和WCF构建一个应用程序服务器(Application Server)。

分布式应用设计目标:

1 客户端的连接,服务器要能控制。服务器根据授权许可文件的内容,控制客户端并发数。

2 服务器崩溃,客户端要得到通知,挂起当前数据输入操作,当服务器可用时,客户端可自动重新连接 。

3 支持数据加密,对敏感的数据可用加密的端口和通道传输。

4 支持数据压缩,改善数据传输效率,因为要做一个压缩与解压缩动作,性能有所降低。

5 安全控制,应用程序服务器阻止未授权的或未签名的应用程序的连接。

6 客户端向服务器传送大文件,传送图片需要时性能优化

7 服务器端发现错误时,支持堆栈传回客户端以诊断原因。

8 开发和部署简单方便。

先设计服务器与客户端通信的接口,一个简单销售合同数据表的访问接口代码如下所示。

public interface ISalesContractManager
{
SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo);
SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo, IPrefetchPath2 prefetchPath);
SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);

EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket);
EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression);
EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath);
EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);

SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity, EntityCollection entitiesToDelete);
SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity, EntityCollection entitiesToDelete, string seriesCode);

void DeleteSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);

bool IsSalesContractExist(Guid sessionId, String ContractNo);
bool IsSalesContractExist(Guid sessionId, IRelationPredicateBucket filterBucket);
int GetSalesContractCount(Guid sessionId, IRelationPredicateBucket filterBucket);

SalesContractEntity CloneSalesContract(Guid sessionId, String ContractNo);
void PostSalesContract(Guid sessionId, String ContractNo);
void PostSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
}

再设计服务实现SalesContractManager,实现上面的接口。

[CommunicationService("SalesContractManager")]
public class SalesContractManager : ManagerBase, ISalesContractManager
{
public SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo)
{
return GetSalesContract(sessionId, ContractNo, null);
}

public SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo, IPrefetchPath2 prefetchPath)
{
return GetSalesContract(sessionId, ContractNo, prefetchPath, null);
}

注意到上面给上面的实现类添加了CommunicationService特性,也就是声明实现类是一个服务。

先来回顾一下最简单的.NET Remoting 客户端与服务器端代码设计模式。

服务器端的设计:

int port = Convert.ToInt32(ConfigurationManager.AppSettings["Port"]);
BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
provider.TypeFilterLevel = TypeFilterLevel.Full;
IDictionary props = new Hashtable();
props["port"] = port;
TcpChannel channel = new TcpChannel(props, null, provider);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(ERP.BusinessLogic.SalesContractManager), "RemotingService", WellKnownObjectMode.SingleCall);

这里是用代码写死服务类,可以用配置文件增加服务类,也可用以反射的方法增加服务类。

客户端调用的代码如下:

ISalesContractManager salesContractManager =(IPdmServer)Activator.GetObject(typeof(ISalesContractManager),string.Format("{0}RemotingService", ApplicationServerUrl));
if (salesContractManager == null)
throw new AppException("Sever configuration error");
salesContractManager.SaveSalesContract(guid, salesContract);

改善服务器端代码,让服务器主动搜索系统中打上CommunicationService特性的服务类。当新增加服务类型时,框架可自动识别并加载服务类型:

Assembly assembly = typeof(Manager).Assembly;
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (type.Namespace == "ERP.BusinessLogic.Managers")
{
string serviceName = string.Empty;
object[] attributes = type.GetCustomAttributes(typeof(CommunicationService), true);

if (attributes.Length > 0)
serviceName = type.Name;

if (!string.IsNullOrEmpty(serviceName))
{
if (clientActivatedServices.Contains(serviceName))
{
RemotingConfiguration.RegisterActivatedServiceType(type);
}
else if (singletonServices.Contains(serviceName))
{
RemotingConfiguration.RegisterWellKnownServiceType(type, serviceName + ".rem", WellKnownObjectMode.Singleton);
}
else
{
RemotingConfiguration.RegisterWellKnownServiceType(type, serviceName + ".rem", WellKnownObjectMode.SingleCall);
}
}