快速入门 - ObjectService


定义服务接口

有如下邮件发送客户端接口
namespace HTB.DevFx.Mail
{
	/// <summary>
	/// 邮件发送服务接口
	/// </summary>
	public interface IMailService
	{
		/// <summary>
		/// 发送邮件
		/// </summary>
		/// <param name="message">MailMessage实体</param>
		void Send(MailMessage message);

		/// <summary>
		/// 发送邮件
		/// </summary>
		/// <param name="message">MailMessage实体</param>
		/// <param name="queued">是否缓存邮件(提高程序响应速度)</param>
		void Send(MailMessage message, bool queued);

		/// <summary>
		/// 发送邮件
		/// </summary>
		/// <param name="from">发送者地址</param>
		/// <param name="to">接收者地址(可填多个地址,用英文分号";"分割)</param>
		/// <param name="subject">邮件主题</param>
		/// <param name="messageText">邮件内容</param>
		void Send(string from, string to, string subject, string messageText);

		/// <summary>
		/// 发送邮件
		/// </summary>
		/// <param name="from">发送者地址</param>
		/// <param name="to">接收者地址(可填多个地址,用英文分号";"分割)</param>
		/// <param name="subject">邮件主题</param>
		/// <param name="messageText">邮件内容</param>
		/// <param name="queued">是否缓存邮件(提高程序响应速度)</param>
		void Send(string from, string to, string subject, string messageText, bool queued);
	}
}

客户端使用服务接口

使用者永远都只依赖服务接口,而非实现者。(接口的实现是由配置文件配置的)
...
var service = ObjectService.GetObject<IMailService>();
service.Send(from, to, subject, messageText);
...

实现服务接口

实现服务接口的类可以是internal的,建议从ServiceBase<TSetting>派生,TSetting是服务依赖的配置接口(会由DevFx注入)
namespace HTB.DevFx.Mail
{
	public class MailService : ServiceBase<IMailServiceSetting>, IMailService
	{
		......
	}
}
其中IMailServiceSetting是服务需要的配置接口,ServiceBase<IMailServiceSetting>定义了IMailServiceSetting Setting,所以在服务实现类内可以直接使用,而无需费心去找配置接口的实现类。

定义服务需要的配置接口

比如邮件服务需要邮件服务器地址、端口、用户名、密码等
namespace HTB.DevFx.Mail.Config
{
	/// <summary>
	/// 邮件发送工具配置
	/// </summary>
	public interface IMailServiceSetting
	{
		/// <summary>
		/// SMTP服务器地址
		/// </summary>
		string SmtpServer { get; }

		/// <summary>
		/// SMTP服务器侦听端口
		/// </summary>

		int ServerPort { get; }
		/// <summary>
		/// 认证用户名
		/// </summary>
		string UserName { get; }

		/// <summary>
		/// 认证用户密码
		/// </summary>
		string Password { get; }

		/// <summary>
		/// 缓存发送时定时器间隔时间(毫秒)
		/// </summary>
		double Interval { get; }
	}
}

实现配置接口(内部类)

接口不能实例化,所以必须实现配置接口,但这个实现可以是internal的,配置实现类必须从ConfigSettingElement派生
using HTB.DevFx.Config;

[assembly: ConfigResource("res://HTB.DevFx.Mail.Config.htb.devfx.mail.config", Index = 300)]

namespace HTB.DevFx.Mail.Config
{
	internal class MailServiceSetting : ConfigSettingElement,  IMailServiceSetting
	{
		protected override void OnConfigSettingChanged() {
			this.SmtpServer = this.GetSetting("smtpServer");
			this.ServerPort = this.GetSetting("serverPort", 25);
			this.UserName = this.GetSetting("userName");
			this.Password = this.GetSetting("password");
			this.Interval = this.GetSetting("interval", 1000D);
		}

		public string SmtpServer { get; private set; }
		public int ServerPort { get; private set; }
		public string UserName { get; private set; }
		public string Password { get; private set; }
		public double Interval { get; private set; }
	}
}

创建默认的配置文件

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<htb.devfx>
		<objects>
			<typeAliases>
				<add name="IMailService" type="HTB.DevFx.Mail.IMailService, HTB.DevFx.BaseFx" /><!--接口类型定义-->
			</typeAliases>

			<object name="MailService" type="IMailService" mapTo="HTB.DevFx.Mail.MailService, HTB.DevFx" setting="{name:'../../mail', type:'HTB.DevFx.Mail.Config.MailServiceSetting, HTB.DevFx'}"><!--接口实现类定义以及依赖的配置实现类和配置节-->
				<lifetime type="Singleton" /><!--服务类的生命周期,Singleton表示单例模式-->
				<constructor configSet="{tag:'parameter'}"><!--服务类构造参数-->
					<parameter name="logService" type="ILogService" value="@LogService" /><!--服务类依赖日志服务,由这里注入-->
				</constructor>
			</object>
		</objects>

		<mail smtpServer="localhost" serverPort="25" userName="" password="" interval="1000" /><!--配置节(默认)-->
	</htb.devfx>
</configuration>
请把配置文件的Build Action设置为Embedded Resource(即嵌入资源),同时在MailServiceSetting非类定义外加入如下代码,告诉DevFx您的资源文件在哪里。
using HTB.DevFx.Config;

[assembly: ConfigResource("res://HTB.DevFx.Mail.Config.htb.devfx.mail.config", Index = 300)]

......

客户端覆盖配置

对于使用者,可以在运行目录添加类似htb.devfx.*.config的配置文件,DevFx会自动查找这些文件并载入分析,内容必须如下格式
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<htb.devfx>
		<mail smtpServer="localhost" serverPort="25" userName="user@domain.com" password="password" interval="1000" />
	</htb.devfx>
</configuration>
也可以配置在web.config或app.config中,需要如下形式进行配置:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
		<section name="htb.devfx" type="HTB.DevFx.Config.ConfigSectionHandler, HTB.DevFx.BaseFx" />
	</configSections>

......

	<htb.devfx>
		<mail smtpServer="localhost" serverPort="25" userName="user@domain.com" password="password" interval="1000" />
	</htb.devfx>
</configuration>

高级用法


324版本的Breaking Change:objects命名容器标签的变动


鉴于配置文件的约定,objects命名容器标签会与其他标签重复,比如Data定义中
<htb.devfx>
	<objects name="Data">
		...
	</objects>
	...
	<data ... />
</htb.devfx>
将会合并,这不是我们希望的。所以324版把命名容器放到全局objects内的namespace标签,htb.devfx下有且只有一个objects标签,下面是324版本的正确样本
<htb.devfx>
	<objects>
		...
		<namespace>
			<objects name="Data">
				...
			</objects>
			...
		</namespace>
	</objects>
	...
	<data ... />
</htb.devfx>
这样的改变引起的后果,就是要把object的setting定义需要相应改变。

配置服务调试信息


	<htb.devfx>
		<startup>
			<core>
				<configService>
					<debug enabled="是否启用调试:false|true" outputFile="完整的配置文件保存地址:..\htb.devfx.full.config" />
				</configService>
			</core>
		</startup>
	</htb.devfx>

可池化的生命周期

类似数据库链接池,这里可以理解为对象池。可被池化的对象需要实现IPoolable接口,为了方便使用可以实现IDisposable接口;同时也内置了可池化对象的基类PoolObjectBase,如下:
	public abstract class PoolObjectBase : IPoolable, IDisposable
	{
		protected event Action<IPoolable> Disposing;
		protected virtual void Dispose(bool disposing) {
		}

		event Action<IPoolable> IPoolable.Disposing {
			add { this.Disposing += value; }
			remove { this.Disposing -= value; }
		}


		void IPoolable.Dispose() {
			this.Dispose(true);
		}

		void IDisposable.Dispose() {
			if(this.Disposing != null) {
				this.Disposing(this);
			}
		}
	}
自己实现IPoolable,需要在使用完对象后调用Disposing事件通知对象池管理器回收(再利用),真正的对象施放代码应该放在IPoolable.Dispose中。
如下配置所示,对象配置中,lifetime设置为Pool即可。当然你还可以通过参数maxPoolSize指定对象池的最大值,默认值为-1,表示没有限制;参数enabled指示是否池化,默认为true,表示池化,false则不池化,为单例模式;参数debug表示是否输出日志(以供参考,默认为false)。
	<htb.devfx>		
		<objects configSet="{tag:'object', nullable:'false'}">
			<object name="PoolingObject" type="TestProject.PoolingTest+PoolObject, TestProject">
				<lifetime type="Pool" />
				<pooling enabled="true" maxPoolSize="1" debug="false" />
			</object>
		</objects>
	</htb.devfx>
	[TestClass]
	public class PoolingTest
	{
		public class PoolObject : PoolObjectBase
		{
			public string Hello() {
				return "hello world";
			}
		}

		[TestMethod]
		public void TestMethod1() {
			using(var obj = ObjectService.GetObject<PoolObject>()) {
				obj.Hello();
			}
			using(var obj1 = ObjectService.GetObject<PoolObject>()) {
				obj1.Hello();
			}
		}
	}

简单的日志和异常处理


	/// <summary>
	/// 写日志
	/// </summary>
	/// <param name="sender">调用者</param>
	/// <param name="level">日志等级,参见<see cref="LogLevel"/></param>
	/// <param name="logFormat">日志格式</param>
	/// <param name="parameters">格式化参数</param>
	/// <remarks>先找是否有<see cref="ILogService"/>的实现,有则调用相关方法;无则默认写入WARN级及以上的日志信息到应用程序同级目录Logs下</remarks>
	public static void WriteLog(object sender, int level, string logFormat, params object[] parameters);
	/// <summary>
	/// 发布异常
	/// </summary>
	/// <param name="e">异常</param>
	/// <param name="level">记录异常的日志等级</param>
	/// <remarks>先找是否有<see cref="IExceptionService"/>的实现,有则调用相关方法;无则默认写日志信息</remarks>
	public static void Publish(Exception e, int level = LogLevel.ERROR);

日志处理默认等级


	<htb.devfx>
		<log enabled="false|true" verbose="INFO" />
	</htb.devfx>
verbose 参数表示日志默认处理等级(默认为INFO),比如开发时可以配置为MIN(这样可以输出所有的日志信息),生产环境可以配置为INFO或WARNING(这样只输出警告等级的日志信息,避免日志文件过大)

Last edited Sep 23, 2011 at 6:45 AM by R2, version 1

Comments

No comments yet.