(因为是按照自己的思绪写的,所以写得比较乱,有些部分不一定准确,后续会有更新

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.exefarm 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类的构造函数:

·        默认构造函数

·        参数如下的构造函数:

o   一个SPServer对象,表示运行此Service实例的Server

o   所对应的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类的构造函数:

·        默认构造函数

·        带下列参数的构造函数:

o   Service Application名称

o   所对应的SPIisWebService对象

o   所使用的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至少需要用到如下信息:

o   Service Application名称

o   它所对应的SPService对象

o   它所使用的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 channelweb.config.svc只需要通过标签告知ServiceFactory类即可。

<%@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里面,需要创建ServiceService Instance对象,并将它们PersistConfig DB中(通过调用Update())。注册可以通过一个Farm Featureactivate事件来实现。在新建ServiceService Instance对象之前,可以通过调用SPFarm.Services.GetValue()来确认是否Farm中已经注册了此服务。在Farm Featuredeactivate事件中,可以将相关的ServiceService 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类的构造函数:

·        默认构造函数

·        包含以下参数的构造函数:

o   Service Application Proxy的名称

o   所对应的Service Proxy对象

o   Service ApplicationEndpoint 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 ApplicationURL来进行构造。

Service Application Proxy要实现的方法:

·        一个根据Service ApplicationUrl,获取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;

}

·        一个获取ChannelService 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 ApplicationChannel对象。

创建(并Provision)一个Service Application Proxy的步骤如下:

·        调用Service Application Proxy构造函数,新建实例。

·        调用SPServiceApplicationProxy.Update(true)将对象persistConfigDB中。

·        调用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,可以在创建并ProvisionService 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操作。