通过一个模拟程序让你明白WCF大致的执行流程
原文http://www.cnblogs.com/artech/archive/2011/12/07/wcf-how-to-work.html
在《通过一个模拟程序让你明白ASP.NET MVC是如何运行的》一文中我通过一个普通的ASP.NET Web程序模拟了ASP.NET MVC的执行流程,现在我们通过类似的原理创建一个用于模拟WCF服务端和客户端工作原理的模拟程序。[源代码从这里下载]
目录
一、基本的组件和执行流程
二、创建自定义HttpHandler实现对服务调用请求的处理
三、定义创建WCF组件的工厂
四、定义HttpModule映射WcfHandler
五、创建自定义的真实代理实现服务的调用
六、定义服务代理工厂
七、服务“寄宿”和调用
一、基本的组件和执行流程
我们只模拟WCF完成一个简单的服务调用所必需的组件和流程,右图反映了进行服务调用的必要步骤和使用的相关WCF组件。下面列出了服务端涉及的组件和流程:
- 请求消息的接收和回复消息的发送:服务端在传输层监听与接收来自客户的请求,并将经过编码后的回复消息通过传输层发送到客户端;
- 请
求消息的解码和回复消息的编码:将接收到的字节数组通过解码生成请求消息对象,并将回复消息通过编码转化成字节数组。消息的编码和解码通过消息编码器
(MessageEncoder)完成,而消息编码器工厂(MessageEncoderFactory)负责创建该对象; - 请
求消息的反序列化和回复消息的序列化:对请求消息进行反序列化,为服务操作的执行生成相应的输入参数,以及将服务操作执行的结果(返回值或输出/引用参
数)序列化,并生成回复消息。序列化和反序列化通过分发消息格式化器(DispatchMessageFormatter)完成; - 服务对象的创建:创建或激活服务对象实例,实例提供者(InstanceProvider)用于服务对象的创建或获取,本例直接通过反射创建服务实例;
- 服务操作的执行:调用创建的服务对象的操作方法,并传入经过反序列化生成的输入参数。操作调用器(OperationInvoker)完成对服务操作的最终执行。
相较于服务端的请求监听、消息接收、服务实例激活和操作调用流程,客户端的处理流程显得相对简单,仅仅包含以下3个必需的步骤:
- 请求消息的序列化和回复消息的反序列化:生成请求消息并将输入参数序列化到请求消息中,以及对回复消息进行反序列化,转化成方法调用的返回值或输出/引用参数。序列化和反序列化通过ClientMessageFormatter完成;
- 请求消息的编码和回复消息的解码:对请求消息进行编码生成字节数组供传输层发送,以及将传输层接收到的字节数组解码生成回复消息。消息的编码和解码通过消息编码器完成,而消息编码器工厂负责创建该对象;
- 请求消息的发送和回复消息的接收:在传输层将经过编码的请求消息发送到服务端,以及接收来自服务端的回复消息。
本实例的解决方法依然采用包含Service.Interface、Service和Client三个项目的结构,不过Service项目现在是一个Web应用。也就是说我们通过一个Web应用的方式实现WCF端对服务调用请求的整个处理流程。
二、创建自定义HttpHandler实现对服务调用请求的处理
对于一个ASP.NET Web应用来说,对请求的处理最终都落实到一个具体的HttpHandler对象上,所以我们通过实现接口System.Web.IHttpHandler自定义了如下一个WcfHandler用于处理针对WCF服务请求的处理。
1: public class WcfHandler: IHttpHandler
2: {
3: //其他成员
4: public Type ServiceType { get; private set; }
5: public MessageEncoderFactory MessageEncoderFactory { get; private set; }
6: public IDictionary<string, MethodInfo> Methods { get; private set; }
7: public IDictionary<string, IDispatchMessageFormatter> MessageFormatters { get; private set; }
8: public IDictionary<string, IOperationInvoker> OperationInvokers { get; private set; }
9:
10: public bool IsReusable
11: {
12: get { return false; }
13: }
14:
15: public WcfHandler(Type serviceType, MessageEncoderFactory messageEncoderFactory)
16: {
17: this.ServiceType = serviceType;
18: this.MessageEncoderFactory = messageEncoderFactory;
19: this.Methods = new Dictionary<string, MethodInfo>();
20: this.MessageFormatters = new Dictionary<string, IDispatchMessageFormatter>();
21: this.OperationInvokers = new Dictionary<string, IOperationInvoker>();
22: }
23: }
如上面代码所示,上述的关于WCF服务端框架所需的组件以只读属性的方式体现在WcfHandler上。ServiceType属性表示服务的类 型,基于这个类型通过反射创建服务实例。消息编码器工厂通过MessageEncoderFactory属性表示,两个字典类型的属性 MessageFormatters和OperationInvokers代表基于操作的分发消息格式化器和操作调用器列表,字典的Key为操作请求消息 的<Action>报头的值。而Methods表示契约接口所有操作方法的MethodInfo集合。
针对WCF服务的请求处理实现在如下的ProcessRequest方法中,执行的逻辑也不算复杂。我们直接通过消息编码器工厂创建的消息编码从当 前HTTP请求的输入流中读取出消息。然后根据当前消息的<Action>报头的值从MessageFormatters属性中找到与当前请 求操作相匹配的分发消息格式化器对消息进行反序列化。
接着直接通过反射的方式根据服务类型创建服务实例对象。同样根据当前消息的<Action>报头从OperationInvokers属性获取出基于当前请求操作的操作调用器,并将创建的服务实例和反序列化后生成的参数作为输入执行操作方法。
操作的执行结果通过分发消息格式化器进行序列化生成的消息最终通过消息编码器写入当前HTTP回复的输出流中返回给客户端。
1: public class WcfHandler: IHttpHandler
2: {
3: //其他成员
4: public void ProcessRequest(HttpContext context)
5: {
6: //对HttpPRequest进行解码生成请求消息对象
7: Message request = this.MessageEncoderFactory.Encoder.ReadMessage(context.Request.InputStream, int.MaxValue, "application/soap+xml; charset=utf-8");
8:
9: //通过请求消息得到代表服务操作的Action
10: string action = request.Headers.Action;
11:
12: //通过Action从MethodInfo字典中获取服务操作对应的MethodInfo对象
13: MethodInfo method = this.Methods[action];
14:
15: //得到输出参数的数量
16: int outArgsCount = 0;
17: foreach (var parameter in method.GetParameters())
18: {
19: if (parameter.IsOut)
20: {
21: outArgsCount++;
22: }
23: }
24:
25: //创建数组容器,用于保存请求消息反序列后生成的输入参数对象
26: int inputArgsCount = method.GetParameters().Length - outArgsCount;
27: object[] parameters = new object[inputArgsCount];
28: try
29: {
30: this.MessageFormatters[action].DeserializeRequest(request, parameters);
31: }
32: catch
33: {}
34:
35: List<object> inputArgs = new List<object>();
36: object[] outArgs = new object[outArgsCount];
37: //创建服务对象,在WCF中服务对象通过InstanceProvider创建
38:
39: object serviceInstance = Activator.CreateInstance(this.ServiceType);
40:
41: //执行服务操作
42: object result = this.OperationInvokers[action].Invoke(serviceInstance,parameters, out outArgs);
43:
44: //将操作执行的结果(返回值或者输出参数)序列化生成回复消息
45: Message reply = this.MessageFormatters[action].SerializeReply(request.Version, outArgs, result);
46: context.Response.ClearContent();
47: context.Response.ContentEncoding = Encoding.UTF8;
48: context.Response.ContentType = "application/soap+xml; charset=utf-8";
49:
50: //对回复消息进行编码,并将编码后的消息通过HttpResponse返回
51: this.MessageEncoderFactory.Encoder.WriteMessage(reply, context.Response.OutputStream);
52: context.Response.Flush();
53: }
54: }
三、定义创建WCF组件的工厂
对于本例来说,客户端和服务端需要的组件主要有四类,即消息编码器工厂、分发消息格式化器、客户端消息格式化器和操作调用器。我们通过具有如下定义的静态的工厂类ComponentBuilder来创建它们。我们调用操作行为DataContractSerializerOperationBehavior的GetFormatter方法来创建基于指定操作的消息格式化器。不过该方法是一个内部方法,所以我们是通过反射的方式来调用的。isProxy参数表示创建的是客户端消息格式化器(True)还是分发消息格式化器(False)。
消息编码器工厂通过基于文本编码方式绑定元素TextMessageEncodingBindingElement的
CreateMessageEncoderFactory创建,传入的参数分别表示消息的版本和文本编码类型。我们采用
SyncMethodInvoker以同步的方式进行操作的执行。由于SyncMethodInvoker是一个内部类型,所以我们不得不采用反射的方式
来创建它。
1: public static class ComponentBuilder
2: {
3: public static object GetFormatter(OperationDescription operation, bool isProxy)
4: {
5: bool formatRequest = false;
6: bool formatReply = false;
7: DataContractSerializerOperationBehavior behavior = new DataContractSerializerOperationBehavior(operation);
8: MethodInfo method = typeof(DataContractSerializerOperationBehavior).GetMethod("GetFormatter", BindingFlags.Instance | BindingFlags.NonPublic);
9: return method.Invoke(behavior, new object[] { operation, formatRequest, formatReply, isProxy });
10: }
11:
12: public static MessageEncoderFactory GetMessageEncoderFactory(MessageVersion messageVersion, Encoding writeEncoding)
13: {
14: TextMessageEncodingBindingElement bindingElement = new TextMessageEncodingBindingElement(messageVersion, writeEncoding);
15: return bindingElement.CreateMessageEncoderFactory();
16: }
17: public static IOperationInvoker GetOperationInvoker(MethodInfo method)
18: {
19: string syncMethodInvokerType = "System.ServiceModel.Dispatcher.SyncMethodInvoker, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
20: Type type = Type.GetType(syncMethodInvokerType);
21: return (IOperationInvoker)Activator.CreateInstance(type, new object[]{method});
22: }
23: }
四、定义HttpModule映射WcfHandler
我们通过HttpModule的方式将用于处理WCF服务请求的映射到相应的WCF服务调用请求,为此我们定义了如下一个实现了
System.Web.IHttpModule接口的WcfHttpModule类型。WcfHttpModule通过注册
HttpApplication的BeginRequest事件的方式将创建的WcfHandler映射为处理当前HTTP请求的
HttpHandler。
1: public class WcfHttpModule: IHttpModule
2: {
3: public void Dispose() {}
4:
5: public void Init(HttpApplication context)
6: {
7: context.BeginRequest += (sender, args) =>
8: {
9: string relativeAddress = HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.Remove(0,2);
10: Type serviceType = RouteTable.Routes.Find(relativeAddress);
11: if (null == serviceType)
12: {
13: return;
14: }
15: IHttpHandler handler = this.CreateHttpHandler(serviceType);
16: context.Context.RemapHandler(handler);
17: };
18: }
19: protected IHttpHandler CreateHttpHandler(Type serviceType)
20: {
21: MessageEncoderFactory encoderFactory = ComponentBuilder.GetMessageEncoderFactory(MessageVersion.Default, Encoding.UTF8);
22: WcfHandler handler = new WcfHandler(serviceType, encoderFactory);
23: Type interfaceType = serviceType.GetInterfaces()[0];
24: ContractDescription contract = ContractDescription.GetContract(interfaceType);
25: foreach (OperationDescription operation in contract.Operations)
26: {
27: IDispatchMessageFormatter messageFormatter = (IDispatchMessageFormatter)ComponentBuilder.GetFormatter(operation, false);
28: handler.MessageFormatters.Add(operation.Messages[0].Action, messageFormatter);
29:
30: IOperationInvoker operationInvoker = ComponentBuilder.GetOperationInvoker(operation.SyncMethod);
31: handler.OperationInvokers.Add(operation.Messages[0].Action, operationInvoker);
32:
33: handler.Methods.Add(operation.Messages[0].Action, operation.SyncMethod);
34: }
35: return handler;
36: }
37: }
至于WcfHandler的创建,需要确定服务的类型。而服务的类型只能根据请求的地址来确定,这个IIS寄宿根据.svc文件来创建 ServiceHost的原理是一样的。对于本例来说,我们需要对请求的地址和服务类型作一个映射,为此我们定义了如下一个RouteMapping的类 型表示这个映射。
1: public class RouteMapping
2: {
3: public string Address { get; private set; }
4: public Type ServiceType { get; private set; }
5: public RouteMapping(string address, Type serviceType)
6: {
7: this.Address = address;
8: this.ServiceType = serviceType;
9: }
10: }
而映射表则通过如下一个继承自Collection<RouteMapping>的RouteTable来定义。泛型的 Register<T>方法用于注册地址与服务类型的映射关系,而Find方法则根据地址获取相应的服务类型。静态属性Routes表示当前 被使用的映射表,而在WcfHttpModule中正是通过这个静态属性根据解析出来的地址得到用于创建WcfHandler的服务类型的。
1: public class RouteTable: Collection<RouteMapping>
2: {
3: public static RouteTable Routes{get; private set;}
4: static RouteTable()
5: {
6: Routes = new RouteTable();
7: }
8: public Type Find(string address)
9: {
10: RouteMapping routeMapping = (from route in this
11: where string.Compare(route.Address, address,true) == 0
12: select route).FirstOrDefault();
13: return null == routeMapping? null: routeMapping.ServiceType;
14: }
15:
16: public void Register<T>(string address)
17: {
18: this.Add(new RouteMapping(address, typeof(T)));
19: }
20: }
五、创建自定义的真实代理实现服务的调用
ChannelFactory<TChannel>创建的服务代理仅仅是一个透明代理,而真实实现服务调用的是它的真实代理。为此我们
创建了如下一个继承自RealProxy的泛型的ServiceChannelProxy<TChannel>,其中泛型参数为契约接口类
型。
1: public class ServiceChannelProxy<TChannel>: RealProxy
2: {
3: //其他成员
4: public Uri Address { get; private set; }
5: public MessageVersion MessageVersion { get; private set; }
6: public IDictionary<string, IClientMessageFormatter> MessageFormatters { get; private set; }
7: public MessageEncoderFactory MessageEncoderFactory { get; private set; }
8:
9: public ServiceChannelProxy(Uri address, MessageVersion messageVersion, MessageEncoderFactory encoderFactory): base(typeof(TChannel))
10: {
11: this.Address = address;
12: this.MessageVersion = messageVersion;
13: this.MessageEncoderFactory = encoderFactory;
14: this.MessageFormatters = new Dictionary<string, IClientMessageFormatter>();
15: }
16: }
和WcfHttpHandler类似,进行服务调用所需的组件通过相应的只读属性表示。属性MessageEncoderFactory表示消息编 码器工厂,而字典类型的MessageFormatters表示基于每个操作的客户端消息格式化器列表,其中的Key为操作的名称。属性Address表 示被调用服务的地址。
针对透明代理的方法调用最终都会转移到针对真实真实代理的Invoke方法,所以我们将所有的服务调用操作实现在如下的Invoke方法中。我们首 先获取代表当前调用方法的MethodBase上应用的OperationContractAttribute特性,并借此获得操作名称。
根据获取的操作名称从属性MessageFormatters属性中获得基于当前操作的客户端消息格式化器,并将方法调用转化消息。接着根据 Address属性表示的服务调用地址创建EndpointAddress对象并将其附加到请求消息中。除此之外,还需要为请求消息添加一些必要的报头 (比如<MessageId>和<ReplyTo>)。
接下来通过消息编码器工厂创建的消息编码器对消息进行编码,并将得到的字节数据通过创建的HttpWebRequest对象发送出去。对于得到的 HttpWebResponse,则通过消息编码器进行解码以生成回复消息。回复消息最终通过客户端消息格式化器进行反序列化,得到的对象映射为方法返回 值和输出/引用参数返回。
1: public class ServiceChannelProxy<TChannel>: RealProxy
2: {
3: //其他成员
4: public override IMessage Invoke(IMessage msg)
5: {
6: IMethodCallMessage methodCall = (IMethodCallMessage)msg;
7:
8: //得到操作名称
9: object[] attributes = methodCall.MethodBase.GetCustomAttributes(typeof(OperationContractAttribute), true);
10: OperationContractAttribute attribute = (OperationContractAttribute)attributes[0];
11: string operationName = string.IsNullOrEmpty(attribute.Name) ? methodCall.MethodName : attribute.Name;
12:
13: //序列化请求消息
14: Message requestMessage = this.MessageFormatters[operationName].SerializeRequest(this.MessageVersion, methodCall.InArgs);
15:
16: //添加必要的WS-Address报头
17: EndpointAddress address = new EndpointAddress(this.Address);
18: requestMessage.Headers.MessageId = new UniqueId(Guid.NewGuid());
19: requestMessage.Headers.ReplyTo = new EndpointAddress("http://www.w3.org/2005/08/addressing/anonymous");
20: address.ApplyTo(requestMessage);
21:
22: //对请求消息进行编码,并将编码生成的字节发送通过HttpWebRequest向服务端发送
23: HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(this.Address);
24: webRequest.Method = "Post";
25: webRequest.KeepAlive = true;
26: webRequest.ContentType = "application/soap+xml; charset=utf-8";
27: ArraySegment<byte> bytes = this.MessageEncoderFactory.Encoder.WriteMessage(requestMessage, int.MaxValue, BufferManager.CreateBufferManager(long.MaxValue, int.MaxValue));
28: webRequest.ContentLength = bytes.Array.Length;
29: webRequest.GetRequestStream().Write(bytes.Array, 0, bytes.Array.Length);
30: webRequest.GetRequestStream().Close();
31: WebResponse webResponse = webRequest.GetResponse();
32:
33: //对HttpResponse进行解码生成回复消息.
34: Message responseMessage = this.MessageEncoderFactory.Encoder.ReadMessage(webResponse.GetResponseStream(), int.MaxValue);
35:
36: //回复消息进行反列化生成相应的对象,并映射为方法调用的返回值或者ref/out参数
37: object[] allArgs = (object[])Array.CreateInstance(typeof(object),methodCall.ArgCount);
38: Array.Copy(methodCall.Args, allArgs, methodCall.ArgCount);
39: object[] refOutParameters = new object[GetRefOutParameterCount(methodCall.MethodBase)];
40: object returnValue = this.MessageFormatters[operationName].DeserializeReply(responseMessage, refOutParameters);
41: MapRefOutParameter(methodCall.MethodBase, allArgs, refOutParameters);
42:
43: //通过ReturnMessage的形式将返回值和ref/out参数返回
44: return new ReturnMessage(returnValue, allArgs, allArgs.Length, methodCall.LogicalCallContext, methodCall);
45: }
46:
47: private int GetRefOutParameterCount(MethodBase method)
48: {
49: int count = 0;
50: foreach (ParameterInfo parameter in method.GetParameters())
51: {
52: if (parameter.IsOut || parameter.ParameterType.IsByRef)
53: {
54: count++;
55: }
56: }
57: return count;
58: }
59:
60: private void MapRefOutParameter(MethodBase method, object[] allArgs,object[] refOutArgs)
61: {
62: List<int> refOutParamPositionsList = new List<int>();
63: foreach (ParameterInfo parameter in method.GetParameters())
64: {
65: if (parameter.IsOut || parameter.ParameterType.IsByRef)
66: {
67: refOutParamPositionsList.Add(parameter.Position);
68: }
69: }
70: int[] refOutParamPositionArray = refOutParamPositionsList.ToArray();
71: for (int i = 0; i < refOutArgs.Length; i++)
72: {
73: allArgs[refOutParamPositionArray[i]] = refOutArgs[i];
74: }
75: }
76: }
六、定义服务代理工厂
WCF的服务代理对象是通过ChannelFactory<TChannel>创建的,我们来创建如下一个与之对应的
ServiceProxyFactory<TChannel>类,泛型参数依然表示契约接口类型。CreateChannel方法中通过表示
服务地址的Uri,契约接口类型和默认消息版本创建上述的真实代理ServiceChannelProxy<TChannel>对象,并返回
其透明代理作为进行服务调用的代理对象。
1: public class ServiceProxyFactory<TChannel>
2: {
3: public Uri Address { get; private set; }
4:
5: public ServiceProxyFactory(Uri address)
6: {
7: this.Address = address;
8: }
9: public TChannel CreateChannel()
10: {
11: MessageEncoderFactory encoderFactory = ComponentBuilder.GetMessageEncoderFactory(MessageVersion.Default, Encoding.UTF8);
12: ServiceChannelProxy<TChannel> proxy = new ServiceChannelProxy<TChannel>(this.Address, MessageVersion.Default, encoderFactory);
13: ContractDescription contract = ContractDescription.GetContract(typeof(TChannel));
14: foreach (OperationDescription operation in contract.Operations)
15: {
16: IClientMessageFormatter messageFormatter = (IClientMessageFormatter)ComponentBuilder.GetFormatter(operation, true);
17: proxy.MessageFormatters.Add(operation.Name, messageFormatter);
18: }
19: return (TChannel)proxy.GetTransparentProxy();
20: }
21: }
七、服务“寄宿”和调用
现在我们创建一个服务寄宿在我们自定义的迷你版本的WCF中。依然采用我们熟悉的计算服务,下面是分别定义的Service.Interface和Service项目中的契约接口定义和服务类型定义。
1: //契约接口
2: [ServiceContract(Namespace = "http://www.artech.com/")]
3: public interface ICalculator
4: {
5: [OperationContract]
6: double Add(double x, double y);
7: [OperationContract]
8: double Subtract(double x, double y);
9: [OperationContract]
10: double Multiply(double x, double y);
11: [OperationContract]
12: double Divide(double x, double y);
13: }
14:
15: //服务类型
16: public class CalculatorService: ICalculator
17: {
18: public double Add(double x, double y)
19: {
20: return x + y;
21: }
22: public double Subtract(double x, double y)
23: {
24: return x - y;
25: }
26: public double Multiply(double x, double y)
27: {
28: return x * y;
29: }
30: public double Divide(double x, double y)
31: {
32: return x / y;
33: }
34: }
然后我们为Web项目Service中添加一个Global.asax文件,并通过如下的定义让Web应用启动的时候注册寄宿的服务类型 CalculatorService和地址(calculatorservice)之间的映射关系。然后在IIS中创建一个Web应用(比如起名为 WcfServices)并将物理路径映射为Service项目的根目录。
1: public class Global : System.Web.HttpApplication
2: {
3: protected void Application_Start(object sender, EventArgs e)
4: {
5: RouteTable.Routes.Register<CalculatorService>("calculatorservice");
6: }
7: }
由于最终处理服务调用请求的WcfHandler是通过WcfHttpModule进行映射的,所以我们需要将WcfHttpModule类型配置在Service项目的Web.config中。
1: <configuration>
2: <system.webServer>
3: <modules>
4: <add name="WcfHttpModule" type="Artech.WcfServices.Service.WcfHttpModule, Artech.WcfServices.Service"/>
5: </modules>
6: </system.webServer>
7: </configuration>
在客户端我们只需要按照如下的方式通过指定正确的调用地址(Web应用地址+在Global.asax文件中添加的路由映射的地址)创建ServiceProxyFactory<TChannel>对象,并用它来创建用于尽心服务调用的代理对象即可。
1: Uri address = new Uri("http://localhost/WcfServices/CalculatorService");
2: ServiceProxyFactory<ICalculator> factory = new ServiceProxyFactory<ICalculator>(address);
3: ICalculator proxy = factory.CreateChannel();
4: Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2));
5: Console.WriteLine("x - y = {2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2));
6: Console.WriteLine("x * y = {2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2));
7: Console.WriteLine("x / y = {2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2));
上面的代码执行之后,就像你真正调用WCF服务一样,同样可以得到如下的运算结果。
1: x + y = 3 when x = 1 and y = 2
2: x - y = -1 when x = 1 and y = 2
3: x * y = 2 when x = 1 and y = 2
4: x / y = 0.5 when x = 1 and y = 2
出处:http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
通过一个模拟程序让你明白WCF大致的执行流程的更多相关文章
- 通过一个模拟程序让你明白ASP.NET MVC是如何运行的
ASP.NET MVC的路由系统通过对HTTP请求的解析得到表示Controller.Action和其他相关的数据,并以此为依据激活Controller对象,调用相应的Action方法,并将方法返回的 ...
- WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构
原文:WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构 细算起来,已经有好几个月没有真正的写过文章了.近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析>的写作,一直 ...
- [每日电路图] 7、设计一个PCB的流程及细节·总结——给外行的同学或刚入行的同学一个宏观鸟瞰电路板设计的大致流程的文章
前言 最近两天使用AD14软件设计了一个蓝牙防丢器电路板(PCB)图纸,中间有一些细节在本文中记录下,方便下次设计PCB时参考.也希望能给外行的同学或刚入行的同学一个宏观鸟瞰电路板设计的大致流程的文章 ...
- ASP.NET MVC提交一个较复杂对象至WCF Service
前一篇<jQuery.Ajax()执行WCF Service的方法>http://www.cnblogs.com/insus/p/3727875.html 我们有练习在asp.net mv ...
- 一个简单的mfc单页界面文件读写程序(MFC 程序入口和执行流程)
参考:MFC 程序入口和执行流程 http://www.cnblogs.com/liuweilinlin/archive/2012/08/16/2643272.html 程序MFCFlie ...
- python编程系列---多个装饰器装饰一个函数的执行流程
首先看一个例子 ''' 多个装饰器装饰一个函数 ''' # 定义第一个装饰器 def set_func1(func): def wrapper1(*args,**kwargs): print('装饰内 ...
- jquery动画函数里面可以跟一个回调函数,表示动画结束后执行的代码
jquery动画函数里面可以跟一个回调函数,表示动画结束后执行的代码 使用js监听动画结束后进行的操作: $ele.fadeIn(300,function(){...}) $ele.fadeOut(3 ...
- 一个 Spark 应用程序的完整执行流程
一个 Spark 应用程序的完整执行流程 1.编写 Spark Application 应用程序 2.打 jar 包,通过 spark-submit 提交执行 3.SparkSubmit 提交执行 4 ...
- UNION 和 UNION ALL的区别,一个例子就看明白
[UNION ALL] select a,b,sum(sm) AS s1, SUM(qm) AS s2 from ( select 'a' AS a, 'b' AS b, 2 AS sm, 200 A ...
随机推荐
- Ubuntu14.04(64位)安装ATI_Radeon_R7_M265显卡驱动
电脑型号:Dell inspiron 14-5447 笔记本 显卡配置:集成显卡Intel核心显卡,Cpu是i5-4210U;独立显卡ATI_Radeon_R7_M265 网上关于ATI/Intel双 ...
- shell脚本定时备份数据库
脚本代码: 新建文件back_db.sh #!/bin/bash TODAYTIME="`date +%Y%m%d`" DBNAME="test mysql" ...
- WARNING:Could not increase the asynch I/O limit to 64 for SQL direct I/O. It is set to 0
今天是2014-01-07,解决一下hp-unix异步I/O问题. 从trace日志中看: WARNING:Could not increase the asynch I/O limit to 32 ...
- window.showModalDialog的基本用法
window.showModalDialog的基本用法 showModalDialog() (IE 4+ 支持) showModelessDialog() (IE 5+ 支持) window.show ...
- Android 开发中常用小技巧
TextView中的getTextSize返回值是以像素(px)为单位的, 而setTextSize()是以sp为单位的. 所以如果直接用返回的值来设置会出错,解决办法是 用setTextSize() ...
- Javascript 字符串浏览器兼容问题
先看下不兼容的写法,若我想获取某个字符串的第几位 var str='aavvvcc'; console.info(str[0]); 这种写法 在IE 7以下的浏览器都不兼容,以下提供浏览器全兼容的方式 ...
- codeforces 10 D. LCIS LCIS O(n^2)算法
题目链接 给出两个序列, 求出他们的最长公共上升子序列. 两层循环, 内层循环j, 外层i. 如果a[i] == b[j], 那么dp[j] = max(dp[j], dp[best]+1), bes ...
- SQL Server 完成性检查的顺序
第一步: 默认值 第二步: 违反not null 限制 第三步: 判断check约束 第四步: 对引用表应用foreign key 检查 第五步: 对被引用表做 foreign key 检查 第六步: ...
- #pragma anon_unions, #pragma no_anon_unions
#pragma anon_unions, #pragma no_anon_unions 这些编译指示启用和禁用对匿名结构和联合的支持. 缺省设置 缺省值为 #pragma no_anon_unions ...
- KJFrameForAndroid框架学习----高效设置网络图片
KJFrameForAndroid框架项目地址:https://github.com/kymjs/KJFrameForAndroid 或备用地址http://git.oschina.net/kymjs ...