Siêu thị PDFTải ngay đi em, trời tối mất

Thư viện tri thức trực tuyến

Kho tài liệu với 50,000+ tài liệu học thuật

© 2023 Siêu thị PDF - Kho tài liệu học thuật hàng đầu Việt Nam

Programming C# 4.0 phần 7 pdf
PREMIUM
Số trang
86
Kích thước
10.4 MB
Định dạng
PDF
Lượt xem
1626

Programming C# 4.0 phần 7 pdf

Nội dung xem thử

Mô tả chi tiết

To make life easy for developers, Visual Studio’s installer sets up a special range of

addresses with an ACL that makes it open for any user logged in to the machine. Lis￾tening on anything starting with http://localhost:8732/Design_Time_Addresses/ will

work, even if you’re logged on with a nonadministrative account. That’s why Visual

Studio chooses the base address you see in Example 13-3—it means you don’t need to

run with elevated privileges.

After the <services> element you’ll see a <behaviors> element in your App.config, con￾taining a <serviceBehaviors> element which contains a <behavior> element. This sec￾tion allows various WCF features to be switched on or off. You might wonder why

these settings don’t just go into the <services> section. The reason is that you might

want to host multiple services, all of which share common behavior configuration. You

can define a single named <behavior> element, and then point multiple <service> ele￾ments’ behaviorConfiguration attributes at that behavior, reducing clutter in your con￾figuration file. Or, as in this case, you can create an unnamed <behavior> element, which

defines default behavior that applies to all services in this host process. Since we’re

hosting only one service here, this doesn’t offer much advantage, but this separation

can be useful when hosting multiple services.

The <behavior> element that Visual Studio provides has some comments telling you

what you might want to change and why, but paring it down to the essential content

leaves just this:

<behaviors>

<serviceBehaviors>

<behavior>

<serviceMetadata httpGetEnabled="True" />

<serviceDebug includeExceptionDetailInFaults="False" />

</behavior>

</serviceBehaviors>

</behaviors>

This configures a couple of optional features. The first is related to the metadata ex￾change mentioned earlier—it just ensures that the service description can be fetched

in a certain way. Again, we’ll come back to metadata when we get to the client, so you

can ignore that for now.

The second behavior here—the serviceDebug element—doesn’t have any effect, be￾cause it sets the includeExceptionDetailInFaults property to its default value, False.

Nothing would change if you removed this. The only reason Visual Studio puts this

here at all is to help you out when debugging—sometimes it’s useful to set this to

True temporarily, and putting this entry in the file saves you from having to look up the

name of the setting. Making this True will mean that if your service throws an exception,

the full exception details including stack trace will be sent back to the client in the

response.

Generally speaking, you should never do this, because sending stack traces to your

clients reveals implementation details about your system. If some of your clients are

WCF | 491

evil hackers, this might make it easier for them to break into your system. (Technically,

if your system is completely secure, a stack trace won’t help them, but when did you

last hear about a computer system that was completely secure? It’s safe to presume that

everything has security flaws, so the less help you give hackers the better—this is often

described as reducing the attack surface area of your system.) While you don’t normally

want to send stack traces over the network, doing so can sometimes make it easier to

diagnose problems during development. So you might switch this setting on tempora￾rily to make your life easier. But remember to turn it off before you ship!

That’s everything Visual Studio put into our configuration file. This shows just a tiny

fraction of all the settings we could put in there, but this isn’t a book about WCF, so

that’ll do for now.

After all that, our program still isn’t ready to host the service. As well as putting con￾figuration entries into the application configuration file, our program needs to make

an API call to tell WCF that it wants to host services. (If we were writing a web appli￾cation, we wouldn’t need to do this—having the configuration in the web.config file

would be enough. But for other application types, we need to do this one last step.)

So we need to add a reference to the System.ServiceModel component—that’s

the main .NET Framework class library DLL for WCF—and we also need to add

using System.ServiceModel; and using ChatServerLibrary; directives to the top of the

Program.cs file in our ChatHost project. We can then write our Main method to look like

Example 13-4.

Example 13-4. Hosting a WCF service

static void Main(string[] args)

{

using (ServiceHost host = new ServiceHost(typeof(ChatService)))

{

host.Open();

Console.WriteLine("Service ready");

Console.ReadKey();

}

}

This creates a ServiceHost object that will make the ChatService available. WCF will

load the configuration from our App.config file to work out how to host it. And we need

to make sure our program hangs around—the service will be available only for as long

as the program that hosts it. So we leave the program running until a key is pressed.

If you want to try this out, you’ll need to make sure the host console application is the

program Visual Studio runs by default—right now it won’t be because the ChatServer

Library is still set as the default. You’ll need to right-click on ChatHost in the Solution

Explorer and select Set as Startup Project. Now pressing F5 will run the program, and

a console window will appear showing the message “Service ready” once the

ServiceHost is ready.

492 | Chapter 13: Networking

If you didn’t delete the App.config file in the ChatServerLibrary project

earlier, you’ll now get an error. Even when you set ChatHost as the

startup application, Visual Studio will still attempt to launch the WCF

Service Host for the ChatServerLibrary project. That would be useful in

a solution that has just a WCF client and a service DLL. It’s unhelpful

here because we end up with two programs trying to host the same server

on the same URL—whichever one gets there second will fail.

If you don’t want to delete the App.config in that project, you can disable

the WCF Service Host by opening the ChatServerLibrary project’s Prop￾erties, going to the WCF Options tab, and unchecking the relevant

checkbox.

Now what? We no longer have the WCF Test Client, because Visual Studio thinks

we’re running a normal console application. Since the default wsHttpBinding for our

service endpoint uses HTTP we could try pointing a web browser at it. Remember, the

service is running on the address in the configuration file:

http://localhost:8732/Design_Time_Addresses/ChatServerLibrary/ChatService/

Strictly speaking, the service isn’t really designed to support a web browser. This chap￾ter is all about enabling programs to communicate with one another, not how to build

web user interfaces. However, WCF is rather generous here—it notices when we con￾nect with a web browser, and decides to be helpful. It generates a web page that pa￾tiently explains that the thing we’ve connected to is a service, and shows how to write

code that could talk to the service. And that’s exactly what we’re going to do next.

Writing a WCF Client

We need to create a client program to talk to our service. Again, to keep things simple

we’ll make it a console application. We’ll add this to the same solution, calling the

project ChatClient. (Obviously, you’ll need to stop the ChatHost program first if you’re

trying this out and it’s still running in the debugger.)

When you right-click on a project’s References item in Visual Studio’s Solution Ex￾plorer, you’re offered an Add Service Reference menu item as well as the normal Add

Reference entry. We’re going to use that to connect our client to our server via WCF.

The Add Service Reference dialog offers a Discover button (shown in Figure 13-6) which

attempts to locate services in your current solution. Disappointingly, if we were to click

it with our code as it is now, it would report that it didn’t find any services. That’s

because we wrote all the hosting code by hand for ChatHost—Visual Studio doesn’t

realize that our console application is hosting services. It usually looks only in web

projects—if we’d hosted the service in an ASP.NET web application, it would have

found it. But with the approach we’re taking here, it needs a little help.

WCF | 493

If you left the App.config file in place in the ChatServerLibrary project,

it would find that and would launch the WCF Service Host for you when

you click Discover. But be careful—ChatHost is our real service, and

when we start modifying settings in its App.config (which we’ll do later)

it’s important that the Add Service Reference dialog is talking to the

right service. That’s why we suggested deleting the App.config from the

DLL project earlier—it avoids any possibility of accidentally configuring

your client for the wrong service host.

For Visual Studio to be able to connect to our console-hosted service we need the service

to be up and running before the Add Service Reference dialog is open. The easiest way

to do this is to run the project, without debugging it. Instead of pressing F5, we choose

Debug→Start Without Debugging, or we press Ctrl-F5. This runs the ChatHost program

without debugging, leaving Visual Studio free for other tasks, such as adding a service

reference.

We’ll need the address of the service handy, and since it’s quite long, it’s easiest to open

our host’s App.config and copy the service address to the clipboard. (It’s the

baseAddress attribute in the <host> section.) Then we can go to the ChatClient project

and add a Service Reference. If we paste the address of the service into the Address box

and then click the Go button, after a few seconds we’ll see the Services panel on the

left display a ChatService entry. Expanding this shows an IChatService item repre￾senting the contract, and selecting this shows the one operation available in our con￾tract, PostNote, as Figure 13-6 shows.

While the list of services, contracts, and operations in the Add Service Reference dialog

is useful for verifying that we have the service we wanted, the significance of the infor￾mation here goes a little deeper—it’s part of an important feature of how systems

communicate in WCF. Remember that we defined a contract earlier, to describe the

operations our service provides to its clients. For the client to communicate successfully

with the server, it also needs a copy of that contract. So the best way to think of the

Add Service Reference dialog is that it’s a tool for getting hold of the contract from a

service.

Figure 13-6. Add Service Reference

494 | Chapter 13: Networking

This is the purpose of the metadata exchange entry we saw earlier when we looked at

the configuration Visual Studio generated for our WCF service. Metadata exchange is

just a fancy way of saying that a service provides a way for a client to discover the

contract and related information about the service. The Add Service Reference dialog

uses this information to configure a client application to communicate with the service,

and to provide it with a copy of the contract.

To see the results of this, we’ll finish with this dialog. In the Namespace text box near

the bottom, we’ll type ChatService—Visual Studio will put the contract and any other

types relating to this service into this namespace. When we click OK a Service Refer￾ences item appears in the project in the Solution Explorer, and it will contain an entry

called ChatService. (Now that we’ve done this, we can stop the service host console

window we ran earlier.)

Visual Studio generates some code when adding a service reference. By default, it hides

this, but we can take a look at it. At the top of the Solution Explorer, there’s a toolbar,

and if you hover your mouse pointer over the buttons you’ll find that one has a tool tip

of Show All Files. This button toggles each time you click it. When it’s pressed in, the

ChatService service reference can be expanded, as Figure 13-7 shows.

Figure 13-7. Generated files in a service reference

The most interesting file in here is Reference.cs, inside the Reference.svcmap item. Inside

this file, near the top, there’s a copy of IChatService—the contract we wrote earlier:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",

"4.0.0.0")]

[System.ServiceModel.ServiceContractAttribute(

ConfigurationName="ChatService.IChatService"]

public interface IChatService

{

[System.ServiceModel.OperationContractAttribute(

Action="http://tempuri.org/IChatService/PostNote",

ReplyAction="http://tempuri.org/IChatService/PostNoteResponse")]

void PostNote(string from, string note);

}

WCF | 495

It looks a little more complex than the original, because Visual Studio has annotated it

with various attributes, but it’s simply being explicit about the values that WCF fills in

by default.† Aside from these extra details, you can see that it is essentially a copy of

the original contract.

Sharing contracts

You might wonder why we jumped through all these hoops rather than just copying

IChatService from the service project to the client. In fact, that would have worked,

and we could even have written a separate DLL project to define the contract interface

and shared that DLL across the two projects. As you’ll see shortly, Visual Studio gen￾erated a few other useful things for us as part of this Add Service Reference process,

but as it happens, sharing the contract definition directly is sometimes a perfectly rea￾sonable thing to do—you’re not obliged to use metadata exchange.

Of course, you won’t always own the code at both ends. If you need to connect to a

service on the Internet provided by someone else, metadata exchange becomes more

important—it provides a way to get hold of a contract you didn’t write. And since the

metadata exchange mechanisms are standards-based, this can work even when the

service is not written in .NET.

Metadata exchange is not universally supported. In practice, contract

discovery can happen in all sorts of ways, including (and we’re not

making this up) being faxed a printout showing samples of the messages

the service expects to send and receive.‡ If you’re getting the contract

through that kind of informal channel, you’ll need to write an interface

(by hand) in your client program to represent the service contract.

The process of metadata import also highlights an important point about service evo￾lution. You might modify the ChatService after the ChatClient has added its reference.

If these modifications involve changing the contract, it’s clear that there’s a problem:

the client’s copy of the contract is out of date. You might think that sharing the interface

directly through a common DLL would be a good way to avoid this problem, but it

might only make the problem harder to see: what if you’ve already deployed a version

of the client? If you then modify the contract the modified code might run fine on your

machine, but if you deploy an update to the service with this changed contract any

copies of the old client out there will now be in trouble because they’re still working

with an old copy of the contract. Explicitly going through the metadata exchange

† In fact, it has revealed a small problem: the tempuri.org that appears in the URL indicates something

temporary that we’re supposed to fill in—the ServiceContract attribute on the original service definition has

a Namespace attribute, and we’re supposed to pick a URI that is unique to our service. It’s not mandatory in

this particular scenario because everything works with the default, but a temporary-looking URI doesn’t look

entirely professional.

‡ It could be worse. See http://www.neopoleon.com/home/blogs/neo/archive/2003/09/29/5458

.aspx.

496 | Chapter 13: Networking

doesn’t make this problem any easier to solve, of course, but it makes it less likely for

changes to creep in by accident and go undetected. A complete solution to the problem

of service evolution is beyond the scope of this book, so for now, just be aware that

changing a contract should not be undertaken lightly.

Michele Leroux Bustamante’s Learning WCF (O’Reilly) discusses ver￾sioning of service contracts.

Proxy

Looking further through the Reference.cs file generated by adding the service reference,

the next most interesting feature after the contract is a class called ChatServiceClient.

This implements IChatService, because it acts as a proxy for the service. If we want to

communicate with the service, all we need to do is create an instance of this proxy and

invoke the method representing the operation we’d like to perform. So if we add a

using ChatClient.ChatService; directive to the top of Program.cs in ChatClient, we

can then modify its Main method as shown in Example 13-5.

Example 13-5. Invoking a web service with a WCF proxy

static void Main(string[] args)

{

using (ChatServiceClient chatProxy = new ChatServiceClient())

{

chatProxy.PostNote("Ian", "Hello again, world");

}

}

Notice the using statement—it’s important to ensure that you dispose of WCF proxies

when you have finished using them. When the client calls this method on the proxy,

WCF builds a message containing the inputs, and it sends that to the service. Over in

the service (which is running in a separate process, perhaps on a different machine)

WCF will receive that message, unpack the inputs, and pass them to the PostNote

method in the ChatService class.

To try this out, we’re going to need to run both the client and the server simultaneously.

This means configuring the solution in Visual Studio a little differently. If you right￾click on the WcfChat solution in the Solution Explorer and select Set Startup Projects,

the dialog that opens offers three radio buttons. If you select the Multiple Startup

Projects radio button, you can choose which of your projects you’d like to run when

debugging. In this case, we want to change the Action for both the ChatClient and

ChatHost projects from None to Start. (We leave the ChatServerLibrary Action as

None—we don’t need to run that project, because our ChatHost project hosts the server

library.) Also, we want to give the service a head start so that it’s running before the

WCF | 497

client tries to use it, so select ChatHost and click the up arrow next to the list, to tell

Visual Studio to run it first. (In theory, this is not a reliable technique, because there’s

no guarantee that the server will get enough of a head start. In practice, it appears to

work well enough for this sort of debugging exercise.) Figure 13-8 shows how these

settings should look.

Figure 13-8. Starting multiple projects simultaneously

If we run the program by pressing F5, two console windows will open, one for the client

and one for the service.

If you’re following along, it’s possible that you’ll see an AddressAlrea

dyInUseException with an error message complaining that “Another ap￾plication has already registered this URL with HTTP.SYS.” This usually

means you have a copy of ChatHost still running—somewhere on your

desktop you’ll find a console window running the service host. Or pos￾sibly, the WCF Service Host is still running. This error occurs when you

launch a second copy of the service because it tries to listen on the same

address as the first, and only one program can receive requests on a

particular URL at any one time.

Visual Studio displays the message in its Output window because of the call to

Debug.WriteLine in PostNote, just like it did when using the WCF Test Client earlier,

verifying that the proxy was able to invoke an operation on the service. (You might

498 | Chapter 13: Networking

Tải ngay đi em, còn do dự, trời tối mất!