SET COLLATE ชอง SQL SERVER ผ่าน ENTITY FRAMEWORK แบบเอาให้มั่น คั้นให้อยู่มือกันเลยน่ะครับ
หัวข้อ :
ต้องการ set collate ของ sql server(ฐานข้อมูลอื่นก็ใช้ได้ครับไปเพิ่ม .net connector ของฐานข้อมูลนั้นมาก็พอ) database ให้สนับสนุนภาษาไทย เพราะในบางกรณีเราไม่ได้เป็นคนติดตั้งตัว SQL SERVER เองหรือไปยุ่งกับค่า DEFAULT ของเครื่องที่ติดตั้งไม่ได้ ทำให้ไม่แน่ใจว่าจะได้ฐานข้อมูลที่ suport ภาษาไทยตามต้องการไหม
ที่มาที่ไป :
คือใช้ link https://www.thaicreate.com/dotnet/forum/062440.html ใช้ในงานของตัวเอง เลยเอาสิ่งที่ได้ไปกลับมาคืนน่ะครับ
framework :
Entity framework 6.2.x , ADO.NET พร้อม.net connector ของฐานข้อมูลนั้นตามข้างต้น
วิธีการ/ลำดับการทำงาน :
ใช้ t-sql ในการสั่งให้ database เปลี่ยน collate ตามที่ต้องการ (ในกรณีนี้คือ Thai_CI_AS ) จะใช้ ALTER DATABASE เป็นหลัก แต่เนื่องจากคำสั่ง ALTER นี้เป็นกลุ่ม DDL Command ทำให้มีปัญหาหลายอย่างจนสั่งผ่าน RAW SQL EXECUTE ของ Entity framework (ขอใช้ EF ละกันครับยาวเกิ้น) จึงต้องทำผ่าน ADO.NET แบบดั้งเดิมแทน อีกทั้งเป็นคำสั่งที่ทำให้เกิด effect อย่างมากจึงแนะนำให้ใช้ขณะที่ไม่เกิดผลกระทบมากมาย ส่วนตัวผมเองนั้นเนื่องจากเป็นสาย code first เลยเอาไปติดตั้งขณะที่ฐานข้อมูลกำลังถูกสร้างครั้งแรก (ตัวงานจะไม่มีฐานข้อมูลเลย จะสร้างครั้งเดียวตอน run ครั้งแรกแบบอัตโนแมว และอาจมีการอัพเดทบ้างนิดๆหน่อยๆ) แต่ก็เอาไปประยกต์ใช้เรียกที่ตำแหน่ง/ลำดับไหนก็ได้ตามที่เห็นสมควรครับ และที่ต้องเรียกผ่าน ADO.NET แบบ Asyncronous ก็เพราะชุดคำสั่งนี้ต้องทำไล่เรียงกันไป อีกทั้งต้องให้แน่ใจว่าทำเสร็จครบทุกคำสั่งจริงๆ ไม่งั้นมันมีเพี้ยน เช่น รันผ่านไปแล้ว แต่ฐานข้อมูลถูก lock ไว้ใน mode single user กันเลยทีเดียว
รายละเอียด :
code นี้เป็นแกนหลักของทั้งหมด ตามร่ายมานั่นแหละครับ เอาไปแปะตรงไหนก็ได้ตามสมควร แต่ระวังๆกันหน่อยละกันครับ ผิดที่ผิดเวลานี่ความปวดกะบาลตามมาแน่นวล
ปล.ทำไมต้องเป็น static method เพราะมันจะมี overload แบบ extension mehod เพื่อความสะดวกในการเรียกใช้ต่่างกรรมต่างวาระน่ะครับ และตัวมันเองจะเรียกใช้ method อื่นอีก จะลงไล่ลำดับถัดจาก overload ของมันไปนะครับ อ้อ overload นี่ถ้าไม่ใช้ก็ไม่ต้องสนใจมันนะครับ จะเฉดหัวขับไล่ไสส่งก็ตามที่สะดวก
Code (C#)
public static void SQLServerSetDatabaseCollation
(string ConnectionString, string DatabaseName, string SpecificCollation = "Thai_CI_AS")
{
//ref https://www.thaicreate.com/dotnet/forum/062440.html
string tempStr = string.Empty;
StringBuilder sb = new StringBuilder();
// WITH SQL-DDL COMMAND NOT ALLOW PASS VALUE FROM PARAMETER
// ENTITY.EXECUTESQLCOMMAND NOT SUPPORT ALTER COMMAND THEIR YIELD ERROR WITH CONNECTION RESET
// THEN EXECUTE PASSTHROUGH TRADDITION (ASYNC)ADO.NET
sb.Append("IF DB_ID('"+ DatabaseName + "') IS NOT NULL");
sb.Append("BEGIN");
sb.Append(" ALTER DATABASE " + DatabaseName + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE;");
sb.Append(" ALTER DATABASE " + DatabaseName + " COLLATE " + SpecificCollation + ";");
sb.Append(" ALTER DATABASE " + DatabaseName + " SET MULTI_USER;");
sb.Append("END ;");
tempStr = sb.ToString();
// USE ASYNC CALL PREVENTS ALL RESULT HAVE COMPLETED ONE BY ONE.
SqlServerAsyncExecuteCommandWithADODotNet(ConnectionString, tempStr)
.ContinueWith(t => Console.WriteLine(t.Exception)
, TaskContinuationOptions.OnlyOnFaulted);
}
Overload :
Code (C#)
public static void SQLServerSetDatabaseCollation
(this SqlConnectionStringBuilder SpecificConnectionBuilder, string SpecificCollation = "Thai_CI_AS")
{
SQLServerSetDatabaseCollation(SpecificConnectionBuilder.ConnectionString
, SpecificConnectionBuilder.InitialCatalog, SpecificCollation);
}
public static void SQLServerSetDatabaseCollation<TContext>
(this TContext Context, string SpecificCollation = "Thai_CI_AS")
where TContext : DbContext
{
SQLServerSetDatabaseCollation(Context.Database.Connection.ConnectionString
, Context.Database.Connection.Database, SpecificCollation);
}
inner call method :
Code (C#)
public static async Task<int> SqlServerAsyncExecuteCommandWithADODotNet(string ConnectionString
, string CommandText, params object[] Parameters)
{
int iret = 0;
string tempStr = CommandText;
string ParamName = string.Empty;
try
{
using (var conn = new SqlConnection(ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
for (int i = 0; i < Parameters.Length; i++)
{
ParamName = "@param" + (i + 1).ToString();
tempStr = tempStr.Replace("{" + i.ToString() + "}", ParamName);
cmd.Parameters.AddWithValue(ParamName, Parameters[i]);
}
cmd.CommandText = tempStr;
await conn.OpenAsync();
await cmd.ExecuteNonQueryAsync();
iret = 1;
}
}
}
catch (SqlException e)//(Exception e)
{
//Debug.WriteLine("At SqlServerAsyncExecuteCommandWithADODotNet : [" + e.Message + "]");
iret = -1;
}
return iret;
}
usage:
การใช้งานก็เรียกใช้แบบบ้านๆเดิมๆนั่นแหละครับ
Code (C#)
public CreateDatabaseIfNotExistsWithCollationEx(TContext context, string Collation = "Thai_CI_AS")
{
if (!context.Database.Exists())
{
context.Database.CreateIfNotExists();
context.SQLServerSetDatabaseCollation<TContext>(Collation);
}
}
อ่อปิดท้ายด้วยอีก method ที่เอาใช้ตรวจสอบ collate ของ database และส่วนยิบย่อยเพิ่มเติมเดี๋ยวจะต่อใน comment อีกทีข้างล่่างนะครับ เดี๋ยวมันจะยาวไป
Code (C#)
public static string SQLServerGetDatebaseCollation<TContext>(this TContext SpecificDBContext)
where TContext : DbContext
{
StringBuilder sb = new StringBuilder();
string DatabaseName = SpecificDBContext.Database.Connection.Database;
sb.AppendLine("SELECT collation_name");
sb.AppendLine("FROM sys.databases");
sb.AppendLine("WHERE [name] = @databaseName;");
var DatabaseNameParam = new SqlParameter("@databaseName", DatabaseName);
//for debug only
String str = sb.ToString();
str = SpecificDBContext.Database.SqlQuery<string>
(str, DatabaseNameParam).FirstOrDefault<string>();
return str;
}
Tag : .NET, Ms SQL Server 2016, C#, Windows
ประวัติการแก้ไข 2020-04-22 02:04:54 2020-04-22 02:05:21 2020-04-22 02:31:46 2020-04-22 02:40:26
Date :
2020-04-22 02:01:54
By :
baka baka
View :
1516
Reply :
9
ดูๆ code แล้วมันก็ไม่ค่อยเกียวกับ EF สักเท่าไหร่ แต่ในการใช้งานดังที่กราบหว่างอกหว่างใจไปกันแล้วนั้น ผมใชังาน ณ ตำแหน่งการติดตั้งครั้งแรก ซึ่งตามแบบแผนของ EF เขาจะให้ยุ่งกับการทำงานส่วนนี้ผ่านทาง database initializer ซึ่งมีด้วยกัน 4 แบบ ตามนี้ >> https://www.entityframeworktutorial.net/code-first/database-initialization-strategy-in-code-first.aspx เหตุผลก็ดังว่า ไม่อยากรบกวนใจระบบ ตรงนี้ทำกันครั้งเดียวให้มันจบๆไป เพื่อการนี้ผมเลือกใช้ custom initializer แบบว่าไหนๆก็ไหนๆแล้วน่ะครับ ตัว code จะวางลงไป 2 ส่วนคือ
1. ส่วน custom class ที่สืบแล้วทอดมาจาก interface ที่ชื่อว่า IDatabaseInitializer (แม่แบบของ database initializer ทั้งมวล) ก็ยัดส่วนสร้าง database แล้วก็ ALETER Collate มันเสีย ที่ constructor ของเขาไป
Code (C#)
public class CreateDatabaseIfNotExistsWithCollationEx<TContext>
: IDatabaseInitializer<TContext> where TContext : DbContext
{
public void InitializeDatabase(TContext context) { }
public CreateDatabaseIfNotExistsWithCollationEx(TContext context, string Collation = "Thai_CI_AS")
{
if (!context.Database.Exists())
{
context.Database.CreateIfNotExists();
context.SQLServerSetDatabaseCollation<TContext>(Collation);
}
}
}
2. ใน DbContext ส่วน constructor ของเราเพื่อติดตั้ง database initializer ให้ลงล้อกตามแบบแผนของตัว EF ไป ที่เหลือก็ปล่อยให้มันไหลไปตาม flow ครับ
Code (C#)
public class SchoolContext : DbContext
{
public SchoolContext() : base()
{
SetContextInitializer();
}
public SchoolContext(string ConnectionString) : base(ConnectionString)
{
SetContextInitializer();
}
void SetContextInitializer()
{
Debug.WriteLine("SetContextInitializer");
Database.SetInitializer<SchoolContext>
(new CreateDatabaseIfNotExistsWithCollationEx<SchoolContext>(this,"Thai_CI_AS"));
//Database.SetInitializer<SchoolContext>(new DropCreateDatabaseAlwaysWithThaiCollation<SchoolContext>());
}
public DbSet<Student> Students { get; set; }
public DbSet<Grade> Grades { get; set; }
}
ปล. อันที่จริงเรื่อง Collate นี่มันจะง่ายกรุบๆกริบๆ เพียงแค่เราไป set ผ่าน SQL Management ที่เขาแจกๆกันนั่นก็ได้ แต่จากประสบการณ์นี้บางที่บางเจ้าบางคนท่านไม่ยอมให้เราไปยุ่งครับ ก็เลยเตรียมๆแบบ worst case เอาไว้ก่อน เดี๋ยวงานจะสดุดเพราะเรื่องเล็กๆ ก็เท่านั้นเองครับ
ประวัติการแก้ไข 2020-04-22 02:22:37
Date :
2020-04-22 02:19:24
By :
baka baka
เปลี่ยน วิธีใช้เป็นของตระกูล n???? ไม่ว่าจะ collate เป็นภาษาใด ก็ไม่เกี่ยวกับของเราแล้วครับ
เช่น nchar nvarchar ntext แถมรองรับ ทุก web server
Date :
2020-04-22 07:13:50
By :
Chaidhanan
@baka baka คุณกำลังจะบอกอะไรครับ
คุณใช้คำว่า ประสบการณ์ แต่ถ้าผมบอกอายุของคุณให้รับทราบรับรู้ คุณก็จะงงงง เหมือนผมงงงง เหมือนกัน
Source Code ของคุณ สำหรับผม มันก็ไม่แตกต่างกับคุณยก Dbase/FoxPro/Clipper/Clarion/etc..
มาอธิบายให้ผมฟัง คุณไม่เข้าใจก็ไม่เป็นไร
ปล. คุณอธิบายได้ดีครับ แต่วิธีของคุณ ผมมองว่า เด็กอนุบาล
Date :
2020-04-23 19:31:23
By :
หน้าฮี
ถ้าคุณยังสงสัยในตัวของคนชื่อหน้าฮี จะบอกให้ก็ได้ มีสักกี่คนที่เขียน SQL Qeury ปิดงบทางบัญชีได้
ที่เห็นฯ ก็ธรรมดาฯ
ผมคิดแบบนี้นะ
ปล. เก่ง C#/VB/etc.. มากแค่ไหน มันก็ยังไม่พอหรอก ถ้าความน่าเชื่อถือของคุณมันต่ำพอฯ กับตัวของคนชื่อหน้าฮี
ไม่ได้ว่าคุณนะ แต่อยากแนะนำและเล่าประสบการณ์จริงแท้ ให้ฟังก็เท่านั้นเอง
(ไม่เคยคิดจะยกมือไหว้ใครเลย ในเมืองไทยนี้)
Date :
2020-04-23 19:48:43
By :
หน้าฮี
โอ้ ดราม่าได้ด้วยหรอเนี่ย ก็ถ้าไปกระทบอะไรใครก็ขออภัยอย่างแรงๆ ไม่ได้ตั้งใจครับ
( คือถ้าตั้งใจจะตรงไปตรงมาไม่ต้องอ้อมอะไรกันน่ะครับ )
สาเหตุที่เอามาลงก็เพราะผมยกเอาส่วนนึงที่ใช้การอ้างอิงจากที่นี่ไป แล้วเอาไปใช้แบบนั้นอย่างนั้นแหละ
นี่คือจุดประสงค์ ผมก็ว่าผมเขียนตรงๆ ไปอย่างชัดเจนอยู่นะ ถ้าอ่านแล้วตีความเป็นอย่างอื่นก็เอาที่สะดวกครับ
จริงๆว่าจะเอามาลงตั้งนานแต่ก็ลืมแล้วลืมอีก เพราะใช้งานมาพอสมควร ซึ่งปัจจุบันเปลี่ยนเป็น EF CORE
ไปรื้อเอา code บางส่วนมาใช้ใหม่กับ .net core เลยเจอ แล้วก็เอามาลงเอาตอนนี้
ส่วนจะน่าเชื่อถือหรือเอาไปทำประโยชน์ได้ไหมก็แล้วแต่จะเห็นสมควร ผมไม่ได้เรียกร้องนี่นา
อ้อ ส่วนเรื่องว่าจะผมสนใจใครอะไรในตัวคุณหรือเปล่า ตอบชัดๆว่าไม่เลย ไม่สักนิด ไม่อย่างสิ้นเชิงครับ
แต่ยังไงก็จะถือว่าเป็นคำติชมทั่วไป ส่วนที่มีประโยชน์ก็จะขอขอบคุณมาก ส่วนที่อะไรๆนั่นก็แล้วๆกันไปครับ
ปะครับแยกย้ายกันไปทำงานเถอะ
ปล.สมัครแล้วก็ลืม password ต้องขออภัยอย่างสูงครับ
Date :
2020-04-24 00:50:57
By :
ลืม password เองล่ะ
เคยได้ยินผีเรียกอย่าขานรับ หมากัดอย่ากั.... หรือไม่
แต่จริงๆ ที่ที่เราทุกคนอยู่ คือโลกแห่งมายา
ถ้าในสังคมมีแต่คนดีหรือคนเก่ง ความต่างไม่เกิด
แรงผลักดันสู่การพัฒนา ก็จะไม่มี ทุกอย่างล้วนมีสีสัน
พูดดีใช่ว่าหวังดี พูดไม่ดีใช่ว่าจะหวังร้าย ไม่มีคำว่าบังเอิญ
ทุกสรรพสิ่งล้วนมีที่มา ดอกบัวมีหลายเหล่า
บางคนเกิดมารวยแต่ใช้เงินไม่เป็นมันก็จนได้
วิทยาการความรู้ไม่หมั่นทบทวนก็จะหลงลืม
ดังนั้น ความสุขที่แท้จริงคืออะไร ทุกคนย่อมมีคำตอบให้กับตัวเอง
อยู่ที่ว่าจะเปิดเผยหรือไม่ จะแสดงออกมากน้อยแค่ไหน
ขอบคุณสำหรับ ประสบการณ์ที่นำมาแบ่งปันครับ
Date :
2020-04-24 06:44:52
By :
PhrayaDev
Load balance : Server 01