SharePoint2010 Custom Service
Create Custom Service Class
Service类用来定义这个自定义“Service”,也是各种功能的入口点。它继承SPIisWebService,并实现IServiceAdministration(以接入到管理系统中)。
[Guid("E828358E-E24C-4D78-BE87-C69D458766F4")]
public class KaneboyService : SPIisWebService, IServiceAdministration
Service类通常实现二个构造函数:
- 默认构造函数
- 一个参数为SPFarm对象的构造函数
public KaneboyService() { }
public KaneboyService(SPFarm farm) : base(farm) { }
IServiceAdministration的方法:
- CreateApplication():创建一个SPServiceApplication对象。只有在通过运行psconfig.exe或farm configuration wizard创建Service Application时才会调用它(所以对于自定义SPService,它通常不会有机会被调用)。
- CreateProxy():创建一个SPServiceApplicationProxy对象。与CreateApplication()类似。
- GetApplicationTypeDescription():返回对此Service的描述信息。
- GetApplicationTypes():返回Service Application的类型。
- GetCreateApplicationLink():如果希望可以在管理中心的“管理服务应用程序”中新建此Service Application实例,那么需要实现此方法并返回新建Service Application页面的URL。
Create Custom Service Instance Class
Service Instance用来描述运行在每个SPServer上的Service实例。它继承SPIisWebServiceInstance。
public class KaneboyServiceInstance : SPIisWebServiceInstance
Service Instance类的构造函数:
- 默认构造函数
- 参数如下的构造函数:
- 一个SPServer对象,表示运行此Service实例的Server
- 所对应的SPIisWebService对象
public KaneboyServiceInstance() : base() { }
public KaneboyServiceInstance(SPServer server, SPIisWebService service) : base(server, service) { }
Service Instance类需要实现的方法:
- TypeName属性:描述此Service Instance的名称,显示在管理UI。
Create Custom Service Application Class
Service Application类是对Service Application的实现。它继承SPIisWebServiceApplication,并通常实现Service Contract接口。
[IisWebServiceApplicationBackupBehavior]
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.PerSession,
ConcurrencyMode = ConcurrencyMode.Multiple,
IncludeExceptionDetailInFaults = true)]
[Guid("933A11F0-7265-4694-97C0-FE04BFB370A5")]
public class KaneboyServiceApplication : SPIisWebServiceApplication, IKaneboyServiceContract
Service Application类的构造函数:
- 默认构造函数
- 带下列参数的构造函数:
- Service Application名称
- 所对应的SPIisWebService对象
- 所使用的SPIisWebServiceApplicationPool对象
public KaneboyServiceApplication() : base() { }
private KaneboyServiceApplication(String name, KaneboyService service, SPIisWebServiceApplicationPool appPool) : base(name, service, appPool) { }
Service Application需要告诉系统,如何连接到WCF Service,所以它需要实现:
- InstallPath属性:返回.svc所在的目录。
- VirtualPath属性:返回.svc文件的名称。
如果在Provision这个Service Application的时候,还需要Provision数据库或其他资源,可以重载Provision()和Unprovision()方法。
创建(并Provision)一个Service Application不能直接使用它的构造函数,而是需要使用如下步骤:
- 创建Service Application至少需要用到如下信息:
- Service Application名称
- 它所对应的SPService对象
- 它所使用的SPIisWebServiceApplicationPool对象
- 通过SPService.Applications.GetValue(),确定这个Service Application是否有其他实例存在。
- 使用它的构造函数,新建一个实例对象。
- 调用SPServiceApplication.Update()保存新建的实例。
- 调用SPServiceApplication.AddServiceEndpoint()为Service Application新建WCF Endpoint。
serviceApp.AddServiceEndpoint("http", SPIisWebServiceBindingType.Http);
serviceApp.AddServiceEndpoint("https", SPIisWebServiceBindingType.Https, "secure");
- 调用SPServiceApplication.Provision()以Provision它。
创建并Provision 自定义Service Application的方法,可以放到Service Application类中,以static method提供。管理员可以使用管理中心UI(位于”ADMIN”中的自定义.aspx,路径由IServiceAdministration.GetCreateApplicationLink()指定)或自定义powershell cmdlets来完成这个操作,所以在管理中心UI或自定义cmdlets中,需要调用这个static method。
WCF Artifacts
首先,需要一个自定义Host Factory类,继承自ServiceHostFactory。
public class KaneboyServiceHostFactory : ServiceHostFactory
它重载CreateServiceHost()方法,创建并返回一个ServiceHost对象。
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
ServiceHost serviceHost = new ServiceHost(typeof(KaneboyServiceApplication), baseAddresses);
serviceHost.Configure(SPServiceAuthenticationMode.Claims);
return serviceHost;
}
在”WebServices”这个mapped folder下面,需要放置自定义Service Application的.svc文件和定义WCF channel的web.config。.svc只需要通过标签告知Service和Factory类即可。
<%@ServiceHost Language="C#" Debug="true"
Service="KaneboyServiceApp1.KaneboyServiceApplication, $SharePoint.Project.AssemblyFullName$"
Factory="KaneboyServiceApp1.KaneboyServiceHostFactory, $SharePoint.Project.AssemblyFullName$" %>
web.config里面需要定义Service Application将使用的channel。
<configuration>
<system.serviceModel>
<services>
<service name="KaneboyServiceApp1.KaneboyServiceApplication">
<endpoint
address=""
contract="KaneboyServiceApp1.IKaneboyServiceContract"
binding="customBinding"
bindingConfiguration="CustomServiceHttpBinding" />
<endpoint
address="secure"
contract="KaneboyServiceApp1.IKaneboyServiceContract"
binding="customBinding"
bindingConfiguration="CustomServiceHttpsBinding" />
</service>
</services>
<bindings>
<customBinding>
…
</customBinding>
</bindings>
</system.serviceModel>
<system.webServer>
<security>
…
</security>
</system.webServer>
</configuration>
Register Service on Farm
将Service注册到Farm里面,需要创建Service和Service Instance对象,并将它们Persist到Config DB中(通过调用Update())。注册可以通过一个Farm Feature的activate事件来实现。在新建Service和Service Instance对象之前,可以通过调用SPFarm.Services.GetValue()来确认是否Farm中已经注册了此服务。在Farm Feature的deactivate事件中,可以将相关的Service和Service Instance删除。
Create and Provision Service Application
新建Service Application的实例,可以使用管理中心UI(“管理服务应用程序”)或自定义powershell cmdlets来实现。如果是通过管理中心UI,可以在“ADMIN”中新建一个.aspx,并将其路径在Service类中通过IServiceAdministration.GetCreateApplicationLink()告知系统。
无论哪种方法,最关键的是,如何将Service Application所使用的SPIisWebServiceApplicationPool对象传递给新建Service Application的代码。如果是使用.aspx,可以使用” ~/_admin/IisWebServiceApplicationPoolSection.ascx”控件,然后通过IisWebServiceApplicationPoolSection.GetOrCreateApplicationPool()得到用户选择的App Pool。如果是使用cmdlets,那么就通过SPIisWebServiceApplicationPoolPipeBind类来得到App Pool信息。(Check MSDN to get detail information.)
------------------ “Server”(App Server)与”Client”(WFE)的分割线 ----------------------
WCF Artifacts
在”WebClients”这个mapped folder下面,将一个”client.config”文件放置到一个自定义目录中,此文件中包含了告知Service Application Proxy通过哪个channel连接到Service Application的配置信息。
<configuration>
<system.serviceModel>
<client>
<endpoint
name="http"
contract="KaneboyServiceApp1.IKaneboyServiceContract"
binding="customBinding"
bindingConfiguration="CustomServiceHttpBinding" />
<endpoint
name="https"
contract="KaneboyServiceApp1.IKaneboyServiceContract"
binding="customBinding"
bindingConfiguration="CustomServiceHttpsBinding" />
</client>
<bindings>
<customBinding>
…
</customBinding>
</bindings>
</system.serviceModel>
</configuration>
Create Custom Service Proxy Class
Service Proxy类用来描述service consumer。它继承SPIisWebServiceProxy,并实现IServiceProxyAdministration。
[SupportedServiceApplication("933A11F0-7265-4694-97C0-FE04BFB370A5", "1.0.0.0", typeof(KaneboyServiceApplicationProxy))]
[Guid("44203351-1E42-413E-BAAA-42A4A0788A8D")]
public class KaneboyServiceProxy : SPIisWebServiceProxy, IServiceProxyAdministration
Service Proxy类的构造函数:
- 默认构造函数
- 参数为SPFarm对象的构造函数
IServiceProxyAdministration的方法:
- CreateProxy():创建一个SPServiceApplicationProxy。
- GetProxyTypeDescription():返回对此Service Proxy的描述。
- GetProxyTypes():返回Service Application Proxy的类型。
Create Custom Service Application Proxy Class
Service Application Proxy类定义了连接到Service Application的接口,Service Consumer通过它实现对Service的调用。它继承SPIisWebServiceApplicationProxy类。
[IisWebServiceApplicationProxyBackupBehavior]
[System.Runtime.InteropServices.Guid("6339C282-E7D3-4B0D-94DF-2D09773FDF80")]
public class KaneboyServiceApplicationProxy : SPIisWebServiceApplicationProxy
Service Application Proxy类的构造函数:
- 默认构造函数
- 包含以下参数的构造函数:
- Service Application Proxy的名称
- 所对应的Service Proxy对象
- Service Application的Endpoint URL
public KaneboyServiceApplicationProxy() : base() {}
public KaneboyServiceApplicationProxy(String name, KaneboyServiceProxy serviceProxy, Uri serviceEndpointUri) : base(name, serviceProxy, serviceEndpointUri)
{
_loadBalancer = new SPRoundRobinServiceLoadBalancer(serviceEndpointUri);
}
由于Service Application通常需要Load Balance的能力,所以Service Application Proxy通常会包含一个SPServiceLoadBalancer类型field,它可以使用SharePoint内置提供的SPRoundRobinServiceLoadBalancer类来实例化。SPRoundRobinServiceLoadBalancer类需要给出Service Application的URL来进行构造。
Service Application Proxy要实现的方法:
- 一个根据Service Application的Url,获取Endpoint Configuration名称(对应到client.config中”<Endpoint>”节点的”name”属性)的方法(如“GetEndpointConfigurationName()”),Endpoint Configuration的名称通常是“http”和“https”。
- 一个创建并返回ChannelFactory<T>的方法(如“CreateChannelFactory()”),它需要读取client.config中的信息。
private ChannelFactory<T> CreateChannelFactory<T>(string endpointConfigName)
{
// open client.config
string clientConfigPath = SPUtility.GetGenericSetupPath(@"WebClients\KaneboyServiceApp1");
Configuration clientConfig = OpenClientConfiguration(clientConfigPath);
ConfigurationChannelFactory<T> factory = new ConfigurationChannelFactory<T>(endpointConfigName, clientConfig, null);
// configure the channel factory for IDFx claims auth
factory.ConfigureCredentials(SPServiceAuthenticationMode.Claims);
return factory;
}
- 一个获取Channel(Service Contract接口)的方法,通过调用ChannelFactory. CreateChannelActingAsLoggedOnUser<T>(),来获取Channel。
private IKaneboyServiceContract GetChannel(Uri address)
{
// get the endpoint config name
string endpointConfigName = GetEndpointConfigurationName(address);
ChannelFactory<IKaneboyServiceContract> channelFactory = CreateChannelFactory<IKaneboyServiceContract>(endpointConfigName);
IKaneboyServiceContract channel = channelFactory.CreateChannelActingAsLoggedOnUser<IKaneboyServiceContract>(new EndpointAddress(address));
return channel;
}
结合以上的Helper方法,就可以拿到可以透过WCF远程调用Service Application的Channel对象。
创建(并Provision)一个Service Application Proxy的步骤如下:
- 调用Service Application Proxy构造函数,新建实例。
- 调用SPServiceApplicationProxy.Update(true)将对象persist到ConfigDB中。
- 调用SPServiceApplicationProxy.Provision()。
类似Service Application,创建Service Application Proxy的方法可以放到此类中,以static method的方式提供。
Create and Provision Service Application Proxy
新建并Provision Service Application Proxy,类似Service Application,可以使用管理中心UI或自定义powershell cmelets实现。
如果是使用管理中心UI,可以在创建并Provision了Service Application之后,紧接着创建Service Application Proxy。如果是创建cmdlets,可以使用SPServiceApplicationPipeBind来得到其所对应的Service Application。
Invoke Service
通过Service Application Proxy调用Service Application所提供的功能,步骤如下:
- 获取SPServiceApplicationProxy对象,可以通过SPServiceContext.GetDefaultProxy()来获得。
- 调用SPServiceApplicationProxy对象所持有的SPServiceLoadBalancer对象的BeginOperation()获取一个SPServiceLoadBalancerContext对象,然后通过使用SPServiceLoadBalancerContext.EndpointAddress拿到这次请求需要调用的Endpoint Url。使用此Url,并通过调用Service Application Proxy中的Helper方法,拿到IChannel对象,也就是实现了Service Contract的对象。
- 调用IChannel上的方法
- 使用IChannel.Close()关闭连接。
- 调用SPServiceLoadBalancer.EndOperation()告知完成了Load Balance操作。