SCSF. Chapter 2. D. CAB Services
A service is a singleton object made available through the loose coupling mechanism of CAB. The consumer 消费者/用户 of the service does not need to know the internal implementation of the service, its packaging or its location, or even its class. Many services are used internally by the system, such as the authentication service. It is also very common for CAB application designers to write their own services.
服务是通过CAB的松耦合策略由单例对象所创建。用户不需要知道服务的实现,它的打包和位置,甚至它的类。许多服务被在内部使用,比如authentication 服务。对CAB程序设计者而言写他们自己的服务是非常平常的。
Consider the CAB authentication service, called during the CAB startup sequence (step 6, previous page). Its function is to figure out who the user is, one way or another 无论如何, and to place onto the application's main thread a token containing the identity of authenticated user. This can be accomplished 完成的 in many different ways, and it is entirely 完全的 reasonable 合理的 for some application developers to prefer one way and others to prefer a different way. Customers also may have their own preferences for authentication: one uses a password, another uses a fingerprint reader, still another uses a radio frequency ID (RFID) tag in an employee's badge.
考虑一下CAB authentication 服务,在CAB启动顺序中被唤起。它的方法指出用户是谁,无论如何,然后把一个包含已验证用户ID的token放在主线程上。这可以通过多种方法被完成,对某些程序开发者而言喜欢一种方式相比另一种是完全合理的。用户也有很多方法来验证:有的是密码,有的是指纹读取器,还有的使用雇员徽章的RFID标签。
The authentication service is defined by the IAuthenticationService interface, containing the single method Authenticate. Note that its return type is void. If it cannot determine the user's identity, its job is to throw an exception. Thus:
authentication 服务被定义在IAuthenticationService 接口中,包含一个单独的Authenticate方法。注意它的返回值是void。如果无法确定用户的身份,则会抛出一个异常。
public interface IAuthenticationService
{
void Authenticate();
}
The default implementation is Shown in the following code sample. It sets the application's identity to that of the user who is logged in to the Windows desktop. It's a good default option. Thus:
默认实现在下面的样例代码中,他设置了哪些账号可以登录程序。
public class WindowsPrincipalAuthenticationService :
IAuthenticationService
{
public void Authenticate()
{
// Set current principal.
AppDomain.CurrentDomain.SetPrincipalPolicy(
PrincipalPolicy.WindowsPrincipal);
}
}
Services are placed in a collection that the WorkItem class maintains 维持 for this purpose (see Lesson 3 for further discussion of WorkItems and their collections). The collection is indexed by the interface that the service implementation supports. Only one class can be registered as the implementation of each service interface. You may place a service into this collection in a number of ways. The authentication service is initialized in the method CabApplication.AddRequiredServices, shown as item 3 in Figure 2-4. We access the root WorkItem, telling its Services collection to instantiate an object of the WindowsPrincipalAuthenticationService class and register it as the implementation of the IAuthenticationService interface. The code looks like this:
服务被放在集合中来实现workitem类的持久化。集合是通过接口实现来索引的。每个服务接口只能注册一个实现。你可以通过各种方法把服务放在集合中。authentication服务被初始化在CabApplication.AddRequiredServices方法中。我们进入root workItem,告诉服务集合IAuthenticationService 接口的实例化对象是谁。代码如下:
private void AddRequiredServices()
{
// Tell the Services collection of the root WorkItem to instantiate
// a new object of the specified class, and register it as the
// implementation of the specified interface.
rootWorkItem.Services.AddNew<WindowsPrincipalAuthenticationService,
IAuthenticationService>();
<... other services added >
}
When some other piece of code wants to fetch and use this service, it does so via the Get method of the Services collection of the WorkItem. In it, we pass the interface for which we want the current implementation. The code that is fetching the service has no idea what the implementation is or where it came from. It knows only that this is the object that is currently registered as supporting the specified interface. If the implementation has been changed from the default to a password checker or a fingerprint reader, this client code doesn't know or care—or want to know or want to care. It simply uses the registered interface to call the desired method. Thus:
当其他代码想使用该服务时,是这样通过workItem获取服务的。我们把接口传递给当前实现。代码获取服务不需要知道它来自哪里。它只知道被引用的注册对象。如果实现被更改为其他方法,客户端不需要知道也不关心,只需要直接使用就行了。
private void AuthenticateUser()
{
// Fetch the current object that is registered as implementing the
// authentication service interface. We don't know or care what
// class it is, or how it got there.
IAuthenticationService auth =
rootWorkItem.Services.Get<IAuthenticationService>(true);
// Call the authenticate method. Exactly how it figures out who the
// user is, and verifies that it really is that guy and not someone
// else, isn't our concern.
auth.Authenticate();
}
Now, suppose we want to replace the default authentication service in our application. Instead of just accepting the identity of the Windows desktop user, we want a different method, say a password or a fingerprint reader or an RFID tag in an employee badge. What would we do? first, we'd write a new implementation of the service, more or less like this:
现在,设想我们替换了默认验证服务。我们不想通过ID验证,采取了一种不同的 方式,我们需要做什么呢?我们只需要写一个新的实现就可以了。
public class MyFingerprintAuthenticationService :
IAuthenticationService
{
public void Authenticate()
{
// Perform fingerprint authentication
MyOwnUserData user = MyOwnFingerPrintReader.GetUser ( ) ;
// If we successfully figured out who the user was, then place
// that information into an identity token and place the token
// onto the main thread.
if (user != null)
{
GenericIdentity identity = new GenericIdentity(user.Name);
GenericPrincipal principal = new GenericPrincipal(identity,
user.Roles);
Thread.CurrentPrincipal = principal;
}
else
{
throw new AuthenticationException(
"couldn't find your fingerprints");
}
}
}
Now that we've written our service implementation, how can we get it into the root work item in place of the original authentication service? There are two ways: either programmatically or declaratively. In the former case, we'd override the shell application's AddServices method, thus:
我们写好了自己的服务实现,那如何去替换原始的服务呢?这里有两个方法:动态配置或者宣告。在前面的例子中我们重写了shell appliction的AddService方法,如此:
protected override void AddServices()
{
base.AddServices();
// Create an instance of our own authentication class
MyFingerprintAuthenticationService mfas =
new MyFingerprintAuthenticationService ();
// Remove the default authentication
RootWorkItem.Services.Remove< IAuthenticationService>();
// Replace it with our own
RootWorkItem.Services.Add< IAuthenticationService>(mfas);
}
Alternatively 二者选一, we could make entries in the app.config file. The <remove> element specifies the interface for which to remove the implementation. The <add> element specifies the interface (serviceType) and the class (instanceType) to register in its place. Note that each type is specified by its fully qualified name, then a comma, and then the name of the DLL file in which it is located. Thus:
二者选一,我们可以记入app.config文件。<remove>元素指定了哪些接口的实现需要移除。<add>元素指定了已注册的接口(服务类型)和类(实例类型)。需要注意的是,指定的类型必须是完整名称,接着一个逗号,然后是DLL文件名。
<CompositeUI>
<services>
<remove serviceType =
"Microsoft.Practices.CompositeUI.Services.IAuthenticationService, CompositeUI" />
<add serviceType =
"Microsoft.Practices.CompositeUI.Services.IAuthenticationService,
CompositeUI"
instanceType="MyOwnNamespace. MyFingerprintAuthenticationService,
MyOwnNamespaceAssembly""/>
</services>
</CompositeUI>
Note that in both cases, we have to explicitly remove the existing implementation before adding the new one. If we add a new one without removing the old one, perhaps hoping to overwrite it, CAB considers that a mistake and throws an exception.
在两个例子中,我们需要在添加实现之前明确的移除现存的实现。如果我们添加了一个新的并没有移除旧的,也许是想重写它,CAB则认为这是一个错误并且抛出一个异常。
I like to add services via config files during development for flexibility 灵活性. But in most production code, that flexibility can become a potential 潜在的 source of errors, a hindrance 障碍 rather than a help, when users or administrators mess with a file that they shouldn't have. So unless you need the flexibility at runtime, for example, to produce different configurations for different customers, then I suggest that you hard-wire them into code for production. If it is not necessary to make them flexible, then it IS necessary to make then NOT flexible.
我想通过config文件灵活的在开发中添加一个服务。但是在大部分产品代码中,这种灵活性可能变成BUG潜在的来源。一个障碍而不是一个帮助,当用户和管理员被一个本不该有的文件所混乱了。除非你需要这样的运行时复杂性,例如,通过不同的配置对应不同的客户,因此我建议你硬编码在产品中,如果不需要变复杂,那么就不要复杂。
Now we'll use the principles 原则 that we learned in modifying the prefabricated CAB services to write our own services. Most applications will contain at least a few. It's a very convenient 方便的 mechanism 机制 for distributing programmatic logic in a loosely coupled way.
所以我们的原则是我们学到了如何预先改变CAB服务来编写我们的服务。大部分程序包含至少一些,这是一个对分布式程序而言非常方便的松耦合机制。
To demonstrate 证明 this service mechanism, I wrote my own CAB service. I first needed to define the interface that this service would provide. For simplicity 简易, I provided just one method. Because it will be used by both the implementer of the service and the consumer 消费者 of the service, I put it in a separate assembly named TimeService.Interface.dll to avoid any type of code dependency. Thus:
为了证明这样的服务机制,我编写了我自己的CAB服务。我首先需要蒂尼接口需要提供的服务。为了简单起见,我只提供了一个方法。因为它会被服务的实现者和消费者共同使用,我把它放在一个分散的程序集中叫TimeService.Interface.dll来避免任何类型的代码依赖。如此:
// Interface to be exposed by my new service
public interface ITime
{
DateTime GetTime();
}
Now I needed to implement the service. It is common to put your implementation in one DLL and your interface definition in another. That way, you have to deliver 交付 the latter only to other developers, so they can't inadvertently set a reference to the implementation rather than the interface. In the case, I called that DLL TimeService.dll. That implementation looks like this:
现在我需要实现服务。把你的实现放在一个DLL中并且在另一个钟定义接口是很通常的行为。那样,你需要后者交付给其他开发者去实现,这样他们就不能无意的引用这个实现而不是接口。在这个例子中,我是用TimeService.dll这个DLL,它的现实像这样:
public class TimeService : ITime
{
public DateTime GetTime()
{
return DateTime.Now ;
}
}
In addition to the techniques shown on the preceding page for adding services, you can also add a service by decorating 给授权 the class definition with the attribute Service, which tells CAB to treat it in that manner 方式. For this technique to work, the module in which it resides must be loaded by the CAB module loader service, which contains the code that recognizes the attribute. You can optionally specify the AddOnDemand attribute (not shown), which tells CAB not to instantiate it until someone asks for it. It looks like this:
除了之前展示的添加服务技术,你还可以通过定义类的特征来授权服务,告诉CAB以那种方式来对待它。这种工作技巧,属于它的module必须被CAB module loader service所加载,它包含了那些识别特征的代码。你可以选择指定AddOnDemand特征,它告诉CAB不要实例化直到某人请求。它看起来像这样:
[Service(typeof(ITime))]
public class TimeService : ITime
{
<etc. >
In packaging my new service's declaration and implementation, I have several choices. I could put them into the SCSF–generated Infrastructure.Interface DLL (for the interface definitions) and Infrastructure.Library DLL (for service implementations). Because I have to roll these out anyway, I might as well piggyback 肩扛 my services along with those of SCSF. This would be a good choice if I had only a few services, and they were so central to my application that I always wanted them and never had to snap them out or snap other ones in.
在打包我的新服务的宣告和实现,我有多个选择。我可以把它们放在SCSF生成的Infrastructure.Interface DLL和Infrastructure.Library DLL中。因为我需要推出它们,我可能需要通过SCSF独自负责我的服务。如果我只有一些服务,那这将是一个好的选择。
Alternatively, I could generate a separate module to hold these services. The SCSF provides the choice of Foundational Module and Business Module. The primary difference is that the latter automatically creates and adds a new WorkItem to the chain, while the former does not. (Think "business = work" and you'll find it easy to remember.) Either of these is fine for a service. Since we want interfaces in a separate DLL, I'd probably select the Create an Interface Library for This Module check box,This causes SCSF to generate a separate module for interfaces, as shown in Figure 2-5.
作为一种选择,我可以可以产生一盒分离的module来保存这些服务。SCSF提供了Foundational Module和Business Module的选择。主要的不同在于后者自动创建一个并添加一个新的WorkItem到chain中,前者却不这么做(想想business = work你会发现这更好记)两者中任何一个对服务而言都是可以的。既然我们想要在一个分离的DLL中获取接口,我可能选择勾选Create an Interface Library for This Module这一项,这会使SCSF生成一个分离的接口module。
Figure 2-5. Separate implemenation and interface modules for a service.


浙公网安备 33010602011771号