WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成
原文:WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成
在这之前,我写过深入介绍MS EnterLib PIAB的文章(参阅《MS Enterprise Library Policy Injection Application Block 深入解析[总结篇]》),也写过WCF与PIAB的集成(参阅:《WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成》)、WCF与Unity的集成(参阅《WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成》)以及Unity与PIAB的集成(参阅《Enterprise Library深入解析与灵活应用(1):通过Unity Extension实现和Policy Injection Application Block的集成》、《Enterprise Library深入解析与灵活应用(7):再谈PIAB与Unity之间的集成》)。由于部分实现时基于EnterLib、Unity前一个版本,在新的版本中(EnterLib V4.1与Unity 1.2)中,MS通过Unity对PIAB进行了重新设计与实现,所以我们很有必要重拾着这个话题,谈谈对于新的EnterLib和Unity,如何将PIAB和Unity集成到WCF之中。(Source Code从这里下载)
一、设计原理简述
在EnterLib中,PIAB与Unity的定位分别是轻量级的IoC Container(或者DI Container)与AOP框架。PIAB采用Method Call Interception的机制实现了策略的动态注入,其本身依赖于Interceptable对象的创建;UnityContainer建立在ObjectBuilder2之上,本质上是一个用于对象创建的容器。所以,我们可以通过UnityContainer按照PIAB的要求创建Interceptable对象,就能实现Unity与PIAB之间的集成(参阅《Enterprise Library深入解析与灵活应用(7):再谈PIAB与Unity之间的集成》)。
Unity与WCF之间的集成,本质上就是让WCF使用UnityContainer进行服务实例的创建。而WCF框架内部,服务实例的创建同时一个特殊的对象——InstanceProvider。所以我们可以通过自定义InstanceProvider,并借助UnityContainer进行服务实例的提供,那么就能实现Unity与WCF两者之间的集成。所以,创建基于UnityContainer的InstanceProvider是关键。
二、创建基于UnityContainer的InstanceProvider:UnityInstanceProvider
在WCF框架内部,InstanceProvider用户进行服务实例的提供。所有的InstanceProvider实现了接口System.ServiceModel.Dispatcher.IInstanceProvider,下面是IInstanceProvider的定义。服务实例提供实现在GetInstance中,而ReleaseInstance用于实现对服务实例的释放和资源回收。
1: public interface IInstanceProvider
2: {
4: object GetInstance(InstanceContext instanceContext);
5: object GetInstance(InstanceContext instanceContext, Message message);
6: void ReleaseInstance(InstanceContext instanceContext, object instance);
7: }
我们现在的目的就是创建一个基于Unity的InstanceProvider,借助UnityContainer提供对GetInstance方法的实现,我姑且把这个自定义的InstanceProvider称为UnityInstanceProvider。在正式介绍UnityInstanceProvider的具体实现之前,我先介绍一个辅助类型的定义:UnityTypeMapping。
我们知道,UnityContainer采用动态注册接口或者抽象类于具体类型的匹配关系,使得我们可以利用UnityContaner实现基于接口或者抽象类的方式创建我们希望的具体类的对象。UnityTypeMapping用以描述类型的匹配,其定义如下。Type和Mapto分别表示相应的类型(接口或者抽象类)与被匹配的类型(具体类),Name则表示该Mapping Entry的名称(唯一标识)。
1: public class UnityTypeMapping
2: {
3: public Type Type
4: { get; set; }
5:
6: public Type MapTo
7: { get; set; }
8:
9: public string Name
0: { get; set; }
1: }
Unity可以采用编程和配置的方式实现类型的匹配,在真正的系统开发中,后者是首选。为了实现类型匹配配置(UnityTypeElementCollection)到我们定义的UnityTypeMapping列表(IList<UnityTypeMapping>)之间的转化,我定义了下面一个扩展方法(Extension Method):Copy。
1: public static class Extension
2: {
3: public static IList<UnityTypeMapping> Copy(this UnityTypeElementCollection unityTypeElements)
4: {
5: IList<UnityTypeMapping> mappings = new List<UnityTypeMapping>();
6: foreach (UnityTypeElement type in unityTypeElements)
7: {
8: mappings.Add(new UnityTypeMapping { Type = type.Type, MapTo = type.MapTo, Name = type.Name });
9: }
0:
1: return mappings;
2: }
3: }
下面列出了UnityInstanceProvider的具体定义。属性ContractType与Container分别代表服务契约与用于创建服务实例的UnityContainer对象,字段_registeredTypeMapping表示当前UnityContainer相关的类型匹配集合。出于性能的考虑,为了避免UnityContainer的频繁创建和类型匹配关系的频繁解析,我通过两个静态属性|字段来保存它们(Containers和registeredTypeMappings,Key为Container的名称)。在构造函数中接受两个输入参数:contractType与containerName,分别表示服务契约类型与相应UnityContainer的名称。根据containerName判断相应的UnityContainer是否已经创建,如果是,则直接从上述的两个静态变量中提取相应的UnityContainer和类型匹配列表。否则,重新创建UnityContainer,加载相应的配置信息对其进行配置。需要特别指出的是,在对创建的UnityContainer进行初始化的时候,添加了一个特殊的UnityContainerExtension:ExtendedIntercepiton,该UnityContainerExtension用户实现Unity与PIAB的集成,在《Enterprise Library深入解析与灵活应用(7):再谈PIAB与Unity之间的集成》中对ExtendedIntercepiton的实现原理具有详细的介绍。
在GetInstance方法中,我们通过UnityContainer根据服务契约(接口)类新进行具体服务实例的创建。在创建之前,我们需要判断服务契约类型与服务类型之间的类型匹配是否已经注册到UnityContainer中,如果没有,则进行注册,并将类型匹配添加到当前类型匹配列表(_registeredTypeMappings)和全局类型匹配列表(registeredTypeMappings)中。
1: public class UnityInstanceProvider : IInstanceProvider
2: {
3: private static object syncHelper = new object();
4: private static IDictionary<string, IList<UnityTypeMapping>> registeredTypeMappings;
5: public static IDictionary<string, IUnityContainer> Containers
6: { get; private set; }
7:
8: private IList<UnityTypeMapping> _registeredTypeMappings;
9: public Type ContractType
10: { get; private set; }
11: public IUnityContainer Container
12: { get; private set; }
13:
14: static UnityInstanceProvider()
15: {
16: registeredTypeMappings = new Dictionary<string, IList<UnityTypeMapping>>();
17: Containers = new Dictionary<string, IUnityContainer>();
18: }
19:
20: public UnityInstanceProvider(Type contractType, string containerName)
21: {
22: if (contractType == null)
23: {
24: throw new ArgumentNullException("contractType");
25: }
26:
27: this.ContractType = contractType;
28:
29: string key = containerName ?? string.Empty;
30: if (Containers.ContainsKey(key))
31: {
32: this.Container = Containers[key];
33: this._registeredTypeMappings = registeredTypeMappings[key];
34: return;
35: }
36:
37: UnityContainerElement containerElement = this.GetUnitySettings(containerName);
38: IUnityContainer container = new UnityContainer();
39: if (null != containerElement)
40: {
41: containerElement.Configure(container);
42: }
43: container.AddNewExtension<ExtendedInterception>();
44: PolicyInjectionSettings section = (PolicyInjectionSettings)ConfigurationSourceFactory.Create().GetSection("policyInjection");
45: if (section != null)
46: {
47: section.ConfigureContainer(this.Container, ConfigurationSourceFactory.Create());
48: }
49: lock (syncHelper)
50: {
51: if (!Containers.ContainsKey(key))
52: {
53: Containers[key] = container;
54: registeredTypeMappings[key] = containerElement.Types.Copy();
55: }
56: }
57:
58: this.Container = container;
59: this._registeredTypeMappings = registeredTypeMappings[key];
60: }
61:
62: #region IInstanceProvider Members
63:
64: public object GetInstance(InstanceContext instanceContext, Message message)
65: {
66: string contractServiceTypeMappingName = string.Empty;
67: var contractServiceTypeMappings = from mapping in this._registeredTypeMappings
68: where mapping.Type == this.ContractType &&
69: mapping.MapTo == instanceContext.Host.Description.ServiceType
70: select mapping;
71: if (contractServiceTypeMappings.Count() == 0)
72: {
73: contractServiceTypeMappingName = Guid.NewGuid().ToString();
74: this.Container.RegisterType(this.ContractType, instanceContext.Host.Description.ServiceType, contractServiceTypeMappingName);
75: this._registeredTypeMappings.Add(new UnityTypeMapping { Type = this.ContractType, MapTo = instanceContext.Host.Description.ServiceType, Name = contractServiceTypeMappingName });
76: }
77: else
78: {
79: contractServiceTypeMappingName = contractServiceTypeMappings.ToArray<UnityTypeMapping>()[0].Name;
80: }
81:
82: return this.Container.Resolve(this.ContractType, contractServiceTypeMappingName);
83: }
84:
85: private UnityContainerElement GetUnitySettings(string containerName)
86: {
87: UnityConfigurationSection unitySection = ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
88: if (unitySection == null)
89: {
90: return null;
91: }
92:
93: if (string.IsNullOrEmpty(containerName))
94: {
95: return unitySection.Containers.Default;
96: }
97: else
98: {
99: return unitySection.Containers[containerName];
100: }
101: }
102:
103: public object GetInstance(InstanceContext instanceContext)
104: {
105: return this.GetInstance(instanceContext, null);
106: }
107:
108: public void ReleaseInstance(InstanceContext instanceContext, object instance)
109: {
110: IDisposable disposable = instance as IDisposable;
111: if (disposable != null)
112: {
113: disposable.Dispose();
114: }
115: }
116:
117: #endregion
118: }
三、为UnityInstanceProvider创建Behavor对象
自定义行为(Behavior)是进行WCF扩张最为典型和常用的方式。按照作用域的不同,WCF的行为可以分为以下四类:契约行为(Contract Behavior)、服务行为(Service Contract)、终结点行为(Endpoint Behavior)和操作行为(Operation Behavior)。为了将上面自定义的UnityInstanceProvider应用到WCF服务端的分发系统,定义了如下一个行为类型:UnityIntegrationBehaviorAttribute。我们可以看出,UnityIntegrationBehaviorAttribute同时实现了IServiceBehavior、IContractBehavior和IEndpointBehavior,所以既是一个服务行为,也是一个契约行为,同时还是一个终结点行为。同时UnityIntegrationBehaviorAttribute继承了Attribbute,所以同时可以以Attribute的形式应用到服务契约(作为契约行为)类型和服务(作为服务类型)。你同样可以通过配置的方式以服务行为和终结点行为的方式应用该UnityIntegrationBehaviorAttribute。如果想采用配置的方式,你还需要定义相关的BehaviorExtensionElement,由于篇幅的问题,我就不就对BehaviorExtensionElement的问题作介绍了。
1: using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace Artech.UnityIntegration
{
public class UnityIntegrationBehaviorAttribute : Attribute, IServiceBehavior, IContractBehavior,IEndpointBehavior
{
public string ContainerName
{ get; set; }
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint,ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(endpoint.Contract.ContractType, this.ContainerName);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
#region IContractBehavior Members
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint,ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint,DispatchRuntime dispatchRuntime)
{
dispatchRuntime.InstanceProvider = new UnityInstanceProvider(endpoint.Contract.ContractType, this.ContainerName);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
#region IServiceBehavior Members
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(GetContractType(serviceHostBase, endpointDispatcher), this.ContainerName);
}
}
}
private Type GetContractType(ServiceHostBase serviceHostBase, EndpointDispatcher endpointDispatcher)
{
var endpoint = serviceHostBase.Description.Endpoints.Where(item => item.Contract.Name == endpointDispatcher.ContractName &&
item.Contract.Namespace == endpointDispatcher.ContractNamespace).ToArray<ServiceEndpoint>()[0];
return endpoint.Contract.ContractType;
}
public void Validate(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase)
{
}
#endregion
}
}
四、将UnityIntegrationBehavior应用到WCF应用中
为了演示UnityIntegrationBehavior的效果,我们创建了一个简单的WCF实例应用。我们采用《Enterprise Library深入解析与灵活应用(7):再谈PIAB与Unity之间的集成》中同步时间提供的例子,通过一个服务得到同步的当前时间。下面是服务契约的定义:
1: using System;
2: using System.ServiceModel;
3: namespace Artech.UnityIntegrationDemo.Contracts
4: {
5: [ServiceContract(Namespace="urn:artech.com")]
6: public interface ISyncTimeProvision
7: {
8: [OperationContract]
9: DateTime GetCurrentTime();
0: }
1: }
实现了该服务契约的SyncTimeProvisionService本是并不具体实现不同时间的提供,而是通过另一个组件SyncTimeProvider。你可以将SyncTimeProvider看成是同一个应用的另一个模块,将此例子看成是一个典型的跨模块调用。为了实现真正的模块化,达到模块之间的松耦合,我们借助Unity,采用“属性注入(Propetry Setter Injection)”的方式,通过接口的方式(ISyncTimeProvider)调用另一个模块。为了证实PIAB的效果,我在SyncTimeProvider上面应用了CachingCallHandlerAttribute,如果该CallHandler生效的话,方法返回的结果将会被缓存,在缓存过期之前,你将得到相同的时间。而我们定义的UnityIntegrationBehaviorAttribute以服务行为的方式直接应用到服务类型(SyncTimeProvisionService)上。
1: using System;
2: using Artech.UnityIntegration;
3: using Artech.UnityIntegrationDemo.Contracts;
4: using Microsoft.Practices.Unity;
5:
6: namespace Artech.UnityIntegrationDemo.Services
7: {
8: [UnityIntegrationBehavior(ContainerName="myContainer")]
9: public class SyncTimeProvisionService : ISyncTimeProvision
10: {
12: [Dependency]
13: public ISyncTimeProvider SyncTimeProvider
14: { get; set; }
15:
16: #region ISyncTimeProvision Members
17:
18: public DateTime GetCurrentTime()
19: {
20: return this.SyncTimeProvider.GetCurrentTime();
21: }
22:
23: #endregion
24: }
25:
26: public interface ISyncTimeProvider
27: {
28: DateTime GetCurrentTime();
29: }
30:
31: [CachingCallHandler]
32: public class SyncTimeProvider : ISyncTimeProvider
33: {
34: #region ISyncTimeProvider Members
35:
36: public DateTime GetCurrentTime()
37: {
38: return DateTime.Now;
39: }
40:
41: #endregion
42: }
43: }
在服务寄宿的配置中,提供了WCF服务和Unity的相关设置:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
5: </configSections>
6: <system.serviceModel>
7: <services>
8: <service name="Artech.UnityIntegrationDemo.Services.SyncTimeProvisionService">
9: <endpoint address="http://127.0.0.1:3721/synctimeprovisionservice"
10: binding="ws2007HttpBinding" bindingConfiguration="" contract="Artech.UnityIntegrationDemo.Contracts.ISyncTimeProvision" />
11: </service>
12: </services>
13: </system.serviceModel>
14: <unity>
15: <typeAliases>
16: <typeAlias alias="ISyncTimeProvider" type="Artech.UnityIntegrationDemo.Services.ISyncTimeProvider, Artech.UnityIntegrationDemo.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
17: <typeAlias alias="SyncTimeProvider" type="Artech.UnityIntegrationDemo.Services.SyncTimeProvider, Artech.UnityIntegrationDemo.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
18: </typeAliases>
19: <containers>
20: <container name="myContainer">
21: <types>
22: <type type="ISyncTimeProvider" mapTo="SyncTimeProvider" />
23: </types>
24: </container>
25: </containers>
26: </unity>
27: </configuration>
当服务成功寄宿,在Console Appliation下,执行下面一段服务端调用程序,你将得到下面的输出。从输出结果中,我们可以清晰地看到,返回的5个返回的时间均是相同的,由此我们可以看出应用才SyncTimeProvider上面的CachingCallHandlerAttribute生效了。进而证明了PIAB和Unity、Unity和WCF的有效集成:
1: using System;
2: using System.ServiceModel;
3: using System.Threading;
4: using Artech.UnityIntegrationDemo.Contracts;
5:
6: namespace Artech.UnityIntegrationDemo.Client
7: {
8: class Program
9: {
10: static void Main(string[] args)
11: {
12: using (ChannelFactory<ISyncTimeProvision> channelFactory = new ChannelFactory<ISyncTimeProvision>("synctimeprovisionservice"))
13: {
14: ISyncTimeProvision proxy = channelFactory.CreateChannel();
15: using (proxy as IDisposable)
16: {
17: try
18: {
19: for (int i = 0; i < 5; i++)
20: {
21: Console.WriteLine("The current time is {0}", proxy.GetCurrentTime());
22: Thread.Sleep(1000);
23: }
24: }
25: catch (CommunicationException)
26: {
27: (proxy as ICommunicationObject).Abort();
28: throw;
29: }
30: catch (TimeoutException)
31: {
32: (proxy as ICommunicationObject).Abort();
33: throw;
34: }
35: }
36: }
37:
38: Console.Read();
39: }
40: }
41: }
42:
执行结果:
注:部分内容节选自《WCF技术剖析(卷1)》第十章: WCF实例研究(WCF in Practice)
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成的更多相关文章
- WCF技术剖析之二十一: WCF基本的异常处理模式[上篇]
原文:WCF技术剖析之二十一: WCF基本的异常处理模式[上篇] 由于WCF采用.NET托管语言(C#和NET)作为其主要的编程语言,注定以了基于WCF的编程方式不可能很复杂.同时,WCF设计的一个目 ...
- WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...
- WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)
原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...
- WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制 和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service ...
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇]
原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇] 在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术 ...
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇]
原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[上篇] 在进行基于会话信道的WCF服务调用中,由于受到并发信道数量的限制,我们需要及时的关闭信道:当遇到某些异常,我们需要强行中止(Abor ...
- WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]
原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...
- WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序)
原文:WCF技术剖析之二十七: 如何将一个服务发布成WSDL[基于HTTP-GET的实现](提供模拟程序) 基于HTTP-GET的元数据发布方式与基于WS-MEX原理类似,但是ServiceMetad ...
- WCF技术剖析之二十八:自己动手获取元数据[附源代码下载]
原文:WCF技术剖析之二十八:自己动手获取元数据[附源代码下载] 元数据的发布方式决定了元数据的获取行为,WCF服务元数据架构体系通过ServiceMetadataBehavior实现了基于WS-ME ...
随机推荐
- matlab之kmeans聚类用法
kmeans函数用法如下: [IDX,C,sumd,D] = kmeans(X,2,'Distance','city','Replicates',5,'Options',opts); 参数含义如下:I ...
- C语言选择法排序
#include <stdio.h> int main() { int i, j, p, n, q; ] = {, , , , }; //对无序数组进行排序 ; i<; i++) { ...
- C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码
extern void *memcpy(void *dest,void *src,unsigned int count);#include <string.h> 功能:由src所指内存 ...
- mysql记录慢查询
1,配置开启 Linux: 在mysql配置文件my.cnf中增加 log-slow-queries=/var/lib/mysql/slowquery.log (指定日志文件存放位置,可以为空,系统会 ...
- android面试题之一
在接下来的一段时间,我将收集一些常见面试题,综合网上资料加自己测试与理解,将其总结出来和大家分享,里面难免有一些问题,希望大家提出宝贵意见以便及时更正. 一.Activity.Service.Broa ...
- ThinkPHP - 空模块+空操作
空操作 空操作是指系统在找不到指定的操作方法的时候,会定位到空操作(_empty)方法来执行,利用这个机制,我们可以实现错误页面和一些URL的优化. 例如,下面我们用空操作功能来实现一个城市切换的功能 ...
- POJ 3276 Face The Right Way 翻转(开关问题)
题目:Click here 题意:n头牛排成一列,F表示牛面朝前方,B表示面朝后方,每次转向K头连续的牛的朝向,求让所有的牛都能面向前方需要的最少的操作次数M和对应的最小的K. 分析:一个区间反转偶数 ...
- Easyui中tree组件实现搜索定位功能及展开节点定位
这几天遇到个input + tree 实现搜索功能的需求,在这里贴出来供大家参考下,如果你有更好的实现效果希望不腻赐教! 首先给大家看看效果 小二 上图 : 需要的部件知识: easyui ...
- Python 数据处理扩展包: numpy 和 pandas 模块介绍
一.numpy模块 NumPy(Numeric Python)模块是Python的一种开源的数值计算扩展.这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list str ...
- Solr入门之SolrServer实例化方式
随着solr版本的不断升级, 差异越来越大, 从以前的 solr1.2 到现在的 solr4.3, 无论是类还是功能都有很大的变换, 为了能及时跟上新版本的步伐, 在此将新版本的使用做一个简单的入门说 ...