Nine MVP's Blog

24/09/2013

Entity Framework: Partial Update Object

Introduce

เป็นปากเสียงกันได้เลยสำหรับคนใช้ Entity Framework (EF) และ NHibernate (NH) ถึงเรื่องความสามารถต่างๆ
อาจจะเพราะ EF เกิดมาช้าเลยพากันขัดตาไปเสียหมด อาจจะเพราะว่า NH ได้ถูกนำโค้ดจาก Hibernate ที่เป็น JAVA มาเขียนเป็น C# ใหม่ด้วยกระมัง
ดังนั้นจึงมีของให้ใช้เยอะและตรงใจผู้พัฒนาค่อนข้างมาก

มาถึงเหตุให้ต้องเขียนตอนนี้ขึ้นมาเพราะมีคำถามว่า EF มี Merge() method สำหรับการ Update เฉพาะ property ที่เรากำหนดค่าได้หรือไม่

ยกตัวอย่างเช่นว่า มี 


public class User
{
    public int Id { get; set; }     

public string UserName { get; set; }

    public string Password { get; set; }

    public DateTime LastLogin { get; set; }

}

แต่ต้องการแค่อยากจะอัพเดทแค่ Password กับ LastLogin property เท่านั้นแบบนี้ โดยที่ไม่กำหนด property อื่นๆ
และเมื่อส่งไปอัพเดทใน database ต้องไม่ทำให้ property อื่นๆที่ไม่ได้กำหนดค่ากลายเป็น Null แบบตัวอย่างข้างล่างนี้


//pass user object and specify update properties
userDto.Password = “P@ssw0rd”;
userDto.LastLogin = DateTime.Now;db.PartialUpdate(userDto);

db.SaveChanges();

 

คำตอบคือ ไม่มีหรอกเสียใจด้วยครับ

แต่……ช้าก่อน  เราทำได้ครับ EF เก่งพอที่จะให้เราโชว์ฝีเท้าใส่โค้ดเข้าเพิ่มเติมเข้าไปเองได้

..

SOLUTION 1 Passing Property Name

วิธีแรกก็คือ ก็ส่งชื่อ Property ที่ต้องการจะ update เข้าไปเองก็สิ้นเรื่อง แต่ก็ไม่ถึงกับต้องมานั่งพิมพ์ค่า property name ใส่ string variable เข้าไปกันเองหรอกนะ มีวิธีที่ดีกว่านั้น


เริ่มต้นด้วยการติดตั้ง
Entity Framework ลงในโปรเจคก่อน
จากนั้นสร้าง
User class ขึ้นตาม introduce ด้านบน
ต่อมากก็สร้าง UserContext class สำหรับใช้งานตาม code ด้านล่างนี้


public partial class UserContext : DbContext
{
    public UserContext() : base(“name=UserDB”) { }    

public DbSet<User> Users { get; set; }

}

เสร็จแล้วให้เพิ่ม class file อีกอัน สำหรับสร้าง Partial Class เพื่อเพิ่ม method สำหรับทำ PartialUpdate ตาม codeด้านล่าง ซึ่งจะเห็นได้ว่ามีแต่ Method Name และ Signature ตัว code จะเพิ่มให้ใน block หลังๆครับ


public partial class UserContext
{     

public void PartialUpdate<T>(T entity, params Expression<Func<T, object>>[] propsToUpdate) where T : class {}    

private IEnumerable<string> ConfigPartialUpdate<T>(T entity, out DbEntityEntry<T> entry) where T : class { }

private IEnumerable<string> GetPrimaryKeys<T>() where T : class {}

}

จากโค้ดด้านบนจะเห็นได้ว่ามี 3 methods ซึ่งเป็น public 1, private 2  ซึ่งขออธิบายดังนี้

GetPrimaryKeys() Method เราเอาไว้ดึง primary key ทั้งหมดที่มีใน T class ที่เราส่งเข้ามาเพื่อทำการอัพเดท โดยดึง property name ออกมาเพื่อที่จะให้ข้ามการอัพเดทไป เพราะจะทำให้เกิด Error หากสั่งอัพเดทรายการนี้


private IEnumerable<string> GetPrimaryKeys<T>() where T : class
{    

ObjectContext objectthis = ((IObjectContextAdapter)this).ObjectContext;

ObjectSet<T> set = objectthis.CreateObjectSet<T>();

IEnumerable<string> keyNames = set.EntitySet.ElementType.KeyMembers.Select(k => k.Name);

    return keyNames;

}

ConfigPartialUpdate() Method จะมีหน้าที่ในการตรวจสอบ object ที่จะทำการอัพเดทโดยป้องกันการ attach object ซ้ำซ้อนใน DbContext


private IEnumerable<string> ConfigPartialUpdate<T>(T entity, out DbEntityEntry<T> entry) where T : class
{

    //set validate on save 
   

this.Configuration.ValidateOnSaveEnabled = false;


//get primary keys 

var pks = GetPrimaryKeys<T>();


//get object entry

entry = this.Entry(entity);


//if dbthis is already tracking T object then remove it all and attach new object

if (entry.State == EntityState.Detached && this.ChangeTracker.Entries<T>().Any())

{

foreach (DbEntityEntry<T> item in this.ChangeTracker.Entries<T>())

{

this.Entry<T>(item.Entity).State = EntityState.Detached;

}

this.Set<T>().Attach(entity);

}

//if T object is detached 

else if (entry.State == EntityState.Detached)

{

this.Set<T>().Attach(entity);

}

return pks;

}

PartialUpdate() Method เราจะใช้ในการกำหนด property ที่มีการแก้ไขค่าและบอกให้ EF ทำการอัพเดท
โดยใช้ params Expression<Func<T, object>>[] มาช่วยในการรับค่า property ที่ต้องการจะทำการอัพเดท


public void PartialUpdate<T>(T entity, params Expression<Func<T, object>>[] propsToUpdate) where T : class
{   

DbEntityEntry<T> entry;

var pks = ConfigPartialUpdate(entity, out entry);

foreach (var prop in propsToUpdate)

{

//if prop.name is pk go next loop

if (pks.Contains(prop.Name))

continue;

var errors = entry.Property(prop).GetValidationErrors();

if (errors.Count == 0)

{

entry.Property(prop).IsModified = true;

}

else

{

throw new DbEntityValidationException(string.Format({0} Model has errors please check.”, typeof(T).Name), errors.Select(o => new DbEntityValidationResult(entry, errors)));

}

}

}

..

SOLUTION 1 DEMO TEST

มาทดลอง method ที่เราเขียนกันไว้ดูด้วย code ข้างล่างนี้

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DemoPartialUpdate.DAL;
using DemoPartialUpdate.Domain;
namespace DemoPartialUpdate
{

class Program

{

static void Main(string[] args)

{

using (var db = new UserContext())

{

//If first run

if (db.Database.CreateIfNotExists())

{

for (int i = 1; i < 3; i++)

{

User user = new User();

user.Id = i;

user.UserName = “User” + i;

user.Password = “1234”;

user.LastLogin = DateTime.Now;

db.Users.Add(user);

}

db.SaveChanges();

}


var userx = db.Users.AsNoTracking().ToList();

Console.ForegroundColor = ConsoleColor.Yellow;

Console.WriteLine(“Before Update……..”);


foreach (var user in userx)

{

Console.WriteLine(“User Id {0}, UserName {1}, Password {2}, LastLogin {3}, user.Id, user.UserName, user.Password, user.LastLogin.ToString(“G”));

}

Console.WriteLine();


//Simulate DTO object

var userDto = new User();

userDto.Id = 1; //Id is a key it will not update

userDto.Password = “P@ssw0rd”; //Update

userDto.LastLogin = DateTime.Now.AddDays(1); //Update


//pass user object and specify update properties

db.PartialUpdate(userDto, x => x.Password, x => x.LastLogin);

db.SaveChanges();

userx = db.Users.AsNoTracking().ToList();

Console.ForegroundColor = ConsoleColor.Green;

Console.WriteLine(“After Update……..”);

foreach (var user in userx)

{

Console.WriteLine(“User Id {0}, UserName {1}, Password {2}, LastLogin {3}, user.Id, user.UserName, user.Password, user.LastLogin.ToString(“G”));

}

}

}

}

}

จากโค้ดด้านบนเป็นการสั่งให้ Create Database หากไม่มีอยู่
และให้ทำการ
loop 2 เพื่อสร้าง User Object ลงไปใน Database
จากนั้นทำการ simulate ด้วยการสมมติว่ารับค่ามาจากภายนอก โดยกำหนด userDto Object ขึ้นมาและระบุ Id = 1 เพื่อบอกว่าให้อัพเดทค่าไปที่ User.Id =1 ต่อมากำหนดค่า 2 property คือ Password, LastLogin เพื่อต้องการให้อัพเดทลง Database
แล้วเราก็ดึงออกมาแสดงผลหลังทำการ
Insert ลงไปครั้งแรกโดยระบุ AsNoTracking() เพื่อไม่ให้ DbContext เก็บค่า object เอาไว้ติดตามในระบบ

และถึงเวลาทดสอบ เราก็เรียก db.PartialUpdate() โดยส่ง userDto เข้าไปและใช้ lambada x=> กำหนด property ที่ต้องการจะให้ update เข้าไป

ต่อมาก็สั่ง SaveChanges() เพื่อให้ commit รายการลง Database

สุดท้ายก็ดึงค่า ขึ้นมาจาก Database ทั้ง 2 User เพื่อแสดงค่าที่เปลี่ยนแปลงไปของ 2 property ที่สั่ง PartialUpdate ไป

เราไปดูใน Database หลังจากที่รันโค้ดชุดด้านบนนี้ไปแล้ว จะพบว่าเจอ 2 records ใน Users Table และมีการ update ค่าถูกต้องที่ User.Id = 1

ซึ่งเราไม่ได้กำหนดค่าให้ UserName Property หากใช้ Update โดยการ Attach() ของ DbContext ตรงๆ จะทำให้กลายเป็น Null ไปนั่นเอง

..

SOLUTION 2 Implement INotifyPropertyChanged

วิธีนี้จะไปยุ่งยากกับ Entity Object ในระบบทั้งหมด แต่จะทำให้ PartialUpdate method สะอาดไม่รกเต็มไปด้วย Paramter ว่าแล้วก็เริ่มลงมือ

แรกเริ่มเราจะเพิ่ม UpdatePartial() method เข้ามาใน partial class UserContext ใหม่อีก 1 method


public partial class UserContext
{

//////////


public void PartialUpdate<T>(T entity) where T : EntityBase

{


DbEntityEntry<T> entry;

var pks = ConfigPartialUpdate(entity, out entry);


foreach (var prop in entity.PropertyChangedList)

{

if (pks.Contains(prop))

continue;

var errors = entry.Property(prop).GetValidationErrors();


if (errors.Count == 0)

{

entry.Property(prop).IsModified = true;

}

else

{

throw new DbEntityValidationException(string.Format({0} Model has errors please check.”, typeof(T).Name), errors.Select(o => new DbEntityValidationResult(entry, errors)));

}

}

}

///////////////

}

เสร็จแล้วให้ทำการสร้าง NotifyPropertyChangedInvocatorAttribute Class เอาไว้ช่วยให้เราไม่ต้องระบุ property name ในตอนเรียก ตาม Code ด้านล่างนี้

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute {

public NotifyPropertyChangedInvocatorAttribute() }

public NotifyPropertyChangedInvocatorAttribute(string parameterName)

ParameterName = parameterName; }


public string ParameterName { get; private set; }

}

ต่อไปจะสร้าง BaseEntity สำหรับเป็นตัวช่วยให้ Entity Classของเราเก็บค่า Property Name ที่มีการเปลี่ยนแปลงค่าจากการ assign ค่าได้

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace DemoPartialUpdate.Domain
{

public abstract class EntityBase : INotifyPropertyChanged

{


protected EntityBase(){ }


private HashSet<string> _propertyChangeList = new HashSet<string>();


public HashSet<string> PropertyChangedList { get { return _propertyChangeList; } }


public void AddPropertyChanged(string propertyName)

{

if (!_propertyChangeList.Contains(propertyName))

_propertyChangeList.Add(propertyName);

}


public void ClearPropertyChanges()

{

_propertyChangeList.Clear();

}


public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]

protected virtualvoid OnPropertyChanged([CallerMemberName] string propertyName = null)

{

PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));

AddPropertyChanged(propertyName);

}

}

}

จากนั้นไปเปิด User Class มาแก้ไขให้ได้ตามโค้ดด้านล่างนี้ โดยนำ EntityBase ไป Implement

using System;

using System.Collections.Generic;

using System.ComponentModel.DataAnnotations;

using System.Linq;

using System.Runtime.InteropServices;

using System.Text;

using System.Threading.Tasks;


namespace DemoPartialUpdate.Domain
{

public class User : EntityBase

{


private int _id;

private string _userName;

private string _password;

private DateTime _lastLogin;

public int Id {

get { return _id; }

set { _id = value; }

}

public string UserName {

get { return _userName; }

set {

_userName = value;

OnPropertyChanged();

}

}

public string Password {

get { return _password; }

set {

_password = value;

OnPropertyChanged();

}

}

public DateTime LastLogin {

get { return _lastLogin; }

set {

_lastLogin = value;

OnPropertyChanged();

}

}

}

}

SOLUTION 2 DEMO TEST

ให้เรา copy test class จาก Solution 1แล้วเปลี่ยนเป็น db.PartialUpdate(userDto);

ก่อนรันให้ไปลบ UserDB Database ทิ้งก่อน

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DemoPartialUpdate.DAL;
using DemoPartialUpdate.Domain;
namespace DemoPartialUpdate
{

class Program 

{

static void Main(string[] args)

{

using (var db = new UserContext())

{

//If first run

if (db.Database.CreateIfNotExists())

{

for (int i = 1; i < 3; i++) {

User user = new User();

user.Id = i;

user.UserName = “User” + i;

user.Password = “1234”;

user.LastLogin = DateTime.Now;

db.Users.Add(user);

}

db.SaveChanges();

}


var userx = db.Users.AsNoTracking().ToList();

Console.ForegroundColor = ConsoleColor.Yellow;

Console.WriteLine(“Before Update……..”);


foreach (var user in userx)

{

Console.WriteLine(“User Id {0}, UserName {1}, Password {2}, LastLogin {3}, user.Id, user.UserName, user.Password, user.LastLogin.ToString(“G”));

}

Console.WriteLine();


//Simulate DTO object

var userDto = new User();

userDto.Id = 1; //Id is a key it will not update

userDto.Password = “P@ssw0rd”; //Update

userDto.LastLogin = DateTime.Now.AddDays(1); //Update

//pass user object and specify update properties

db.PartialUpdate(userDto);

db.SaveChanges();

userx = db.Users.AsNoTracking().ToList();

Console.ForegroundColor = ConsoleColor.Green;

Console.WriteLine(“After Update……..”);

foreach (var user in userx)

{

Console.WriteLine(“User Id {0}, UserName {1}, Password {2}, LastLogin {3}, user.Id, user.UserName, user.Password, user.LastLogin.ToString(“G”));

}

}

}

}

}

ซึ่งก็จะได้ผลลัพธ์เหมือนกับ Solution 1

Conclusion

สรุปว่าทั้งสองวิธีมีข้อดีและข้อเสียแตกต่างกัน แต่ได้ผลลัพธ์เดียวกัน

แบบที่ กำหนด property ที่ต้องการด้วยตัวเอง

ข้อดีคือ ใช้งานง่ายเหมาะกับระบบที่มีโมเดลอยู่แล้ว และไม่ต้องการจะไปรื้อโครงสร้างให้วุ่นวาย
ข้อเสียคือ จำเป็นต้องระบุ
property ที่ต้องการให้ Change ค่าเอง ซึ่งก็อาจจะหลงๆลืมๆส่งไปไม่ครบก็ได้ อาจจะทำงานผิดพลาด

แบบที่ ใช้ INotifyPropertyChanged เข้าช่วยในการตรวจสอบการเปลี่ยนแปลง

ข้อดีคือ ไม่ต้องกังวลว่าจะลืมส่ง Property อะไรไปบ้าง จุดเรียกใช้งานก็ใช้ง่าย
ข้อเสียคือ ต้องแก้ไข
entity class อาจจะกระทบงานเก่าที่มีอยู่

หวังว่าตอนนี้จะเป็นประโยชน์ ในการนำไปใช้งาน และช่วยเพิ่มความรู้และเทคนิคต่างๆให้เพื่อนๆกันได้ไม่มากก็น้อย

download source code  

จบครับ

Advertisements

03/06/2010

[Article] Database Series : ทดสอบใช้ SQLite 3 กับ .NET Application (Using Sqlite 3 with .NET Application)

Filed under: Database — Nine MVP @ 12:19 pm

Agenda :

  • Database Series : ทดสอบใช้ SQLite 3 กับ .NET Application (Using SQLite 3 with .NET Application)
  • Database Series : มารู้จักกับ OODB กันเถอะ next…

Tools

  1. Visual Studio 2010/2008 ตัวใดก็ได้ใช้ได้เหมือนกัน
  2. SQLite3 Precompiled Library – Database Command-Line เลือกเอา for windows http://www.sqlite.org/sqlite-3_6_23_1.zip
  3. System.Data.SQLite .NET Data Provider (Support VS2008/VS2010) กดเพื่อ download ที่นี่
  4. SQLite Expert Personal Edition (Free) เอาไว้จัดการดาต้าเบสแบบ GUI กดเพื่อ download ที่นี่

Introduce

พอดีมีถามตอบประเด็นในกระทู้ ก็เลยเอ๊ะ!! เจ้า SQLite หากจะใช้งาน Embedded DB ตัวนี้ ต้องทำอย่างไรบ้างน๊ออ เนื่องจากความสามารถของเจ้า SQLite นี้มีชื่อเสียงระบือไกลไม่ต่างกับเจ้า MySql (เป็นที่กล่าวขานกันได้ด้านดี) บทความนี้เป็นเพียงพื้นฐานแนะนำการใช้งานเท่านั้น เพราะผู้เขียนก็ขอทดสอบเพียงแค่นี้

SQLite Embedded Database

ต้องขอบอกว่า SQLite เหมาะกับงานเล็ก ส่วนมากเหมาะจะเอาไปฝังไว้กับพวกอุปกรณ์ hand held ต่าง ๆ   หรือจะทำเป็นดาต้าเบสขนาดย่อม ๆ สำหรับเก็บข้อมูลที่ฝั่ง client ก็ดี ซึ่ง SQLite คือEmbedded Database เป็นระบบ File System ไม่ต้องติดตั้ง ไม่ต้องสตาร์ทเซอร์วิสใดๆ ขึ้นมาทำงาน  รองรับมาตรฐาน SQL-92   มีความรวดเร็วในการทำงานสูง กินเมมโมรี่ต่ำ สามารถทำงานได้หลายแพลทฟอร์ม รองรับการทำ Transaction ไม่ต้องตั้งค่ากำหนดค่าการทำงานอะไร เริ่มต้นก็สร้าง Database/ Table ใช้งานได้เลย

Basic SQLite

1. หลังจากได้ดาวน์โหลดเจ้า Command-Line ในหัวข้อ Tools –> 2. สำหรับสร้าง Database สร้าง Table มาแล้วให้ทำการแตก zip ออกมาจะได้ sqlite3.exe

2. วิธีการใช้งาน ก็ให้เปิด command ขึ้นมาใช้งานโดยไปที่ Start –> Run –> พิมพ์ Cmd กด Enter จากนั้นก็ Dir ไปยัง folder ที่แตกไฟล์ในข้อ 1 ไว้

ดังภาพของผู้เขียนแตก sqlite3.exe ไว้ใน folder SQLiteTest ก็จะใช้คำสั่ง  >Dir SQLiteTest  เข้าไปยังโฟลเดอร์ดังภาพ

3. สร้าง Database แสนง่ายด้วยคำสั่ง C:\SQLiteTest>SQLite3  Customer.db

จะได้คำสั่งรอเตรียมพร้อมสำหรับการสร้าง Table ดังภาพ

4. มาทดลองสร้างเทเบิ้ล และคำสั่ง CRUD พื้นฐานกันดูครับ พิมพ์สคริปเข้าไปตรง ๆ จบสคริปด้วย “;”

    4.1  เปิดการใช้งาน Foreign key ด้วยคำสั่ง

>PRAGMA FOREIGN_KEYS = ON;

จากนั้นพิมพ์ script ตามนี้เพื่อสร้างเทเบิ้ลสำหรับ Customer table และ Address table ซึ่งมี Foreign Key ผูกกันไว้

 

    4.2 ทดสอบคำสั่ง INSERT

ทำการ insert data ลง customer table

ทำการ insert data ลง address table

    4.3 ทดสอบคำสั่ง Select ในแบบต่าง ๆ

Select * from table

Select with Inner Join

 

    4.4 ทดสอบคำสั่ง Update

 

    4.5 ทดสอบคำสั่ง Delete

เมื่อเสร็จสิ้นการใช้งาน ก็พิมพ์คำสั่ง  >.quit เพื่อออกจากการใช้งาน  และเมื่อลองกลับไปดูในโฟลเดอร์ SQLiteTest จะพบไฟล์ customer.db ปรากฎอยู่ดังภาพ

SQLite Database Management Tools

เพื่อความง่ายในการจัดการโครงสร้างและแก้ไขข้อมูลต่าง หลาย ๆ ท่านนิยมที่จะใช้ GUI tool ตามแต่ถนัดของแต่ละท่าน ซึ่งของฟรีมีให้ใช้เยอะมากครับ ไม่ว่าจะเป็นWeb Tool, Application Tool, command-line tool ในหัวข้อนี้ขอแนะนำตัวนี้ครับ SQLite Expert Personal Edition เพียงพอสำหรับใช้พัฒนาและทดสอบครับ ใช้งานง่ายด้วยสิ

   

 

SQLite Database Driver And Test Drive

สำหรับวิธีการเขียนโปรแกรมติดต่อกับ SQLite นั้น มีหลายวิธีเช่นกันทั้ง C/C++, TCL , ODBC, JDBC, ADO.NET ครบทุกค่าย แต่ผู้เขียนได้ลองค้นหา ADO.NET Data Provider สำหรับใช้งานเจ้า SQLite ปรากฎว่าพบกับ SQLite.NET ADO.NET Provider ใน version 1.0.66.0 นี้จะทำงานกับ VS2008/VS2010 ได้ โดย support ทั้ง Typed DataSet Designer, Entity Framework (ยังทำงานไม่ดีนัก)  จึงได้นำมาทดลองใช้งานในบทความนี้

ให้ดาวน์โหลดและทำการติดตั้งได้เลยครับ จากนั้นเปิด Visual Studio ขึ้นมาเพื่อทดสอบเขียนโค้ดเพื่อทำงานกับ Customer.db ที่เราสร้างไว้ในขั้นตอนก่อนหน้านี้

ซึ่งเข้าไปเอา Driver ได้ที่นี่ C:\Program Files (x86)\SQLite.NET\bin\System.Data.SQLite.dll 

ดูรูปแบบของ connection string ได้ที่นี่ http://www.connectionstrings.com/sqlite  ในส่วน sqlite.net

จากนั้นก็เขียน Code ติดต่อ Database ตามปกติ

using System;
using System.Data;
using System.Windows.Forms;
using System.Data.SQLite;

namespace SQLiteTester
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private string constr = @"data source=Customer.db";  //กรณีโปรแกรม exe อยู่ folder เดียวกับ customer.db

        private void Form1_Load(object sender, EventArgs e)
        {
            dataGridView1.DataSource = GetCustomerData().Tables[0];
        }

        public DataSet GetCustomerData()
        {
            using (var conn = new SQLiteConnection(constr))
            {
                var ds = new DataSet();
                using (var da = new SQLiteDataAdapter(
                    "select * from customer c inner join address a on c.id = a.customer_id", conn))
                {
                    da.Fill(ds, "CustomerData");
                    return ds;
                }
            }
        }
    }
}

 

ผลลัพธ์เมื่อกด RUN (F5)

 

ปล. สำหรับภาษาไทยให้ใช้พวก NVARCHAR, NCHAR จะรองรับ unicode language ได้

 


สามารถ download source code ได้ที่นี่

Code Example Download Here!!

 

Chalermpon Areepong Nine (นาย)

Microsoft MVP Thailand ASP.NET

Greatfriends.biz Community Leader

 

Email : nine_biz-talk.net at hotmail dot com

Blog : http://nine-biztalk-net.spaces.live.com/blog

Create a free website or blog at WordPress.com.