(翻译)Microsoft Enterprise Library Frequently Asked Questions
最近比较闲,项目也差不多都忙完了。打算给自己的代码和技术做个整理,升个级。
目前公司的系统用于访问数据库的组件是Enterprise Library 2.0。现在它已经到了5.0 beta1 版了。
先看它的FAQ,写的很详细。原文地址是
http://entlib.codeplex.com/wikipage?title=EntLib%20FAQ&referringTitle=Home
因为有些模块没有应用到实际项目中,按照字面意思翻译可能不准确。
有的内容没有按照它的原文翻译,太长的句子看不明白,我一般是按照自己的意思描述出来。
有不恰当的地方,请多多指教。
Part 1 企业库安装和配置
入门
问题:如何解决错误提示:该版本的企业库不能与4.0的企业库同时安装,请卸载4.0版的企业库后重新运行安装程序。当安装企业库4.1时,即使卸载了4.0版的企业库,仍然会得到这个错误提示。
解答:当您卸载4.0版的企业库时,同时还要在注册表中删除Enterprise Library v4 的键值。打开注册表编辑器(Regedit.exe),找到HKEYLOCALMACHINE\SOFTWARE\Microsoft\Enterprise Library v4, 删除这个键。
问题:当加载配置文件时,提示“无法加载程序集”和“本地程序集资源清单(manifest)文件的描述与引用的程序集不匹配
解答:通常的原因是配置工具所引用的程序集与应用程序部署时引用的程序集不匹配。
因为配置工具也要引用相应的程序集才能运行。(没有翻译,请查看原文)
问题: 安装企业库失败时,是否有相应的办法来诊断问题
解答: 当安装带有源代码的安装包时,通常会提示“由于系统管理策略,安装无法继续“。
出现这种情况时,可以尝试以管理员身份运行源代码安装包,同时开启命令行提示。
迁移和升级
问题:可否把1.x的配置文件升级到新版本
解答:不行。受.NET 2.0框架的支持,配置文件在2.0时已经有极大的改善。即使配置文件没有加密,1.1的配置文件和新版本的文件是不兼容的。您可以手动转换,或是直接拷贝新版本的配置信息
问题:可否把2.0的配置文件升级到更新的版本
解答:没有自动迁移的工具,因为更新版本(相对于1.x)的配置节相当稳定,手动升级配置文件也很容易。您需要升级配置文件中的程序集引用为新版本的程序集,如果是升级到企业库4.0,还应该考虑把公匙B03F5F7F11D50A3A 转换为 31BF3856AD364E35。
Part 2: 企业库应用程序块
依赖注入应用程序块
问题:依赖注入应用程序块支持泛型方法吗?
解答:不支持,当前版本的应用程序块不支持泛型方法。关于使用泛型的替代方案,请参考codeplex的讨论,地址是Policy Injection with CachingHandler and generic method
问题:依赖注入应用程序块能拦截扩展方法(extension methods)吗
解答:不支持。依赖注入应用程序只能拦截类型的成员方法,扩展方法并不是它所扩展的类型的真正的成员。(应该是编译器提供的方便)
问题:为什么把ValidationCallHandler特性运用于属性时,它不起作用。
解答:您应该把ValidationCallHandler特性作用于属性的setter方法,而不是作用于属性本身。
下面的代码展示它的用法
public interface IPerson
{
int Age
{
get;
[method: ValidationCallHandler()]
[param: RangeValidator(0, RangeBoundaryType.Ignore, 70, RangeBoundaryType.Inclusive)]
set;
}
}
问题:如何给依赖注入应用程序块配置性能计数器句柄?
解答:详细的解决方案,请参考MSDN中的内容,网址是The Performance Counter Handler
问题:当把ICallHandler特性作用于接口时,为什么会执行两次
解答:这是一个bug. 当您只运用MemberNameMatchingRule到匹配的接口的实现方法中时,就会出现这种情况。假如您有下面的接口和实现
namespace ServiceNamespace
{
public interface IService
{
void Save();
}
public class Service : IService
{
public void Save();
}
}
如果策略MemberNameMatchingRule设置为匹配Save方法,该策略会匹配两次,一次是匹配IService.Save,另一次是Service.Save。作为一个替代的结局方法,添加一个TypeMatchingRule,用于匹配接口。对于本例,它应该是ServiceNamespace.IService
日志应用程序块
问题:如何创建自定义的格式化器
解答:请依照下面的步骤
1 为项目添加一个新类
2 (这个步骤可选)在新增加的类的文件头部,添加程序集引用
C#
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
Visual Basic
Imports Microsoft.Practices.EnterpriseLibrary.Common.Configuration
Imports Microsoft.Practices.EnterpriseLibrary.Logging
Imports Microsoft.Practices.EnterpriseLibrary.Logging.Configuration
3 指定新添加的类实现ILogFormatter接口
4 为类添加ConfigurationElementType特性,指定CustomFormatterData为该特性的构造函数的参数值。
5 添加一个具有NameValueCollection类型参数的构造函数
6 为类添加Format方法,然后按照您需要的格式实现该方法
问题:如何把日志的时间戳格式设为本机的格式
解答:把TextFormatter定义为符号值{timestamp(local)}
问题:如果在运行时改变日志记录级别(logging level),日志应用程序块如何能检测到配置文件的变化
解答:最好的方案是使用外部文件来存储日志的配置信息。日志应用程序快检测到外部配置文件的变化,在不重启的情况下,从外部文件中重新加载配置,应用于系统。使用SectionChangeHandler可以检测配置文件的变化。
问题:如何避免产生两个日志文件,即使指定了日志输出为一个日志文件。
解答:在异常处理应用程序快中,设置LoggingHandler 的UseDefaultLogger属性为true,该属性的默认值为false,这个改变会使用GUID作为文件名。
问题:如何把日志记录到自定义的数据库或表中
解答:把配置选项DatabaseTraceListener设置为自定义的存储过程(就是不使用企业库提供的默认的存储过程),设置AddCategory(职责是写日志类别)和WriteLog(职责是写日志)为自定义的存储过程,同时,还可以指定自定义的数据库实例。
为了更好的扩展性,尝试实现自定义的DatabaseTraceListener,实现自定义的日志记录方法。
关于更多的创建自定义DatabaseTraceListener,查看下面的MSDN内容,地址是Walkthrough: Creating a Custom Trace Listener
问题:为什么无法记录日志
解答:有很多可能的原因,一些是由配置文件中引起的,另一些是由错误导致的。
(没有翻译,请查看原文)
缓存应用程序块
问题:如何遍历已经加入到CacheManager中的缓存项
解答:目前还没有方法遍历CacheManager中的缓存项。缓存项也可能在遍历的过程中失效。关于这个问题的解决方法,请参考codeplex的讨论,地址是Get list of items in a cache manager
问题:如何在没有配置文件的情况下使用缓存应用程序块
解答:使用类CacheManagerFactory,给它传递IConfigurationSource接口。配置源可以是DictionaryConfigurationSource,它描述了管理缓存的行为和存储相关的内容,如下面的代码所示
DictionaryConfigurationSource configSource = new DictionaryConfigurationSource();
CacheManagerSettings cacheSettings = new CacheManagerSettings();
configSource.Add(CacheManagerSettings.SectionName, cacheSettings);
CacheStorageData storageConfig = new CacheStorageData("MyStorage", typeof(NullBackingStore));
cacheSettings.BackingStores.Add(storageConfig);
CacheManagerData cacheManagerData = new CacheManagerData("CustomCache", 120, 100, 5, storageConfig.Name);
cacheSettings.CacheManagers.Add(cacheManagerData);
cacheSettings.DefaultCacheManager = cacheManagerData.Name;
CacheManagerFactory cacheFactory = new CacheManagerFactory(configSource);
ICacheManager cacheManager = cacheFactory.CreateDefault();
问题:当缓存项被刷新时,如何避免从CacheManager中被检索出来的项的值为空
解答:偶尔会出现这种情况。为避免这个问题,可以创建自定义的ICacheItemExpiration方法实现。在HasExpired方法中,检查缓存项是否失效,如果它们失效,需要更新值。如果这个方法返回false,表示它永远不失效。HasExpired方法返回false的结果会导致,该缓存项不会被刷新,并且会一直保持首次赋给它的值而不失效。
加密应用程序块
问题:当把使过加密应用程序块的系统部署到生产环境中时,为什么会提示“拼凑(Padding)无效,也不能被移除“
解答:密匙文件被DPAPI加密处理过,因此只能在创建它的主机上被解密(如果启用用户模式选项,同时还有参考创建它的用户)。为了在不同的计算机上使用密匙文件,或者同一个计算机的不同用户之间,密匙文件必须被适当的分发。关于更多的如何使用配置工具分发密匙文件的内容,请参考MSDN中Deploying the Cryptography Application Block
数据访问应用程序块
问题:方法DatabaseFactory.CreateDatabase() 和 DatabaseFactory.CreateDatabase(connString)的区别是什么
解答:没有参数的CreateDatabase方法会使用默认的数据库,可以在配置文件中指定默认的数据库实例连接。使用含参数的CreateDatabase(connString),它会根据参数的名字,在配置文件中检查连接字符串,然后用此字符串创建数据库连接实例,当您的配置文件中有多个数据库连接字符串时,可以用此重载方法指定特定的数据库连接字符串。
问题:如何获取存储过程输出参数的值?
解答:检索存储过程输出参数的值,使用GetParameterValue方法。如下面的代码示例
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetStoredProcCommand("GetCustomerID");
string name = Console.ReadLine();
db.AddInParameter(command, "@name", System.Data.DbType.String, name);
db.AddOutParameter(command, "@CustID", System.Data.DbType.Int32, int.MaxValue);
db.ExecuteNonQuery(command);
int custId = (int)db.GetParameterValue(command, "@CustID");
Console.WriteLine("CustomerID is : " + custId.ToString());
Console.ReadLine();
如果使用ExecuteReader来检索数据,直到关闭数据读取器之前,存储过程的输出参数都是有效的。
问题:如何扩展数据库类
解答:为了创建扩展类,请依照下面的步骤
1 创建一个派生于Database的类
2 实现构造方法,初始化数据连新年好字符串和其他必要的配置
public SqlDatabase(string connectionString) : this(connectionString, SqlClientFactory.Instance){ }
3 需要重写参数处理的方法。例如,重写DeriveParameters来支持查找参数,同时也应该重写下面的方法(内容没有翻译,请查看原文)
4 (内容没有翻译,请查看原文)
问题:LocalSqlServer连接字符串是从那里产生的,为什么即使删除了它,再次打开时配置文件时还是会存在
解答:LocalSqlServer定义在Machine.config配置文件中,它是由System.Configuration API返回的结果,在配置文件编辑器中删除LocalSqlServer并不能影响Machine.config,因此,再次打开文件时,它还是存在。最好的办法是还是不要管它。
异常应用程序块
问题:如何记录FaultException异常
解答:(内容没有翻译,请查看原文)
问题:如何给异常处理程序块配置日志功能
解答:为了给异常处理程序块添加日志功能,依照下面的步骤
1 添加异常处理应用程序块配置节
2 添加异常策略
3 添加异常类型
4 给异常类型添加日志处理功能,然后配置日志选项
问题:如何在异常应用程序块中设置ExceptionShielding特性,不屏蔽异常FaultExceptions
解答:可以为FaultException定义一个WCF策略,它没有处理程序,也没有任何行为。这会使处理器忽略FaultException异常。如下面的配置所示
<exceptionHandling>
<exceptionPolicies>
<add name="WCF Exception Shielding">
<exceptionTypes>
<add type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="ThrowNewException" name="Exception">
<exceptionHandlers>
<add exceptionMessage="Test" faultContractType="ShieldingTest.TestFaultContract, ShieldingTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="Fault Contract Exception Handler" />
</exceptionHandlers>
</add>
<add type="System.ServiceModel.FaultException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="None" name="FaultException" />
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>
安全应用程序块
问题:在不能打开aspnetdb数据训的情况下,如何解决安全应用程序块的错误
解答:您应该把连接字符串指向正确的服务器
问题:在安全应用程序快中,验证和角色管理特性有什么新的变化
解答:自企业库2.0以来,这些特性已经被相应的框架提供程序取代。
验证应用程序块
问题:为什么在基类中配置好的验证组件(validators),在它的派生类中不起作用
解答: 当验证组件在配置文件中指定时,会发生这种情况。为了解决这个问题,您应该在子类中复制一份与验证相关的逻辑。推荐的方法是在基类中,把验证组件(validators)以设置特性的方式传递给它。这种方法不需要在子类中再次重复设定特性。
问题:如何实现有条件的验证
解答:使用验证应用程序块的SelfValidation。
关于更多的讨论,请查看codeplex上的讨论,地址是Conditional validation?
关于如何使用的例子,请查看David Hayden的文章,地址是
Business Object Validation Using Validation Application Block - Self Validation Option - Enterprise Library 3.0
实用应用程序块
问题:如何指定非字符串类型(System.String)的特性值的类型
解答:value元素的值应该是这样的,如下的例子,指定value的类型为整型。
<property name="age" propertyType="System.Int32">
<value value="10" type="System.Int32"/>
</property>
问题:实用程序块可以在Silverlight应用中使用吗
解答:可以。请参看MSDN中相关的内容,地址是Unity Application Block 1.2 for Silverlight
问题:如何检查到未注册(unregistered)的类型时抛出异常?
解答:可以创建一个简单的扩展,添加策略,在调用策略前检查类型是否匹配。为组织操作(buildup operation)添加BuildKeyMappingStrategy策略。
通过这种方式,如果检查出类型不匹配,可抛出异常。
问题:可以使用外部文件来加载实用程序的配置吗
解答:可以,如下的代码所示
var map = new ExeConfigurationFileMap { ExeConfigFilename = "AlternateConfig.xml" };
var config = ConfigurationManager.OpenMappedExeConfiguration(map,ConfigurationUserLevel.None);
var section = (UnityConfigurationSection)config.GetSection("unity");
IUnityContainer container = new UnityContainer();
section.Containers["liveContainer"].Configure(container);
Part 3 常见的问题
问题:如何给配置区段加密和解密
解答:依照下面的步骤来给配置区段加密
1 打开一个配置工具
2 打开现有的配置文件或创建一个新配置文件
3 点击需要加密的应用程序块区段
4 在属性面板中,点击ProtectionProvider
5 设置ProtectionProvider的值为DataProtectionConfigurationProvider 或 RsaProtectedConfigurationProvider.
对于全局的配置,比如密匙文件存放位置,需要编辑Machine.config。这个文件不能用配置工具编辑,你必须选择文本编辑器来修改它。
解密配置文件。在配置工具中打开。当配置文件在配置编辑器中打开时,它会被自动解密。
问题:如何部署有加密配置区段的应用程序
解答:部署有加密配置区段的配置文件,用于加密的密匙应该共享
问题:与环境相关的配制信息会被存储吗?
解答:环境相关的配置信息不会被保存到配置文件中。如果要加载它们,右键点击Environments结点,点击Open Environment Delta,定位含有特定信息的配置文件,点击Open 。
问题:在企业库未来的版本中,是否支持Compact Framework
解答:当前版本的企业库不支持Compact Framework,也没有计划在将来的版本中支持它。微软模式与实践团队发布的Mobile Client Software Factory for .NET Compact Framework,它包含一些支持Compact Framework应用程序块。
问题:为什么切换到设计模式时,会出现空引用异常
解答:通常是由于不适当的调用企业库中的类型导致的。为避免这个问题,当在设计模式下使用企业库时,应该提供自定义的组件设计器,或者检查Component.DesignMode(Windows Forms 程序)和Control.DesignMode(ASP.NET程序)属性。
问题:Object Builder 1.0可以和Object Builder 2.0同时使用吗?我正在使用企业库3.1,它使用的是Object Builder 1.0,我想升级到企业库4.0?
解答:这两个程序库可以在同一个项目中并行工作,没有任何问题
问题:为什么有些程序集没有被拷贝到应用程序目录中去,例如,Logging.Database 和 ExceptionHandling.Logging。即使把引用这程序集的类库的CopyLocal属性设为true,它们也没有被拷贝到应用程序目录中
解答:包含引用应用程序块的类的程序集是不直接被引用的,因此,构建过程中并不会把它们拷贝到应用程序目录中。您需要手动拷贝应用程序的配置文件中引用程序集到项目的输出目录中,或者推荐添加一个编译后的步骤(post build),拷贝这些文件到项目的输出目录中。
问题:为什么会产生InvalidOperationException的异常提示“被请求的性能计数器不是一个自定义的计数器,它必须初始化为只读的“
解答:如果在应用程序的配置文件中启用了性能计数监测,并且这些性能计数器在使用之前并没有被安装,它会抛出上面的异常提示。 对相应的程序集运行Installutil实用工具来安装它提供的性能计数器,或是运行InstallServices.bat脚本来安装所有程序集的性能计数器
问题:如果应用程序是分层或分子系统来设计的,如何保证不同的程序集,不同的层或子系统拥有它自己的配置文件?
解答:一般而言,无法做到这样,配置文件是隶属于应用程序域的,不隶属于程序集,你可以尝试隔离不同的层到不同的应用程序域中,以这种方式使不同的层或子系统各自拥在不同的配置文件,通常这个方法也不可行。
一个变通的办法是,为不同的配置区段指定不同的配置文件。不同的程序集可以通过使用通过给ConfigurationSourceFactory类传递参数来检索配置信息。然而,并不是所有的API都支持IConfigurationSource参数,具体的说,所有的静态门面(facades)方面,例如日志和异常策略,依赖于默认的配置源,因此,它并不支持IConfigurationSource参数。
下面的代码例子,展示如何创建一个配置源
IConfigurationSource source = ConfigurationSourceFactory.Create("DAL");