WCF

一、WCF合并了 ASP.NET Web服务,.NET Remoting技术,消息队列,.NET Enterprise Service(支持自动事物处理)技术。

WCF服务可以存放在ASP.NET运行库,windows服务,COM+进程或windows窗体应用程序中进行对等计算。

WCF提供了HTTP、TCP和IPC信道进行通信的多条信道。也可以创建使用不同传输协议的自定义信道。

WSDL:提供描述服务的元数据。

简单实现的服务和客户端

服务协定

定义服务协定的接口 IRoomService(RoomReservation实体类)

View Code
 [ServiceContract()]
    public interface IRoomService
    {
        [OperationContract]
        bool ReserveRoom(RoomReservation roomReservation);

        [OperationContract]
        RoomReservation[] GetRoomReservations(DateTime fromDate, DateTime toDate);
    }

实现IRoomService

View Code
public class RoomReservationService : IRoomService
    {
        public bool ReserveRoom(RoomReservation roomReservation)
        {
            var data = new RoomReservationData();
            data.ReserveRoom(roomReservation);
            return true;
        }

        
        public RoomReservation[] GetRoomReservations(DateTime fromDate, DateTime toDate)
        {
            var data = new RoomReservationData();
            return data.GetReservations(fromDate, toDate);
        }
    }

配置文件(可以右键配置文件动态配置)

View Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.diagnostics>
    <trace autoflush="true" />
  </system.diagnostics>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- 部署服务库项目时,必须将配置文件的内容添加到 
  主机的 app.config 文件中。System.Configuration 不支持库的配置文件。-->
  <system.serviceModel>
    <services>
      <service name="RoomReservationService.RoomReservationService">
        <endpoint address="" name="haha" binding="wsHttpBinding" contract="RoomReservationService.IRoomService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8735/Design_Time_Addresses/RoomReservationService/aa/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- 为避免泄漏元数据信息,
          请在部署前将以下值设置为 false 并删除上面的元数据终结点  -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- 要接收故障异常详细信息以进行调试,
          请将以下值设置为 true。在部署前设置为 false 
            以避免泄漏异常信息-->
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

</configuration>

自定义服务宿主(包含跟踪错误消息:在配置文件配置可以使用工具svctraceviewer.exe查看错误信息)

View Code
 class Program
    {
        internal static ServiceHost myServiceHost = null;
        /// <summary>
        /// 启动服务
        /// </summary>
        internal static void StartService()
        {
            myServiceHost = new ServiceHost(typeof(RoomReservationService));
            myServiceHost.Open();

        }
        /// <summary>
        /// 停止服务
        /// </summary>
        internal static void StopService()
        {
            if (myServiceHost.State != CommunicationState.Closed)
            {
                myServiceHost.Close();
            }
        }
        static void Main()
        {
            StartService();
            Console.WriteLine("Server is runnin. Press return to exit");
            Console.ReadLine();
            StopService();
        }
    }

宿主的配置文件

View Code
<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="RoomReservationsEntities" connectionString="metadata=res://*/RoomReservationModel.csdl|res://*/RoomReservationModel.ssdl|res://*/RoomReservationModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=(local)\sqlexpress;Initial Catalog=RoomReservations;Integrated Security=True;Pooling=False;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient"/>
  </connectionStrings>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging" switchValue="Verbose,ActivityTracing">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type=""/>
          </add>
          <add name="ServiceModelMessageLoggingListener">
            <filter type=""/>
          </add>
        </listeners>
      </source>
      <source name="System.ServiceModel" switchValue="Verbose,ActivityTracing" propagateActivity="true">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type=""/>
          </add>
          <add name="ServiceModelTraceListener">
            <filter type=""/>
          </add>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add initializeData="c:\code\wcf\roomreservation\roomreservationhost\app_messages.svclog" type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
        <filter type=""/>
      </add>
      <add initializeData="c:\code\wcf\roomreservation\roomreservationhost\app_tracelog.svclog" type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" name="ServiceModelTraceListener" traceOutputOptions="Timestamp">
        <filter type=""/>
      </add>
    </sharedListeners>
    <trace autoflush="true"/>
  </system.diagnostics>
  <system.web>
    <compilation debug="true"/>
  </system.web>
  <!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. -->
  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true"/>
      <endToEndTracing propagateActivity="true" activityTracing="true" messageFlowTracing="true"/>
    </diagnostics>
    <services>
      <service name="Wrox.ProCSharp.WCF.RoomReservationService">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/RoomReservationService/aa/"/>
          </baseAddresses>
        </host>
        <!-- Service Endpoints -->
        <!-- Unless fully qualified, address is relative to base address supplied above -->
        <endpoint address="" binding="wsHttpBinding" contract="Wrox.ProCSharp.WCF.IRoomService">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <!-- Metadata Endpoints -->
        <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. --> 
        <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

客服端就是添加引用服务,也可以编程的方式引用服务

 

二、WCF的协定:数据协定;服务协定;消息协定。

数据协定的属性 DataCOntrace(Namespace可以解决版本的问题)和DataMember

View Code
 [DataContract(Name = "RoomReservation", Namespace = "http://schemas.datacontract.org/2004/07/Wrox.ProCSharp.WCF", IsReference = true)]
    public class NewModel
    {
        [DataMemberAttribute(Name = "ID")]
        public int ID { get; set; }
        [DataMember(Name = "RoomName")]
        public string RoomName { get; set; }
        [DataMember(Name = "StartDate")]
        public DateTime StartDate { get; set; }
        [DataMember(Name = "EndDate")]
        public DateTime EndDate { get; set; }
        [DataMember(Name = "Contact")]
        public string Contact { get; set; }
        [DataMember(Name = "Event")]
        public string Event { get; set; }
    }

服务协定的属性ServiceContract和OperationContract

View Code
 [ServiceContract()]
    public interface IRoomService
    {
        [OperationContract]
        bool ReserveRoom(RoomReservation roomReservation);

        [OperationContract]
        RoomReservation[] GetRoomReservations(DateTime fromDate, DateTime toDate);
    }

消息的协定的属性MessageContract,MessageHeader和MessageBodyMember(Order可以知道元素顺序)

View Code
 [MessageContract]
    public class ProcessPersonRequestMessage
    {
        [MessageHeader]
        public int employeeId;
        [MessageBodyMember(Order= 0)]
        public Person person;
    }
    [ServiceContract]
    public interface IProcessPerson
    {
        [OperationContract]
        public ProcessPersonRequestMessage ProcessPerson(ProcessPersonRequestMessage message);
    }
    public class Person
    { }

三、服务的实现

实现服务的属性ServiceBehavior可以设置同步,事物,会话等问题

View Code
 [ServiceBehavior]
    public class RoomReservationService : IRoomService
    {
        public bool ReserveRoom(RoomReservation roomReservation)
        {
            var data = new RoomReservationData();
            data.ReserveRoom(roomReservation);
            return true;
        }

        
        public RoomReservation[] GetRoomReservations(DateTime fromDate, DateTime toDate)
        {
            var data = new RoomReservationData();
            return data.GetReservations(fromDate, toDate);
        }
    }

1、如果接口[ServiceContract( SessionMode=SessionMode.Required)]和实现接口的类设置成[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)],那么配置文件必须配置会话状态的wsHttpBinding

View Code

2、WSHttpBinding,WSDualHttpbing等支持会话就是。(P1355)

四、以编程方式创建客户端

ChannelFactory<TChannel>类

View Code
 class Program
    {
        static void Main()
        {
            Console.WriteLine("wait for service...");
            Console.ReadLine();
            var binding = new WSHttpBinding();
            var address = new EndpointAddress("http://localhost:8732/Design_Time_Addresses/StateServiceSample/Service1/");

            var factory = new ChannelFactory<IStateService>(binding, address);

            IStateService channel = factory.CreateChannel();
            channel.Init(1);
            Console.WriteLine(channel.GetState());
            channel.SetState(2);
            Console.WriteLine(channel.GetState());
            try
            {
                channel.SetState(-1);
            }
            catch (FaultException<StateFault> ex)
            {
                Console.WriteLine(ex.Message);
                StateFault detail = ex.Detail;
                Console.WriteLine(detail.BadState);

            }

            channel.Close();

            factory.Close();

            Console.ReadLine();

        }
    }

五、错误处理

1、默认情况下WCF服务不会把错误消息发送给客服端<serviceMetadata httpGetEnabled="True"/>这样就发送了

View Code
  <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

2.1、可以抛出一个FaultException异常 在接口里面设置方法的FaultContract属性

View Code
[ServiceContract(SessionMode = SessionMode.Required)]
   public interface IStateService
   {
      [OperationContract(IsInitiating = true)]
      void Init(int i);

      [FaultContract(typeof(StateFault))]
      [OperationContract]
      void SetState(int i);

      [OperationContract]
      int GetState();

      [OperationContract(IsTerminating = true)]
      void Close();
   }

2.2 可以手动抛一个

View Code
 public void SetState(int i)
      {
         if (i == -1)
         {
            FaultReasonText[] text = new FaultReasonText[2];
            text[0] = new FaultReasonText("Sample Error", new CultureInfo("en"));
            text[1] = new FaultReasonText("Beispiel Fehler", new CultureInfo("de"));
            FaultReason reason = new FaultReason(text);

            throw new FaultException<StateFault>(
               new StateFault() { BadState = i }, reason);
         }
         else
         {
            this.i = i;
         }
      }

2.3客服端就可以直接try catch了

View Code
  try
            {
                channel.SetState(-1);
            }
            catch (FaultException<StateFault> ex)
            {
                Console.WriteLine(ex.Message);
                StateFault detail = ex.Detail;
                Console.WriteLine(detail.BadState);

            }

六 、绑定(自己理解就是设置服务的url和启动服务)P1355页 预定义绑定包含WSHttpBinding等

1、编程绑定

View Code
 static ServiceHost host;
        static void StartService()
        {
            var haseAddress = new Uri("http://localhost:8732/Design_Time_Addresses/StateServiceSample/Service1/");
            host = new ServiceHost(typeof(StateService));
            var binding1 = new WSHttpBinding();
            host.AddServiceEndpoint(typeof(IStateService), binding1, haseAddress);
            host.Open();
        }

2、配置文件绑定

View Code
<system.serviceModel>
    <services>
      <service name="Wrox.ProCSharp.WCF.StateService">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8732/Design_Time_Addresses/StateServiceSample/Service1/" />
          </baseAddresses>
        </host>
        <!-- Service Endpoints -->
        <!-- Unless fully qualified, address is relative to base address supplied above -->
        <endpoint address ="" binding="wsHttpBinding" contract="Wrox.ProCSharp.WCF.IStateService">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <!-- Metadata Endpoints -->
        <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. --> 
        <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

七、宿主 ServiceHost这个要配置文件配置然后才代码启动

View Code
 using (var sercviceHost = new ServiceHost())
            {
                sercviceHost.Open();
                Console.WriteLine();
                //回车结束
                sercviceHost.Close();
            }

八、WAS宿主

九、预配置的宿主类

 class Program
    {
        static void Main()
        {
            Uri baseAddress = new Uri("http://localhost:8732/RoomReservation");
            var host = new WebServiceHost(typeof(RoomReservationService), baseAddress);
            host.Open();
            

            Console.WriteLine("service running");
            Console.WriteLine("Press return to exit...");
            Console.ReadLine();

            if (host.State == CommunicationState.Opened)
                host.Close();
        }
    }
View Code
 [WebGet(UriTemplate="Reservations?From={fromDate}&To={toDate}")]
        public RoomReservation[] GetRoomReservations(DateTime fromDate, DateTime toDate)
        {
            var data = new RoomReservationData();
            return data.GetReservations(fromDate, toDate);
        }

十 客户端访问WSDL(服务的元数据)配置文件设置mexHttpBinding。

十一、双工通信

1、支持双工通信的绑定是wsDualHttpBinding

View Code
<configuration>
    <startup> 
       <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client" />             
    </startup>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="" name="Wrox.ProCSharp.WCF.MessageService">
        <endpoint address="" binding="wsDualHttpBinding" bindingConfiguration=""
          contract="Wrox.ProCSharp.WCF.IMyMessage">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8732/Design_Time_Addresses/MessageService/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors />
  </system.serviceModel>
</configuration>

2、服务协定由IMymessage接口定义。回调协定用服务协定定义的CallbackContract属性映射到服务协定上

View Code
 public interface IMyMessageCallback
   {
      [OperationContract(IsOneWay = true)]
      void OnCallback(string message);
   }

   [ServiceContract(CallbackContract = typeof(IMyMessageCallback))]
   public interface IMyMessage
   {
      [OperationContract]
      void MessageToServer(string message);
   }

3、实现服务协定的类MessageService

View Code
 public class MessageService : IMyMessage
   {
      public void MessageToServer(string message)
      {
         Console.WriteLine("message from the client: {0}", message);
         IMyMessageCallback callback =
               OperationContext.Current.
                     GetCallbackChannel<IMyMessageCallback>();

         callback.OnCallback("message from the server");

         Task.Factory.StartNew(new Action<object>(TaskCallback), callback);
         // new Thread(ThreadCallback).Start(callback);
      }
      private void TaskCallback(object callback)
      {
          IMyMessageCallback messageCallback = callback as IMyMessageCallback;
          for (int i = 0; i < 10; i++)
          {
              messageCallback.OnCallback("message " + i.ToString());
              Thread.Sleep(1000);
          }
      }

      //private void ThreadCallback(object callback)
      //{
      //   IMyMessageCallback messageCallback = callback as IMyMessageCallback;
      //   for (int i = 0; i < 10; i++)
      //   {
      //      messageCallback.OnCallback("message " + i.ToString());
      //      Thread.Sleep(1000);
      //   }
      //}
   }

配置文件

View Code
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. -->
  <system.serviceModel>
    <services>
      <service name="Wrox.ProCSharp.MessageService">
        <host>
          <baseAddresses>
            <add baseAddress = "http://localhost:8732/Design_Time_Addresses/MessageService/Service1/" />
          </baseAddresses>
        </host>
        <!-- Service Endpoints -->
        <!-- Unless fully qualified, address is relative to base address supplied above -->
        <endpoint address ="" binding="wsdualHttpBinding" contract="Wrox.ProCSharp.WCF.IMyMessage">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <!-- Metadata Endpoints -->
        <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. --> 
        <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

客户端

View Code
class ClientCallback : IMyMessageCallback
   {
      public void OnCallback(string message)
      {
         Console.WriteLine("message from the server: {0}", message);
      }
   }


   class Program
   {
      static void Main()
      {
         Console.WriteLine("Client - wait for service");
         Console.ReadLine();

         var binding = new WSDualHttpBinding();
         var address =
               new EndpointAddress("http://localhost:8732/Design_Time_Addresses/MessageService/Service1/");

         var clientCallback = new ClientCallback();
         var context = new InstanceContext(clientCallback);

         var factory = new DuplexChannelFactory<IMyMessage>(context, binding, address);

         IMyMessage messageChannel = factory.CreateChannel();

         messageChannel.MessageToServer("From the client");

         Console.WriteLine("Client - press return to exit");
         Console.ReadLine();

      }
   }

启动服务

View Code
   class Program
   {
      internal static ServiceHost myServiceHost = null;

      internal static void StartService()
      {
         //Instantiate new ServiceHost 
         myServiceHost = new ServiceHost(typeof(Wrox.ProCSharp.WCF.MessageService));

         //Open myServiceHost
         myServiceHost.Open();
      }

      internal static void StopService()
      {
         //Call StopService from your shutdown logic (i.e. dispose method)
         if (myServiceHost.State != CommunicationState.Closed)
            myServiceHost.Close();
      }

      static void Main()
      {
         StartService();
         Console.WriteLine("Service running; press return to exit");
         Console.ReadLine();
         StopService();
         Console.WriteLine("stopped");
      }
   }

 

posted on 2013-02-24 20:49  R.Ray  阅读(364)  评论(0)    收藏  举报

导航