一:缓存服务类型与方法
客户端请求的时候
为了方便的知道请求的类型与类型所包含的方法
我们把服务类型和方法缓存到静态字典中了
代码如下
代码语言:javascript复制 public class WCFRouteTable
{
static Dictionary<string, Type> routeService;
static Dictionary<string, MethodInfo> routeMethods;
static WCFRouteTable()
{
routeService = new Dictionary<string, Type>();
routeMethods = new Dictionary<string, MethodInfo>();
var ass = (typeof(WCFRouteTable)).Assembly;
var ts = ass.GetTypes();
foreach (var t in ts)
{
if (t.FullName.StartsWith("RTMDemo.Host.WCF"))
{
routeService.Add(t.FullName, t);
foreach (var m in t.GetMethods())
{
var mName = string.Format("{0}.{1}", t.FullName, m.Name);
routeMethods.Add(mName, m);
}
}
}
}
public static Type GetWCFType(string key)
{
Type result = null;
if (routeService.ContainsKey(key))
{
result = routeService[key];
}
return result;
}
public static MethodInfo GetMethodInfo(string key)
{
MethodInfo result = null;
if (routeMethods.ContainsKey(key))
{
result = routeMethods[key];
}
return result;
}
}
二:托管HTTP请求
在webconfig中增加module以托管请求
代码语言:javascript复制 <modules>
<add name="WcfHttpModule" type="RTMDemo.Host.WCFHttpModule, RTMDemo.Host"/>
</modules>
托管请求对应的类的代码如下
代码语言:javascript复制 public class WCFHttpModule:IHttpModule
{
public void Dispose() { }
/// <summary>
/// 托管请求
/// </summary>
/// <param name="context"></param>
public void Init(HttpApplication context)
{
context.BeginRequest = (sender, args) =>
{
string relativeAddress = HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.Remove(0, 2);
Type serviceType = WCFRouteTable.GetWCFType(relativeAddress);
if (null == serviceType)
{
return;
}
IHttpHandler handler = new WCFHandler(serviceType);
context.Context.RemapHandler(handler);
};
}
}
通过这行代码
Type serviceType = WCFRouteTable.GetWCFType(relativeAddress);
用户只要请求如下路径
http://localhost/RTMDemo.Host/RTMDemo.Host.WCF.MenuService
就会得到MenuService的类型
然后把服务类型传给指定的处理程序
三:处理请求
在WCFHandler类中最重要的莫过于
处理请求的方法
代码如下
代码语言:javascript复制/// <summary>
/// 处理请求
/// </summary>
/// <param name="context"></param>
public void ProcessRequest(HttpContext context)
{
try
{
List<object> paramList = new List<object>();
JavaScriptSerializer jss = new JavaScriptSerializer();
var MethodKey = context.Request["MethodKey"];
var minfo = WCFRouteTable.GetMethodInfo(MethodKey);
var si = new MethodInvoker(minfo);
ParameterInfo[] ps = minfo.GetParameters();
var pstrs = context.Request.Form.AllKeys.OrderBy(m=>m).ToArray();
var pIndex = 0;
for(var i=0;i<pstrs.Length;i )
{
if (string.IsNullOrEmpty(pstrs[i]))
{
continue;
}
if (pstrs[i].StartsWith("p"))
{
var pStr = context.Request[pstrs[i]];
var obj = jss.Deserialize<object>(pStr);
var bts = Encoding.UTF8.GetBytes(pStr);
MemoryStream mss = new MemoryStream(Encoding.UTF8.GetBytes(pStr));
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(ps[pIndex].ParameterType);
var p = jsonSerializer.ReadObject(mss);
paramList.Add(p);
pIndex = 1;
}
}
//todo:此处性能不佳
var instance = Activator.CreateInstance(ServiceType);
var result = si.Execute(instance,paramList.ToArray());
var ss = jss.Serialize(result);
context.Response.ClearContent();
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.ContentType = "application/json; charset=utf-8";
context.Response.Write(ss);
context.Response.Flush();
}
catch
{
context.Response.Write("我们不提供此服务的元数据~<br />");
context.Response.Write("@@@@@@~<br />@@@@@@@~");
return;
}
}
}
注意:首先说这段代码还有很大的优化空间;也未经过严格的测试;但思路基本就是这样的
处理请求主要做了如下几步工作:
1.
先根据请求POST上来的信息得到准备执行的方法
var MethodKey = context.Request["MethodKey"]; var minfo = WCFRouteTable.GetMethodInfo(MethodKey);
MethodInvoker稍后再讲
2.
按顺序取出了方法的参数,并用DataContractJsonSerializer反序列化成对象
方法参数都是用JSON字符串传递的
3.
通过反射创建了服务的实例
然后调用该实例的方法
得到方法的返回值,并序列化成JSON字符串
4.
把返回值以JSON的形式输出给客户端
四:其他
1.
MethodInvoker是用的老赵的类;具体是哪篇文章,我已经找不到了。
代码语言:javascript复制public class MethodInvoker
{
private Func<object, object[], object> m_execute;
public MethodInvoker(MethodInfo methodInfo)
{
this.m_execute = this.GetExecuteDelegate(methodInfo);
}
public object Execute(object instance, params object[] parameters)
{
return this.m_execute(instance, parameters);
}
private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)
{
ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");
ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
List<Expression> parameterExpressions = new List<Expression>();
ParameterInfo[] paramInfos = methodInfo.GetParameters();
for (int i = 0; i < paramInfos.Length; i )
{
var ce = Expression.Constant(i);
BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter,ce);
UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);
parameterExpressions.Add(valueCast);
}
var instanceE = Expression.Convert(instanceParameter, methodInfo.ReflectedType);
Expression instanceCast = methodInfo.IsStatic ? null : instanceE;
MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions);
if (methodCall.Type == typeof(void))
{
Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceParameter, parametersParameter);
Action<object, object[]> execute = lambda.Compile();
return (instance, parameters) =>
{
execute(instance, parameters);
return null;
};
}
else
{
UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
Expression<Func<object, object[], object>> lambda =Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceParameter, parametersParameter);
return lambda.Compile();
}
}
}
2.
服务类和数据访问的类没有什么特殊的
我这里只公布一个服务的类
代码语言:javascript复制 public class MenuService
{
public List<MenuM> GetAllMenu()
{
using (var DA = new MenuDA())
{
var result = DA.GetAllMenu();
return result;
}
}
public void DelMenu(Guid Id)
{
using (var DA = new MenuDA())
{
DA.DelMenu(Id);
}
}
public void AddMenu(MenuM m)
{
using (var DA = new MenuDA())
{
DA.AddMenu(m);
}
}
public void UpdateMenu(MenuM m)
{
using (var DA = new MenuDA())
{
DA.UpdateMenu(m);
}
}
}
MenuDa就是数据访问类了
很普通,就不在公布代码了
3.
完成这些工作之后
我们只要在客户端构造好表单
然后把表单POST到指定的路径
就能完成服务的访问了!