I have a token issuer WCF service which is using Microsoft.IdentityModel (WIF 3.5) that I need to upgrade to System.IdentityModel (.NET 4.5). The problem is that I can't change the original name of the service , Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract, to it's newer counterpart, System.ServiceModel.Security.WSTrustServiceContract. For some reason it's not recognized by IntelliSense:
The blue squiggly line error is:
The 'name' attribute is invalid - The value 'System.ServiceModel.Security.WSTrustServiceContract' is invalid according to its datatype 'serviceNameType'
I do have assembly references to System.ServiceModel and System.IdentityModel in <assemblies> node.
Even when I ignore the IntelliSense error and run the service and access it using browser I'm getting this metadata error:
Metadata publishing for this service is currently disabled.
Metadata publishing is enabled so I think it's because of the name problem of the service.
Also I'm getting this error from the VS.NET WCF test client:
Error: Cannot obtain Metadata from http://localhost:49178/Services/Issuer.svc
If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address. For help enabling metadata publishing, please refer to the MSDN documentation at http://go.microsoft.com/fwlink/?LinkId=65455.
WS-Metadata Exchange Error
URI: http://localhost:49178/Services/Issuer.svc
Metadata contains a reference that cannot be resolved: 'http://localhost:49178/Services/Issuer.svc'.
There was no endpoint listening at http://localhost:49178/Services/Issuer.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
The remote server returned an error: (404) Not Found.
HTTP GET Error
URI: http://localhost:49178/Services/Issuer.svc
The HTML document does not contain Web service discovery information.
I think the "Metadata contains a reference that cannot be resolved" line also refers to the service name resolve error.
Any ideas on what to do here? I'd appreciate any help..
Issuer.svc:
<%# ServiceHost Language="C#" Debug="true" Factory="Identity.Services.Wcf.Core.CustomSecurityTokenServiceContractFactory" Service="CustomSecurityTokenServiceConfiguration" %>
Factory:
public class CustomSecurityTokenServiceContractFactory : WSTrustServiceHostFactory
..
Service:
public class CustomSecurityTokenServiceConfiguration : SecurityTokenServiceConfiguration
..
Sometimes the best way to solve this kind of problems is to create a new WCF project from scratch, configure again your endpoints etc.. and copying over your existing services from your old project, this is especially true when moving from an older version of WCF.
Here is a checklist that I follow every time I have problems with WCF services:
The Server
Make sure your service contracts are defined using interfaces with the appropriate attributes, for example:
IMyService.cs
[ServiceContract]
public interface IMyService
{
[OperationContract]
int ThisAnOperation(int a, int b);
}
Check that you have implemented your contracts using the right interface:
MyService.cs
public class MyService: IMyService
{
public int ThisAnOperation(int a, int b)
{
return a * b;
}
}
You need to have a service host to access your service, they are the files with the extension .svc:
Create a file myService.svc.
Add the following line of code, referencing the class implementing your service:
<%# ServiceHost Language="C#" Debug="true" Service="YourNamespace.MyService" CodeBehind="MyService.cs" %>
Finally, you need to set up a binding which will define which transports and protocols are available to access your server, start with a simple basic HTTP binding to check that your service is working as expected, then change it to something more production ready that includes authentication and/or encryption and compression as needed.
To setup basic HTTP binding:
Remove the block <system.serviceModel>...</system.serviceModel> from your file web.config if it's already there.
Build your solution, it should compile successfully, otherwise fix any error and try again.
Right-click your web.config file and then click on "Edit WCF Configuration", then click on "Create a New Service" and in Service type, browse and choose the DLL file generated when you compiled your service (should be in the bin folder) and select the service class you would like to publish:
Specify the contract for the service (should be automatically filled up).
In the next page select the transport protocol for your service, in this case, "HTTP", then select "Basic Web Services interoperability".
In the next page you can specify the address for the endpoint, for testing purposes, you can leave this field empty (make sure you also remove "HTTP" from the text field).
Click next, close the configuration window and save.
Now you should be able to run the service and browse to MyService.svc to access your service.
Activate metadata publishing so your service can be found, to do this, add the following behavior to your web.config:
<system.serviceModel>
<services>
<service name="WcfService1.MyService">
<endpoint binding="basicHttpBinding"
bindingConfiguration="" contract="WcfService1.IMyService"
BehaviorConfiguration="MyServiceBehaviors" />
</service>
</services>
</system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehaviors" >
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Now you should be able to run your project and get a metadata description page of your service within the browser, this info can be used by clients to find the service and generate a proxy of the service:
The Client
Delete any existing service references from your project.
Right click on your project name then in "Add Service Reference", input your service address and click on "Go", if everything went all right you should see your service in the Service Window:
Try to generate the proxy by finishing the wizard, rebuild your project and try it. If you still have the same problem, delete the generated reference and repeat points 1 and 2 and then:
Click on "Advanced" and uncheck "Reuse types in referenced assemblies":
Then finish the wizard and compile.
Hopefully, everything should work now!!!
I may have a similar setup as yours. In my case, I have both the STS and a service that is called by whoever wants a token. This is what you have, right?
In the Web.config for the actual STS I have:
<bindings>
<ws2007HttpBinding>
<binding name="ws2007HttpBindingConfiguration">
<security mode="TransportWithMessageCredential">
<message establishSecurityContext="false" clientCredentialType="Certificate"/>
</security>
</binding>
</ws2007HttpBinding>
</bindings>
<services>
<service name="System.ServiceModel.Security.WSTrustServiceContract" behaviorConfiguration="STSBehavior">
<endpoint address="IWSTrust13" binding="ws2007HttpBinding" bindingConfiguration="ws2007HttpBindingConfiguration" contract="System.ServiceModel.Security.IWSTrust13SyncContract" name="STSWCF"/>
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
</service>
</services>
And in the Web.config for the service I have:
<protocolMapping>
<!-- We want to use ws2007FederationHttpBinding over HTTPS -->
<add scheme="https" binding="ws2007FederationHttpBinding" bindingConfiguration="ws2007FederationHttpBindingConfiguration"/>
</protocolMapping>
<bindings>
<ws2007FederationHttpBinding>
<binding name="ws2007FederationHttpBindingConfiguration">
<!-- We expect a bearer token sent through an HTTPS channel -->
<security mode="TransportWithMessageCredential">
<message establishSecurityContext="false">
<issuerMetadata address="https://localhost/Identity.STS.WCF/Service.svc/mex"/>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
<services>
<service name="Identity.Auth.WCF.Service" behaviorConfiguration="STSBehavior">
<endpoint address="https://localhost/Identity.Auth.WCF/Service.svc" binding="ws2007FederationHttpBinding" bindingConfiguration="ws2007FederationHttpBindingConfiguration" contract="Identity.Auth.WCF.IService" name="Identity.Auth.WCF"/>
</service>
</services>
Also, it does work for me here, even though I do get the same IntelliSense error as you, and in the very same spot.
Related
I'm just getting familiar with WCF and I have to add additional functionality to a working web service at work. As I feel the need to be able to test the functionality before deploying it, I decided to build a test client. Here comes the problem.
I created a Console Application just for the purpose of a test client and tried to add a Service Reference through the provided WSDL but it didn't work. There was no config file created.. I tried first the "Add Service Reference" option in VS and when it didn't work, I tried creating the Proxy and Config files with svcutil.exe...
Just the proxy class gets created... When I try to instantiate a "client object" from that class, the following Exception is thrown: "Could not find default endpoint element that references contract 'eOrderingService.IeOrderingService' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element."
As this is a working service (a Czech company is using it), apparently there must be a way to create a web.config or app.config even manually but I have no idea where to start.. As I said I'm just getting familiar with WCF so I started looking online but most of the problems connected somehow to my issue were in different parts of the already created Config files.. I managed to bypass that exception adding the following to app.config:
<system.serviceModel>
<services>
<service name="eOrderingService" >
<endpoint
address="http://localhost:61472/eOrderingService.svc"
binding="webHttpBinding"
contract="eOrderingService.IeOrderingService" >
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint
address="http://localhost:61472/eOrderingService.svc"
binding="webHttpBinding"
bindingConfiguration=""
contract="eOrderingService.IeOrderingService"
behaviorConfiguration="web"
name="DeliveryNote" >
</endpoint>
</client>
The service has a lot of methods and the one I need to test is called 'DeliveryNote'.
So lets say the service is at this address:
http://localhost:61472/eOrderingService.svc
The POST method I need to call is:
http://localhost:61472/eOrderingService.svc/DeliveryNote
And respectively the GET method is:
http://localhost:61472/eOrderingService.svc/DeliveryNote?DocumentFilter={DOCUMENTFILTER}&CustomerID={CUSTOMERID}&FromDate={FROMDATE}
The links are working but I cannot figure out how to call them from the client.
When I tested calling the POST method I received another exception:
The remote server returned an unexpected response: (400) Bad Request.
That shouldn't be true because the request I'm sending is already tested and is a valid request in XML format.
So I tried to test with a different GET method that works and receives just two DateTime parameters and not a Xml. If I try the following link:
http://localhost:61472/eOrderingService.svc/PriceChanges?startDate=2018-08-29&endDate=2018-08-30
the result is OK..
But if I call the automatically generated method "PriceChanges" the result is NULL.
I just don't get what I do wrong. It seems like a connection to the service is established but the methods are not called/build correctly. Probably because I cannot comprehend how to build the <system.serviceModel> in app.config.
I definitely should read more about the web services but I don't know where to start.
I have been assigned a old project that currently uses a WCF service.
The point is to update (more like starting fresh) the project to use ASP.NET MVC5 and Web API. The problem is that a 3rd party uses the WCF service, and they are probably not willing to make changes to their end of the communication.
The main function of the project is two basic, one is receive data, save it and just show the last status of several subjects and history graphs, and the other is to send data (turn on/off subjects).
My idea was to maintain the WCF Service (receive/send/save data) as is, just add it to new solution which consists of MVC and Web API (read data). They need (I think) to be in the same project, because the final objective is to implement SignalR on the WCF Service, if possible.
The main problem, is that the MVC and WebAPI are going to stay behind Authentication, but the WCF won't. At the moment when I try to test the WCF on the same project, it fails because it asks for a "Log in."
Error: Cannot obtain Metadata from
http://localhost:50598/ServiceTest.svc If this is a Windows (R)
Communication Foundation service to which you have access, please
check that you have enabled metadata publishing at the specified
address. For help enabling metadata publishing, please refer to the
MSDN documentation at
http://go.microsoft.com/fwlink/?LinkId=65455.WS-Metadata Exchange
Error URI: http://localhost:50598/ServiceTest.svc Metadata contains a
reference that cannot be resolved:
'http://localhost:50598/ServiceTest.svc'. The content type text/html;
charset=utf-8 of the response message does not match the content type
of the binding (application/soap+xml; charset=utf-8). If using a
custom encoder, be sure that the IsContentTypeSupported method is
implemented properly. The first 1024 bytes of the response were: '?
Log in.
Usernamehttp://localhost:50598/ServiceTest.svc The HTML document does not
contain Web service discovery information.
I tried everything that I could find on the web. But couldn't find anything that would work.
My last consisted on fiddling with the web.config:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="UnsecuredBinding">
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="Management.ServiceAspNetAjaxBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="Management.ServiceTest" behaviorConfiguration="serviceBehavior">
<endpoint address="" behaviorConfiguration="Management.ServiceAspNetAjaxBehavior" binding="webHttpBinding" contract="ServiceLibrary.IService" bindingConfiguration="UnsecuredBinding"/>
</service>
</services>
</system.serviceModel>
I also added routes.IgnoreRoute to RouteConfig.cs
routes.IgnoreRoute("{resource}.svc/{*pathInfo}");
routes.IgnoreRoute("{resource}.svc");
and tried to add this to Global.asax
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpApplication context = (HttpApplication)sender;
if (context.Request.Url.ToString().Contains(".svc"))
{
context.Response.SuppressFormsAuthenticationRedirect = true;
}
}
My questions:
If I migrate the WCF methods to WebAPI, will the client need to do any modifications on their end?
If yes, how can I integrate WCF on my MVC/WebAPI project, without being affected by Log In barrier.
In answer to question 1, yes your clients would have to make modifications. WCF is SOAP/XML where as WebApi is REST/JSON(usually) for starters.
As for 2, if the WCF service is working fine, just leave it as it is. You don't need to include it in the project directly, simply use the "Add Service Reference" wizard in Visual Studio to make a client for interacting with it.
As a side note, the error you where experiancing was probably due to using the inbuilt IIS express instance with Visual Studio, which doesn't support running anonymous and authenticated applications at the same time, so your WCF service ended up with authentication enabled.
IIS 7.0 on Windows 2008
WCF Web Service, .NET 4 from VS 2010
Web service is installed via publishing and I have full admin rights on the server. There are several complicated methods, but there is a simple one that returns the build version. If we can get this one working, I can fix them all - here is my interface:
namespace MyNameSpace
{
[ServiceContract]
public interface WebInterface
{
[OperationContract]
[WebGet]
string GetVersion();
Attempt to connect via HTTP:// and everything works fine!
Attempt to conenct via HTTPS:// I get a 404 file not found.
I can reach the generic "You have created a web service..." page, including full web service path and the C# generic sample code when browsing to the exact same URL's both on HTTP and HTTPS.
In C#, I have read that the certificate can cause trouble, and I have already implemented the delegate overload to approve our server certificate.
I suspect missing one or more entries in the Web.config file, but I don't have a clue where to start. I have tried Google searching and Stack Overflow searching, but I haven't found the correct combination of search terms to help with this particular issue.
Web Config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="HttpGetMetadata">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="LinkService" behaviorConfiguration="HttpGetMetadata">
<endpoint address="" contract="WebInterface" binding="basicHttpBinding" />
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Help Please.
You're using the defaults for basicHttpBinding, and the default security mode for that binding is None. You need to define the binding and set the security mode to Transport in your config. Add a Bindings section to your ServiceModel section, like this:
<serviceModel>
<Bindings>
<basicHttpBinding name="secureBinding">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</basicHttpBinding>
</Bindings>
</serviceModel>
Then you need to assign this binding to your endpoint via the bindingConfiguration attribute, like this:
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="secureBinding"
contract="WebInterface" />
You'll probably want to enable httpsGetEnabled as well:
<serviceMetadata httpGetEnabled="true"
httpsGetEnabled="true" />
See BasicBinding with Transport Security (which is what the sample code is based on).
You can also google with terms like "BasicHttpBinding WCF SSL" and stuff like that - lots of examples and information on the web, it's just a matter of using the right words :)
Also, I'm not 100% confident that the transportClientCredential setting is correct for your scenario (it might need to be Certificate), but I've done very little with SSL for WCF.
There may be other issues as well (like how IIS is set up on your machine), but the above is what's needed for the config.
I have a WCF service that is hosted inside a Windows Service. The Windows Service has a OnStart method like this:
protected override void OnStart(string[] args)
{
serviceHost = new ServiceHost(typeof (RouterService));
serviceHost.Open();
}
It's quite minimal now, since I'm trying to find the problem. Since I have not put any error handling here, any exceptions at this point should have stopped the service from starting up properly. It would start, and then automatically stop again immediately.
My config file for the WCF service looks like this:
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<system.serviceModel>
<services>
<service name="WcfService.RouterService"
behaviorConfiguration="serviceBehavior" >
<endpoint address="RouterService" contract="WcfService.IRouterService" binding="wsHttpBinding" bindingConfiguration="NoSecurity" />
<endpoint address="mex" contract="IMetadataExchange" binding="mexHttpBinding" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="NoSecurity">
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior" >
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
In addition, I have added the NETWORK SERVICE user (who the service is running as to be able to listen to Http at the specified port:
netsh http add urlacl url=http://+:8000/ user="NETWORK SERVICE"
Now, in the client, I choose Add Service Reference. I can find the server, and VS also manage to create the configuration and make the proxy files. Then inside the client, I make an instance of the proxy, and open it. Everything still fine. But upon calling the first method on proxy, I get the following exception:
The HTTP service located at http://localhost:8000/RouterService is too busy.
I know that my client is the only thing that is trying to connect to this service, so why do I get this error message then? And how can I fix this? It does not time out when it tries to call the method, it throws the exception almost immediately.
EDIT
Ok, now I found out that my TestServer (not the Windows Client one, but a console application) also gives the same error. Earlier today it didn't, and neither the WCF service or TestServer are changed.
Then I tried changing the port to 8080 instead of 8000, and it worked. Then I tried the same for the Windows Service, and that also worked (after running the netsh http command on the new port)
So something has happened to http on port 8000 for some reason. I have tried restarting the system also of course.
This is puzzling me, so if anyone has any idea what is going on here I would appreciate it.
Ok, after a long time of trying to figure out what happened here, I found the solution.
After I created the windows service, and tried to start it I couldn't because I had to register the NETWORK SERVICE user to listen on http post 8000 first. I therefore ran this command:
netsh http add urlacl url=http://+:8000/RouterService user="NETWORK SERVICE"
It gave a successful result, but then I tried starting the service, and got the same error message. Then I ran the same command without RouterService specified:
netsh http add urlacl url=http://+:8000/ user="NETWORK SERVICE"
It gave a successful result, and now my windows service started up without any problems, but I got the problems mentioned in the question above.
It seems that the first call to netsh causes all of these problems. I tried removing it again with this command:
netsh http delete urlacl url=http://+:8000/RouterService
And then everything worked perfectly.
A bitch to debug this since the exception had nothing to do with the actual problem, so I hope this question can save someone else a couple of hours :)
Following answer of #oyvind-knobloch-brathen consider to use well known SID strings instead of "user" command line key, this will give you cross culture portability (I experienced problems on French-localized Win7 machine). For more information see SID strings. For your case this will be:
netsh http add urlacl url=http://+:8000/ sddl="O:NS"
a link to SDDL on MSDN.
I've been looking for this answer, and all I found was this link, but when I attempted to follow the tutorial I failed hard. What I need is to connect my Silverlight application to a database, just to show informations from a specific table. As I don't want to use the same ORM for my page and my silverlight app, I created a new WCF webservice project, and created my LINQ to SQL classes inside of it.
I tested my WCF service and it works fine, but somehow my Silverlight App doesnt reach it. I've changed the web.config file, and now it looks as follows.
My web.config
<?xml version="1.0"?> <configuration>
<connectionStrings>
<add name="bd_webportosConnectionString" connectionString="Data Source=BARNEY\DEV;Initial Catalog=bd_webportos;User ID=sa;Password=Stigeo_1_adm_1"
providerName="System.Data.SqlClient" /> </connectionStrings> <system.web>
<compilation debug="true" targetFramework="4.0" /> </system.web> <system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBindingConfig">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:7298/DataToSilverlight.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="DataRetrieverReference.IService1" name="BasicHttpBinding_IService1" />
</client>
<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>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer>
<modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration>
I don't know how to solve this problem. And although I got stuck, I tried to keep going forward, but then I got stuck again in the next step, that was to add the service reference to my silverlight app. As I try to do what it says, the following message is shown:
There was an error downloading
metadata from the address. Please
verify that you have entered a valid
address.
I tested the service through WCF Test Client, and it worked, but my silverlight app doesn't reach it. I get the following exception:
An error occurred while trying to make
a request to URI
'http://localhost:7298/DataToSilverlight.svc'.
This could be due to attempting to
access a service in a cross-domain way
without a proper cross-domain policy
in place, or a policy that is
unsuitable for SOAP services. You may
need to contact the owner of the
service to publish a cross-domain
policy file and to ensure it allows
SOAP-related HTTP headers to be sent.
This error may also be caused by using
internal types in the web service
proxy without using the
InternalsVisibleToAttribute attribute.
Please see the inner exception for
more details.
Can you guys help me solving this big problem, or even showing another way to achieve what I want?
I also recently discovered that my crossdomain.xml doesn't get loaded ... but I don't know what that means.
Silverlight runs in very sandboxed environment. So when ever we want make a call to the WCF service from silverlight app, then we have to deploy simple policy.xml and crossdomain.xml in root director and application director of IIS. if you running an silverlight app from VS studio webserver and WCF is hosted in VS internal web server, then you will not experience such problem.
According to MSDN the <services> tag should be inside your <system.serviceModel> tag. Just copy the whole block in there.
Edit: About the connect to database part.
Silverlight can not magically "query" the database through the WCF service unless you create a WCF Data Service/OData. To get data from the database to your Silverlight client in a straight forward way you need to create methods in the WCF service that queries the database according to the in-parameters of the WCF method, packs it up i a suitable data structure (List<Customer> in the example in you link) and returns the result to the Silverlight client.
The error you get "There was an error downloading metadata from the address. Please verify that you have entered a valid address." is not an error relevant to the database, it is an error telling that your WCF service can not be found. That would have happened regardless if your WCF service used a database or not.
It looks like there might be further configuration errors in the WCF service, that might explain why the reference to the service can not be added. How does your dialog corresponding to "Figure 3-13. Adding a reference to the Web Service" look like? You can edit your question and insert a screen dump.
You can try this way :
http://www.dotnetspider.com/tutorials/Silverlight-Tutorial-315.aspx
Simple and easy.