不是架构的架构之四:业务层的实现与自动代理

我们在开篇中提到,希望能有一种办法,能自动适应系统的环境配置,在局域网小型应用中将直接访问数据库以获得最高的性能,在分布式环境中自动使用WCF来获得较好的安全性和连通性。

但是,我们不希望这样的特性使我们的开发变得过于复杂,需要在新特性和工作量之间寻求一个合理的平衡点。

 


 

原理分析:

用自动代理的业务层结构如下:

image

检测到局域网配置时的情况,将直接返回业务对象实例。

image

分布式配置的调用情况。

我们可以从图上看出,使用局域网配置或分布式配置完全取决于自动代理对象,而对于使用者(表现层)是完全透明的。当然,对于分布式配置的情况,传入业务端的参数和结果都是需要经过序列化的,否则将无法在WCF中传输,这是我们在开发业务层时需要注意的问题。

自动代理的对象实现很简单,我们需要做的就是拦截调用,将调用的信息(业务对象名,方法名,参数)等构建为WCF服务对象的参数,通过统一的服务接口调用服务端,服务端得到调用后,根据调用信息实例化合适的业务对象,调用相应的方法,完成后将结果以统一的返回格式返回给自动代理对象,而这些返回的数据将被反序列化后作为该方法的返回值。


代码实现:

在目前的开源框架中,Castle和Spring.net都提供了很好的自动代理的类库。我们将以Spring.net为例来实现自动代理对象。

public static class BizAutoWcfProxy
{
	public static T Get<T>(T instance) where T : class
	{
		var p = new ProxyFactory(instance);
		p.AddAdvice(new AroundAdvise());
		return p.GetProxy() as T;
	}
	internal static IWcfProxy GetMethodProxy()
	{
		var service = PubFunc.GetAppSetting("WcfService");
		if (string.IsNullOrEmpty(service))
		{
			return new WcfProxy();
		}

		var binding = new NetTcpBinding(SecurityMode.None, false);
		var f = new ChannelFactory<IWcfProxy>(binding, service);

		var proxy = f.CreateChannel();
		return proxy;
	}
}

上面是自动代理工厂的实现.这个工厂中,提供了一个供自动代理对象使用的方法GetMethodProxy,这个方法中检测应用程序配置文件的WcfService配置,如果配置不为空,则创建自动代理对象,否则直接返回.

 

public class AroundAdvise : IMethodInterceptor
{
	public object Invoke(IMethodInvocation invocation)
	{
		Log.Get().Debug("方法{0}被调用被拦截", invocation.Method.Name);
		try
		{
			var p = BizAutoWcfProxy.GetMethodProxy();
			//调用信息
			var m = new MethodProxy
			{
				ClassName = invocation.TargetType.AssemblyQualifiedName,        //业务对象名
				MethodName = invocation.Method.Name                             //方法
			};
			//将调用参数也包含在调用信息中
			if (invocation.Arguments != null && invocation.Arguments.Length > 0)
				m.Params = invocation.Arguments.ToList().ConvertAll(PubFunc.Serialize.ObjToBytes).ToArray();
			//调用WCF服务
			var r = p.Execute(m);
			//正常的情况,没有异常出现
			if (!r.HasException)
			{
                //返回值
				object returnValue = r.GetValue();
				var ind = 0;
				//处理ref和out的参数的返回
				invocation.Method.GetParameters().ToList().ForEach(mp =>
				{
					if (mp.IsOut)
					{
						invocation.Arguments[mp.Position] = PubFunc.Serialize.BytesToObj(r.OutValue[ind++]);
					}
				});
				return returnValue;
			}
            
            //出现异常的情况,反序列号异常信息,重新在客户端抛出
			var ex = PubFunc.Serialize.BytesToObj(r.Exception) as Exception;
			if (ex != null) throw ex;
			throw new Exception("无法反序列化异常信息");
		}
		catch (CommunicationException oe)
		{
			Log.Get().Debug("WCF通讯错误", oe);
			throw;
		}

	}
}

 

上面是方法拦截的实现。这里除了需要考虑正常情况下调用之外,还要考虑服务器端可能发生的异常,这里增加了对异常的处理。

//唯一的WCF接口
[ServiceContract]
public interface IWcfProxy
{
    [OperationContract]
    ReturnValue Execute(MethodProxy m);
}
//服务实现
public class WcfProxy : IWcfProxy
{
    public ReturnValue Execute(MethodProxy m)
    {
        try
        {
            //服务端实例化业务对象
            var con = Type.GetType(m.ClassName).GetConstructor(
                                                               BindingFlags.NonPublic
                                                               | BindingFlags.Public
                                                               | BindingFlags.Instance,
                                                               null,
                                                               new Type[0],
                                                               null);
            var cls = con.Invoke(new object[0]);
            //处理参数的反序列化
            var method = cls.GetType().GetMethod(m.MethodName, BindingFlags.Instance | BindingFlags.Public);
            var pm = new object[0];
            if (m.Params != null && m.Params.Length > 0)
                pm = m.Params.ToList().ConvertAll(PubFunc.Serialize.BytesToObj).ToArray();
            //调用
            var rlt = method.Invoke(cls, pm);
            var r = new ReturnValue();
            r.SetValue(rlt);

            //处理out参数
            var outPlist = new List<byte[]>();
            method.GetParameters().ToList().ForEach(p =>
                                                {
                                                        if(p.IsOut)
                                                        {
                                                            outPlist.Add(PubFunc.Serialize.ObjToBytes(pm[p.Position]));
                                                        }
                                                });
            if (outPlist.Count > 0)
                r.OutValue = outPlist.ToArray();
            //正常返回
            return r;
        }
        catch(TargetInvocationException ex1)
        {
            var ex = ex1.InnerException;

            //处理服务器端发生的两种异常信息,这些属于正常的业务异常,需要返回到客户端
            if (ex is BizException || ex is DAException)
            {
                var r = new ReturnValue();
                r.HasException = true;
                r.Exception = PubFunc.Serialize.ObjToBytes(ex);
                return r;
            }
            else
            {
                //非正常情况下的异常,统一包装为服务端异常返回
                var serverex = new ServerException(ex.Message);
                serverex.Trace = ex.StackTrace;

                var r = new ReturnValue();
                r.HasException = true;
                r.Exception = PubFunc.Serialize.ObjToBytes(serverex);
                return r;
            }
        }
    }
}
//服务返回对象
[DataContract]
public class ReturnValue
{
    [DataMember]
    public bool HasException { get; set; }
    [DataMember]
    public byte[] Exception { get; set; }
    [DataMember]
    public byte[] Value { get; set; }
    [DataMember]
    public byte[][] OutValue { get; set; }

    public void SetValue(object value)
    {
        if (value == null)
        {
            Value = null; 
            return;
        }
        Value=PubFunc.Serialize.ObjToBytes(value);
    }
    public object GetValue()
    {
        if (Value == null)
        {
            return null;
        }
        
        return PubFunc.Serialize.BytesToObj(Value);
    }
}
//调用信息
[DataContract]
public class MethodProxy
{
    [DataMember]
    public string ClassName { get; set; }
    [DataMember]
    public string MethodName { get; set; }
    [DataMember]
    public byte[][] Params
    {
        get;
        set;
    }
}

这里是完整的服务端的方法,提供WCF接口,处理业务层对象调用的过程。


实现不算复杂,代码也不多,欢迎讨论。不足之处请指正。

posted @ 2011-01-24 15:39  一味  阅读(890)  评论(0编辑  收藏  举报