Wednesday, 27 July 2011

CRUD with LINQ

Database: Northwind
Visual Studio 2010

Setup:
1. Create a new Web or WinForm Application
2. Create a new Data Connection in Server Explorer to connect to Northwind database
3. Add LINQ to SQL Classes to your project















4. Drag all the Northwind tables to the designer surface left panel, drag stored procedures CustOrderHist, CustOrdersDetails, CustOrdersOrders to the designer surface right panel. Actually I did not use all of those in the application.

5. Create a new stored procedure in the Northwind, name it as UpdateCustomer. Afterwards, I'll show you how to use LINQ to call this SP to update database and get the returned value. The SP is as following:

ALTER PROCEDURE [dbo].[UpdateCustomer]   
      @CustomerID nchar(5),
      @CompanyName nvarchar(40)
AS
BEGIN
 SET NOCOUNT ON;

 update dbo.Customers set CompanyName = @CompanyName where CustomerID = @CustomerID
 return 1 -- for demo purpose
END
The SP updates the CompanyName of the Customers table based on the CustomerID, it takes  two parameters: CustomerID and CompanyName. And always return value 1 for demo purpose here.

6. Refresh the database in Server Explorer and drag UpdateCustomer SP to the right panel of the Northwind.dbml designer surface.

7. Build the application, make sure it succeeded. The application looks like the following:


8. LINQ has generated the code to model the database, and we can start to enjoy the intellisense and strongly typed database object. The following is the code snippet:

Read Products with category name is "Beverages"
NorthwindDataContext db = new NorthwindDataContext();
var products = from p in db.Products where    
               p.Category.CategoryName=="Beverages"
               select p;

foreach (Product p in products)
{
   lblResult.Text = lblResult.Text + p.ProductName + " | ";
}



Update Products table
NorthwindDataContext db = new NorthwindDataContext();
Product product = db.Products.Single(p => p.ProductName == "Chai");

product.UnitPrice = 99;
product.UnitsInStock = 5;

db.SubmitChanges();

Insert Records
NorthwindDataContext db = new NorthwindDataContext();
Category cat = new Category();
cat.CategoryName = "TOY";

Product p1 = new Product();
p1.ProductName = "toy 1";
Product p2 = new Product();
p2.ProductName = "toy 2";

cat.Products.Add(p1);
cat.Products.Add(p2);

db.Categories.InsertOnSubmit(cat);
db.SubmitChanges();

Delete
NorthwindDataContext db = new NorthwindDataContext();
var toyProd = from p in db.Products
              where p.ProductName.Contains("toy")
              select p;
db.Products.DeleteAllOnSubmit(toyProd);
db.SubmitChanges();

Run SP, return a collection
NorthwindDataContext db = new NorthwindDataContext();
var orderDetail = db.CustOrdersDetail(10248);

foreach (CustOrdersDetailResult re in orderDetail)
{
    Console.WriteLine(re.ProductName);
}
Note: When you use the object of CustOrdersDetailResult, you can see the intellisense and can access every Field the Stored Procedure CustOrdersDetailResult returns, that is ProductName, UnitPrice, Quantity, Discount and ExtendedPrice in this case. This SP is to return the order details based on the OrderID, which is passed as a parameter.


Run SP, Update table and return a value
NorthwindDataContext db = new NorthwindDataContext();
var result = db.UpdateCustomer("AAAAA", "BMW");
Console.WriteLine(result.ToString());

Note: UpdateCustomer is the new SP created at step 5. It sets the CompanyName to "BMW" for the CustomerID "AAAAA", Passing the parameter is like to call a regular method, and we do not need SubmitChanges here.

In the SP, @CustomerID declared as nchar(5), @CompanyName declared as nvarchar(40), but in the intellisense, I see the signature changed to db.UpdateCustomer(string customerID, string companyName), need to watch not to exceed the width.


Server side Pagination
NorthwindDataContext db = new NorthwindDataContext();
var products = (from p in db.Products
                where p.Category.CategoryName.StartsWith("C")
                select p).Skip(200).Take(10);
Pagination made easy, only returns the collection we are looking for.

9. Save, compile and run the application. The above code has been tested.

Wednesday, 20 July 2011

Tips of using Trim

Trim() can remove line breaks and space in the same time. For example:


string s = "hello world.\r\n\r\n";
string ret = s.Trim();
Console.WriteLine(ret);

result: "hello world.”  //line breaks removed

string s = "hello world.  \r\n\r\n";
string ret = s.Trim();
Console.WriteLine(ret);

result: "hello world.” //line breaks and trailing space being removed by one Trim()

Sometimes I have my message concatenated in a string:

msg = msg + "; custom message 1";
...
msg = msg + "; custom message 2";
...
msg = msg + "; custom message 3";

In the end, I want to remove the '; ' from the beginning of msg to look better, I can do the following:

string ret = msg.Trim(new char[]{';',' '});

The beauty is: even both custom message 1 and custom message 2 are empty, the msg will look like:

"; ; custom message 3"

After single trimming, you can still get "custom message 3", and you don't have to check if the msg is empty every time.


Sunday, 10 July 2011

Host a WCF Service in IIS

When a WCF service runs in IIS, it can take full advantage of IIS features such as process recycling, idle shutdown, process health monitoring, and message-based activation, it will automatically launch the host process when it gets the first client request. This hosting option requires that IIS be properly configured, but you do not need to write hosting code in your application. The disadvantage is that it only supports HTTP.

Development Environment: IIS 6.0, VS2010, C#

1. Create a new web site, select "WCF Service" as Template


Choose location as HTTP, http://localhost/MyCalculatorService and click OK Button

2. Open Service.cs, double click Service to select, and then use "Refactor | Rename" to rename it to MyCalculatorService, rename IService to IMyCalculatorService in the same way. Rename file service.svc to MyCalculatorService.svc, Service.cs to MyCalculatorService.cs, IService.cs to IMyCalculatorService.cs


3. Open IMyCalculatorService.cs, add

public interface IMyCalculatorService
{
    [OperationContract]
    string GetData(int value);

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);

    [OperationContract]
    double Add(double n1, double n2);

    [OperationContract]
    double Subtract(double n1, double n2);

    [OperationContract]
    double Multiply(double n1, double n2);

    [OperationContract]
    double Divide(double n1, double n2);
}

4. Open MyCalculatorService.cs, add

    public double Add(double n1, double n2)
    {
        return n1 + n2;
    }

    public double Subtract(double n1, double n2)
    {
        return n1 - n2;
    }

    public double Multiply(double n1, double n2)
    {
        return n1 * n2;
    }

    public double Divide(double n1, double n2)
    {
        return n1 / n2;
    }

5. Open MyCalculatorService.svc, make sure the name matches the changed name:

<%@ ServiceHost Language="C#" Debug="true" Service="MyCalculatorService" CodeBehind="~/App_Code/MyCalculatorService.cs" %>

6. Open Web.config, modify

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="false" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="ServiceBehavior" name="MyCalculatorService">
        <endpoint address="http://localhost/MyCalculatorService/MyCalculatorService.svc" binding="wsHttpBinding" contract="IMyCalculatorService">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <!-- 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>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
 
</configuration>

I take out <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
from the web.config, because it causes an error as following:

When 'system.serviceModel/serviceHostingEnvironment/multipleSiteBindingsEnabled' is set to true in configuration, the endpoints are required to specify a relative address. If you are specifying a relative listen URI on the endpoint, then the address can be absolute. To fix this problem, specify a relative uri for endpoint 'http://localhost/MyCalculatorService/MyCalculatorService.svc'.

7. Test in IE
Copy and paste 'http://localhost/MyCalculatorService/MyCalculatorService.svc' to IE, you should see something like the following, which means the service is ready to be consumed.


You can also test it using WcfTestClient tool


8. Create a Console application ServiceClient in the same solution or separate solution. Add Reference System.ServiceModel and System.Runtime.Serialization

9. Generate proxy files.
Open Visual Studio command prompt and nagivate to the ServiceClient folder, the same folder as Program.cs

10. Run
svcutil http://localhost/MyCalculatorService/MyCalculatorService.svc

Two files will be generated:
MyCalculatorService.cs and output.config

11. Rename output.config to app.config and include it in the application, also include MyCalculatorService.cs into the application. In the Main method of Program.cs, add

MyCalculatorServiceClient client = new MyCalculatorServiceClient();
Console.WriteLine("calling the service...");
Console.WriteLine("112.78+45.20=" + client.Add(112.78, 45.20).ToString());
Console.Read();

12. Ctrl+F5 to run the Console application, you should see:

















13. Yeahhh!!! you have done it, take a break.

A Self-Hosting WCF Service Host With Client

The following WCF service is hosted in a .NET console application
Client which consumes the service is a simple WinForm application

A service can also be hosted in a managed windows service, IIS, or Windows Process Activation Service (WAS).

The following code is written in Visual Studio 2010, C#.

Create WCF Host

1. Create a Console Application, name it as SelfHost
2. Add Reference System.ServiceModel
3. The entire Program.cs is as following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace SelfHost
{
    [ServiceContract]
    public interface IHelloWorldService
    {
        [OperationContract]
        string SayHello(string name);
    }

    public class HelloWorldService : IHelloWorldService
    {
        public string SayHello(string name)
        {
            return string.Format("Hello, {0}", name);
        }
    }
   
    class Program
    {
        static void Main(string[] args)
        {
            Uri baseAddressHello = new Uri("http://localhost:8080/hello");
           
            // create serviceHost

            ServiceHost hostHello = new ServiceHost(typeof(HelloWorldService), baseAddressHello);

            // Enable metadata publishing
            ServiceMetadataBehavior smbHello = new ServiceMetadataBehavior();
            smbHello.HttpGetEnabled = true;
            smbHello.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            hostHello.Description.Behaviors.Add(smbHello);

            hostHello.Open();
            Console.WriteLine("the hello service is ready at {0}", baseAddressHello);
                      
            Console.WriteLine("press <enter> to stop the service.");
            Console.ReadLine();

            hostHello.Close();
     
        }
    }
}

It uses default endpoints, and no configuration file is required for this service. If no endpoints are configured, the runtime creates one endpoint for each base address for each service contract implemented by the service

To test your WCF service using WCF Test Client

1. Build the project, make sure it is successful
2. Run the service (Ctrl+F5), you should see the following Screenshot

2. To open WCF Test Client, open a Visual Studio 2010 command prompt and execute WcfTestClient.exe.
3. Select Add Service... from the File menu
4. Type http://localhost:8080/hello into the address box and click OK.
5. Double-click SayHello under the My Service Projects node. Type your name into the Value column in the Request list, and click Invoke. A reply message should appear in the Response list if everything is alright

Create Client Application

1. Add new winform application to the solution, name it as WcfClient
2. Add Reference System.ServiceModel
3. Drag drop a button and double click it to create a click event handler
4. Run the service: Set the Console as startup project and run it by Ctrl+F5
5. Open Visual Studio 2010 command prompt and navigate to WcfClient folder
6. Use the command-line tool ServiceModel Metadata Utility Tool (Svcutil.exe) with the appropriate switches to create the client code

svcutil.exe /language:cs /out:Proxy.cs /config:app.config http://localhost:8080/hello

7. Include the generated file into your winform application: Proxy.cs and app.config
8. Invoke the service

Using System.ServiceModel;
.....

private void button1_Click(object sender, EventArgs e)
{
   HelloWorldServiceClient client = new HelloWorldServiceClient();
   MessageBox.Show(client.SayHello("michael"));
}


9. Click the button, you should the Message Box with the "hello, michael". Done


Reference: MSDN