วันอาทิตย์ที่ 2 พฤศจิกายน พ.ศ. 2557

ออกแบบระบบให้ยืดหยุ่นด้วย Dependency Injection (DI) Framework

 MESSAGE #129281 (อ่าน 9,259 ครั้ง)

[Article] : ออกแบบระบบให้ยืดหยุ่นด้วย Dependency Injection (DI) Framework

Tags: Web, C#, .NET 4.0, VS 2010, OOAD/UML, Coding, Article
reference my blog : http://nine69.wordpress.com/ 

Programming Level:

  • Intermediate – Advance

Computer Skills:

  1. Object Oriented Programming (Interface/Abstract programing)
  2. Layer Architecture
  3. C# 3.0, LINQ
  4. ASP.NET 

Development Tool and Library

  1. Visual Studio 2010

Background: Application Layer Design

มาถึงตอนสำคัญอีกตอนครับ สำหรับกลุ่มนักพัฒนาที่ชอบออกแบบและเขียนโปรแกรม  ในตอนนี้ผมขอเสนอเรื่องราวเกี่ยวกับการออกแบบระบบโดยใช้ PoEAA ตัวที่เรียกว่า Inversion of Control โดยวิธี Dependecy Injection ซึ่งเป็นเทคนิคและแนวทางที่กำลังนิยมอยู่ในปัจจุบัน ซึ่งช่วยให้ระบบมีความยืดหยุ่นในการทำงานทุกอย่าง แต่ก็มีข้อแลกเปลี่ยนซึ่งเป็นข้อเสียเช่นกัน  แต่เมื่อประมาณการแล้วข้อเสียนั้นเล็กน้อยมากเมื่อแลกกับข้อดีที่ได้มาเต็ม ๆ
แต่ก่อนที่ผมจะเข้าสู่การอธิบาย เจ้าด้วย DI ผมขอยกเรื่องราวตัวอย่างในปัญหาการพัฒนาโปรแกรมในชีวิตจริง ของพวกเรา ๆ ที่เริ่มเขียนโปรแกรมไว้ประมาณนี้ครับ
นายเอิธเป็นโปรแกรมเมอร์ที่เพิ่งเรียนจบ ตั้งใจจะรับงานฟรีแลนซ์ กะว่าทำงานคนเดียวคงรวยไม่ต้องแบ่งใคร ในเดือนแรกนายเอิธได้รับงานมาทำเป็น Windows App  ได้ไปเก็บรีไควเม้นท์ได้ ER Diagram, DB Dictionary, Screen  และ business กลับมา  จากนั้นก็เริ่มเขียนโปรแกรม โดยเริ่มจากการออกแบบดาต้าเบส  และเปิด visual studio ขึ้นมา และสร้างโปรเจ็คชื่อว่า EarthAccounting  และสร้างหน้าจอสำหรับแสดงผลและบันทึกข้อมูล  โดยนายเอิธ มักจะกดดับเบิ้ลคลิ๊กปุ่มเพื่อให้เกิด click event แล้วก็เริ่มเขียนบันทึก อัพเดทข้อมูลกันภายในอีเว้นท์นั้น   นายเอิ๊กต้องทำ Form ของโปรแกรม 50 Form เลยทีเดียว  ปรากฎว่าเหนื่อยมาก  เวลาที่จะต้องส่งงานก็ใกล้เข้ามา  เงินที่ฝันไว้กำลังจะโดนหักหากส่งงานไม่ทันตามกำหนด  เนื่องจากทำอยู่คนเดียวงานจึงเสร็จล่าช้า  จึงตัดใจยอมดึงเพื่อนที่จบมาด้วยกันมาช่วยงานโดยแบ่ง form ให้ไปช่วยทำ   ทำไปได้ซักพักปรากฎว่าเริ่มมีฟังชั่นที่ใช้งานซ้ำ ๆ เกิดขึ้นจำนวนมากภายในฟอร์ม  นายเอิธจึงตกลงกับเพื่อนอีกคนในทีมว่า “เพื่อน เราจะแยกฟังชั่นที่ใช้ซ้ำ ๆ พวกนี้ไว้ใน utility class นะ  แล้วเพื่อนค่อยเอาไปใช้นะ”   หลังจากตกลงกันได้นายเอิธและเพื่อนก็เริ่ม refactoring code ในส่วนของฟังชั่นที่ได้ตกลงกันไว้  หลังจากทำงานไปได้ครึ่งทางปรากฎว่าเกิดมีการเปลี่ยน requirement โดยของแก้ business ส่วนกลางที่ใช้ในการคำนวนค่าทางบัญชี โดยการเปลี่ยนครั้งนี้กระทบกับ form ทั้งหมด 25 form 50 function เนื่องจากเป็นส่วนคำนวนที่ฝังโค้ดเอาไว้ในปุ่ม และยังมีงานเหลืออีก 15 Form ตั้งใจว่าจะ out source งานส่วนนี้ให้มือปืนรายอื่นเข้ามาช่วยงาน  แต่ต้องพบกับปัญหาที่ว่าไม่สามารถบอกโครงสร้างทางบิสซิเนสของลูกค้าให้บุคคลอื่นที่นอกเหนือจากสัญญาจ้างที่ได้เซ็นกันไว้ก่อนนี้  จนนายเอิธรู้สึกท้อใจกับการทำงานแนวทางนี้ จึงได้เปิดเว็บไปหากูเกิ้ล พบทางสว่างแห่งการลดปัญหาที่ว่า สามารถแบ่งงานกันทำได้ แก้ไขแล้วไม่กระทบระบบมากนัก ลดความซ้ำซ้อนและกระจายตัวของระบบงาน ก็คือการแบ่งโค้ดออกเป็น Layer โดยนายเอิธเห็นแนวทางว่าต้องแบ่งระบบออกเป็น 3 Layer (3 project) โดยมี
1. Presentation Layer (PL)  ส่วนที่ใช้แสดงผลได้แก่พวกกลุ่ม windows form, web form เป็นต้น
2. Business Logic Layer (BLL) แยกส่วนที่บรรจุลอจิกทาง business ออกมารวมกันไว้เป็นคลาส
3. Data Access Layer (DAL) เป็นส่วนที่ใช้ติดต่อกับดาต้าเบส
โดยทั้ง 3 layer มีความสัมพันธ์กันดังนี้ PL –> BLL –> DAL โครงสร้างที่นายเอิธใช้ในรอบแรกมีลักษณะเป็นแบบนี้ครับ

Sample01 Pattern : Basic structure
เริ่มต้นเริ่มแรกใครได้ลองเขียนแยก layer ของโปรเจ็คออกเป็นชิ้นย่อย ๆ PL,BLL,DAL หากจุดไหนใครอยากเรียกใครก็แค่ add reference เข้ามาแล้วก็สร้างอินแสตนท์และเรียกใช้งานกันตรงนั้นดังตัวอย่างนี้
image
กรณีใน Business Logic Layer (BLL) ที่จะเรียกใช้ Data Access Layer (DAL)image
จาก class diagram จะพบว่า CustomerBLL มี association กับ CustomerDAL ตรง ๆ อีกนัยก็คือได้ add reference ของ project เข้ามาใช้งาน ลองดูโค้ดด้านล่างนี้ครับ
image
จากภาพจะพบได้ว่าใน line 3,7 มีการประกาศตัวแปรทีและสร้างอินสแตนท์ของ CustomerDAL ขึ้นใช้งาน ซึ่งเป็นการอ้างถึง DAL ตรง ๆ จะทำให้เราต้องมีการสร้าง DAL ขึ้นก่อน ถึงจะสามารถเขียน BLL ต่อไปได้ หรือแม้กระทั่งการทำเทสก็ยากต่อการทดสอบในกรณีที่ต้องแยกเทสแต่ละ Layer เพราะ BLL ไม่สามารถเทสโดยข้ามการสร้างอินแสตนท์ของ DAL ไปได้ และ DAL ยังถูกเรียกใช้ในทุกๆ จุดของเม็ธธอดของ BLL นั่นเอง
และในส่วนของ dependency diagram จะเห็นว่ามี 2 DLL ที่เรียกใช้งานกันตรง ๆ ซึ่งเป็นคำพูดที่ว่า Tight Coupling นั่นเอง
image

ต่อมาแหม่ ทำงานพอได้ระบบใหญ่เข้าหน่อยชักฮึกเหิม ไม่นานนายเอิธก็บรรเจิดปิ๊งไอเดียที่ว่า อยากให้ EarthAccounting รองรับดาต้าเบสได้มากกว่า 1 ชนิด ต้องทำอย่างไร แน่นอนครับพึ่งพากูเกิ้ลอีก เสริชเข้าไปจนได้ความรุ้ที่ว่า “ก็ใช้ Class/Interface + Factory Method Pattern สิคร้าบบ” นายเอิธไม่รอนานรีบก่อนจะสายเวลาลงมือรื้อ BLL และ DAL ใหม่ทั้งยวง ตามตัวอย่างที่ 2 ข้างล่างนี้


Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:00 GMT+7
วันที่ปรับล่าสุด: 12 เม.ย. 54 10:46 GMT+7
REPLY #1 (129282)


Sample02 Pattern : Interface and Factory Class
image
ตัวอย่างที่2 ในกรณีที่ว่า ระบบต้องการที่จะรองรับ DATABASE มากกว่า 1 ชนิด ในที่นี้ผมมี DAL ที่เอาไว้ใช้ทั้ง MySql และ MSSql ไว้ซัพพอร์ทลูกค้ากรณีเขาเลือกใช้อย่างใดอย่างหนึ่ง เราจึงแยกโครงสร้างโปรเจ็คไว้ประมาณหน้าตาดังรูปด้านบนภาพด้านล่างนี้เป็น DAL Class Structure ครับ
image
1. ผมกำหนดโครงสร้างของ DAL ไว้เป็น Interface ที่ชื่อว่า ICustomerServiceDAL ซึ่งมีเม็ธธอด GetCustomerName() ไว้ที่โปรเจ็ก Domain02
2. คลาสที่ใช้ติดต่อกับดาต้าเบสทั้ง MySqlDAL02.MySqlDAL และ MSSQLDAL02.MSSQLDAL จะ implement Domain02.ICustomerServiceDAL ทั้งคู่
3. โปรเจ็ก DALFactory Class จะมี GetActiveDAL method โดยมี return type เป็น ICustomerServiceDAL และภายในจะมีการสร้างอินแสตนท์ของคลาส MySqlDAL, MSSQLDAL โดยตามที่อ่านค่าได้จาก configuration file ตามโค้ดด้านล่างนี้
image
4. ในโปรเจ็ค BLL02.CustomerBLL Class จะมีการเรียกใช้ Domain02.ICustomerServiceDAL และ DALFactory02.DALFactory ภายในดังนี้
image
ต่อมาลองดู dependency diagram ของตัวอย่างที่ 2 กันครับ
image
จากรูปด้านบนจะเห็นได้ว่าคลาส BLL02.CustomerBLL นั้นไม่ได้เรียกใช้ MSSqlDALและ MySqlDAL ตรง ๆ แต่จะเรียกใช้ผ่านคลาส DALFactoty02.DALFactory โดยอิงตามเสปคของ Domain02.ICustomerServiceDAL interface ซึง DALFactory เป็นคลาสที่ช่วยแยก DAL ของ Database ชนิดต่าง ๆ ออกไปจัดการภายใน factory method  ตรงนี้จุดที่เกิด tight coupling คือ CustomerBLL และ DALFactory
และในส่วนของ  Factory Class ก็ต้องรู้จักกับคลาส SqlDAL, MySqlDAL และ OracleDAL ตรง ๆ ซึ่งจากดีไซน์นี้ความเป็นจริงคือไม่ได้ลดความ Tight Coupling ของโครงสร้างลงเลย  แต่ว่าดีไซน์นี้มีกลิ่นไอของ Dependency Injection อยู่ระดับนึง

หลังจากที่นายเอิธพยายามกับโปรแกรมสุดเดิ้ล อย่าง EarthAccounting เพื่อเพิ่มความสามารถและสร้างความสามารถที่รองรับกับความต้องการของลูกค้าในเรื่องการเลือก database ได้ถึง 2 ชนิด  กลับมาเจอกับ challenge ใหม่ในหัวข้อที่ว่า ลูกค้าต้องการลอจิกในการคำนวณค่าบางตัวด้วยสูตรทางคณิตที่ซับซ้อน ซึ่งนายเอิธและเพื่อนไม่เก่งคณิตเอาซะเลย เลยตั้งใจจะ outsource งานส่วนนี้ไปให้คนอื่นทำ แต่มาติดปัญหาว่า คลาสใน BLL นั้นต้องมานั่งรอ คลาสอีกตัวของทาง outsource กว่าจะได้จัดส่ง มาให้ใช้งานใน BLL ถ้านั่งรอก็คงกินเวลาไปอีกเป็นอาทิตย์  ไหนกว่าจะได้งานมาก็มานั่งเขียนโปรแกรม นั่งเทส นั่ง build ทำ package งานเกินเวลาส่งแน่ ๆ จึงได้เปิดกูเกิ้ลและค้นหาจนได้พบกำคำว่า Dependency Injection
จากเรื่องราวด้านบน ทำให้เห็นได้ว่าสิ่งที่นายเอิธกำลังต้องการก็คือวิธีีการออกแบบและเฟรมเวิร์กที่จะช่วยแก้ปัญหา
1. ต้องการดีไซน์ที่ช่วยให้แยก Layer และ Dependency ให้เป็นอิสระจากกัน
2. ต้องการแยกเทสในแต่ละ Layer และ Dependency ที่เกี่ยวข้อง
3. ต้องการลดการ rebuild โปรแกรม กรณีที่มีการ update  DLL เพียงแค่บางตัว
4. สามารถกำหนดโครงสร้างของระบบ โดยใช้ Class, Interface เป็น contract ในการพัฒนาระหว่าง Layer และ Dependency ทั้งหมด



Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:01 GMT+7
วันที่ปรับล่าสุด: 21 ก.พ. 54 07:01 GMT+7
REPLY #2 (129283)

Dependency Injection Pattern

ถูกคิดค้นโดยนาย Martin Flower ในหัวข้อเรื่อง Inversion of control (IoC) มีวัตถุประสงค์เพื่อช่วยแก้ปัญหาการยึดเกาะระหว่างโมดูลของระบบให้แยกขาดออกจากกัน โดยกลับเส้นของ dependency จากผู้เรียกใช้กลายเป็นผู้ถูกเรียก (Hollywood Principal)   IoC เป็น 1 ในกลุ่มของ PoEAA โดยมีแนวทางการนำไปใช้งานได้ 2 แบบคือ Service Locator และ Dependency Injection   ซึ่งจะขอกล่าวถึงเพียง DI เท่านั้น  ดังนี้ผมขออธิบายแบบไม่อิงภาษา oo หรือแบบบ้าน ๆ ว่า DI นั้นคือการสร้างและยิง object ณ ตอน runtime เข้าไปยังกลุ่มคลาสที่ได้เรียกใช้ Interface ซึ่งเป็นข้อตกลงที่ object นั้นๆได้นำไป implement โดยการยิง object นั้นมีทั้งแบบ setter และ constructor
การนำ DI Pattern ไปใช้งานนั้นมีได้หลายวิธีครับ แบบ DIY (Do it your self) หรือใช้ Framework ซึ่งในปัจจุบันนั้นมีอยู่เยอะมากกกก ตามรายชื่อที่พอจะรวบรวมมาได้ด้านล่างนี้ (แต่ยังคงมีออีกเยอะ)

Name
Performance
(Transient : Singleton)*
Current Version
Spring.Net
n/a:n/a
1.3.1
Castle Windsor
2:4
2.5.1
Ninject
2:2
2.1.0.76
StructureMap
5:4
2.6.1.0
Unity (Microsoft)
4:4
2.0
AutoFac
3:4
2.2.4.9
MEF (Microsoft)
N/A
1.0
Dynamo
5:5
1.0(beta)
* performance 1:bad,2:poor,3:normal,4:good,5:best
** มีหลายปัจจัยสำหรับการตัดสินใจเลือกใช้ framework เพราะแต่ละตัวนั้นมีความสามารถไม่เท่ากัน บางตัวสร้างมาเพื่อ DI โดยเฉพาะ บางตัวเป็น framework ที่มีการ integrate กับหลายส่วนงาน

DI Start up : Demo ASP.NET 3 Layer with Dependency Injection (Unity 2.0)

สำหรับเริ่มกับ DI ผมขอแนะนำการใช้งานเจ้า DI ตัวนึงก็คือ Unity ครับ เป็นของไมโครซอฟเองก่อนอื่นก็ไปดาวน์โหลดมาติดตั้งก่อนครับ ที่นี่ Unity 2.0 ติดตั้งเสร็จแล้วก็เริ่มใช้งานกันเลย
ลองดูโครงสร้างโปรเจ็คทั้งหมดครับ
image
และให้ add reference ตาม dependency diagram ตามภาพด้านล่างนี้
image
เราจะเห็นได้ว่า ไม่มีเส้น PL ไปเรียก BLL หรือ BLL ไปเรียก DAL ให้เห็นเลย ทุก Layer จะวิ่งไปมองที่ Domain ทั้งหมด

1. Domains03 Project

image
สำหรับโปรเจ็คนี้ ผมออกแบบไว้เพื่อกำหนดโครงสร้าง layer ทั้งหมดของ Application โดยภายในจะประกอบไปด้วย  Domain class เป็นกลุ่มคลาสที่เอาไว้ใช้งานข้ามระหว่าง layer , Service Interface เอาไว้กำหนดโครงสร้างให้ BLL , Repository Interface เอาไว้กำหนดโครงสร้างให้ DAL
    ตามรูปด้านล่างนี้
      image
    public class Customer 
    { 
       public string CustId { getset; } 
       public string FName { getset; } 
       public string LName { getset; } 
    }
public class Customer
{
    public string CustId { getset; }
    public string FName { getset; }
    public string LName { getset; }
}


  • สำหรับ Domain03.Customer class เป็นโดเมนคลาส ที่จะใช้ในการทำงานกับทุก layer
    public interface ICustomerRepository 
    { 
       Customer GetCustomerById(string custId); 
    }
    public interface ICustomerRepository
    {
        Customer GetCustomerById(string custId);
    }


ผมสร้างส่วนของ Domain03.CustomerService interface เป็นข้อกำหนดว่า Busines Logic Class ของ CustomerService ใดๆที่จะพัฒนาและนำเข้ามาใช้งานต้องนำ interface นี้ไปใช้เท่านั้น
    public interface ICustomerService 
    { 
       Customer GetCustomer(string custId); 
    }

ผมสร้างส่วนของ Domain03.CustomerService interface เป็นข้อกำหนดว่า Busines Logic Class ของ CustomerService ใดๆที่จะพัฒนาและนำเข้ามาใช้งานต้องนำ interface นี้ไปใช้เท่านั้น
    public interface ICustomerService 
    { 
       Customer GetCustomer(string custId); 
    }



    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:04 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 07:04 GMT+7
    REPLY #3 (129284)

    2. L2SDAL03 Project (DAL –> Domain)

    image
    สำหรับโปรเจ็คนี้ ผมได้นำ interface ที่ชื่อ ICustomerRepository เข้ามาใช้งานใน CustomerLinq2Sql Class โดยตามภาพไดอะแกรมด้านล่างนี้
    image
    ในส่วนของโค้ดผมได้สร้าง data สำหรับส่งค่ากลับไป ไม่ได้ติดต่อดาต้าเบสจริง ๆ ดังนี้
        public class CustomerLinq2Sql : ICustomerRepository
        {
            public Customer GetCustomerById(string custId)
            {
                if (!string.IsNullOrEmpty(custId))
                {
                    var cust = new Customer{CustId = custId};

                    if (custId.ToLower().Contains('a'))
                    {
                        cust.FName = "abcdefg";
                        cust.LName = "tryruyquwr";
                    }
                    if (custId.ToLower().Contains('ก'))
                    {
                        cust.FName = "ฟหกด่าวสาหกดวสา่";
                        cust.LName = "สวทหวดสเ่หกยเด่";
                    }
                    if (custId.ToLower().Contains('0'))
                    {
                        cust.FName = "29785740573";
                        cust.LName = "23758945897653";
                    }
                    return cust;
                }
                return null;
            }
        }




    3. BLL03 Project  (BLL –> Domain)

    image
    ในส่วนของ BLL03 Project ก็คือ Business Logic Layer ของเรานั่นเอง ผมได้นำ ICustomerService interface เข้ามาอิมพลีเม้นให้กับคลาส CustomerService ตามไดอะแกรมด้านล่างนี้
    image
    ลองมาดูส่วนของโค้ดในคลาส CustomerService กันครับ
        public class CustomerService : ICustomerService
        {
            private ICustomerRepository dal;

            public CustomerService(ICustomerRepository repository)
            {
                dal = repository;
            }
            public Customer GetCustomer(string custId)
            {
                return dal.GetCustomerById(custId);
            }
        }



    • จะเห็นได้ว่าในบรรทัดที่ 3 จะมีการประกาศตัวแปรของ ICustomerRepository ไว้ที่นี่เนื่องจากวน BLL นั้นจะเป็นผ้ที่เรียกใช้ DAL นั่นเอง
    • บรรทัดที่ 5 คือการเปิดให้ DI นั้นยิง object เข้ามาทาง Constructor นั่นเอง
    • บรรทัดที่ 12 เป็นการเรียกใช้ DAL ที่ถูกยิง object เข้ามาโดย DI โดยเรียกผ่านทาง polymorphism ของ interface



    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:05 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 07:05 GMT+7
    REPLY #4 (129289)
    4. WebUI03 Project (PL –> Domain)
    หลังจากที่เราได้วางโครงสร้างและทำการสร้างกลุ่ม BLL, DALเสร็จเรียบร้อยแล้ว  ขั้นตอนต่อไปคือการนำไปใช้งาน โดยก่อนนี้การแบ่งเลเยอร์แบบปกติ ลำดับของผู้ที่จะทำการเรียกใช้ ก็คือ PL –> BLL –> DAL แต่เราจะเปลี่ยนเส้นให้ชี้ไปที่ PL –> Domain และสำหรับ PL นั่นจะมีส่วนที่พิเศษสำหรับการนำ DI เข้ามาใช้งาน เนื่องจากเป็นความแตกต่างของเทคโนโลยี ไม่ว่าจะเป็น ASP.NET, ASP.NET MVC, SilverLight, Windows App เป็นต้น
    image

    ขั้นแรก  ตั้งค่าคอนฟิกของ Unity ใน web.config  ดังนี้
    1. <configSections>
    2.   <section name="unity"type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
    3. </configSections>



    เป็นการเพิ่มโมเดลคอนฟิกของ Unity ภายใน web.config

    1. <unity>
    2.   <typeAliases>
    3.     <!– Lifetime manager types –>
    4.     <typeAlias alias="singleton"
    5.          type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,
    6.              Microsoft.Practices.Unity" />
    7.     <typeAlias alias="perThread"
    8.          type="Microsoft.Practices.Unity.PerThreadLifetimeManager,
    9.              Microsoft.Practices.Unity" />
    10.     <typeAlias alias="external"
    11.          type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager,
    12.              Microsoft.Practices.Unity" />
    13.   </typeAliases>
    14.   <containers>
    15.     <container name="containerOne">
    16.       <types>
    17.         <!– map ICustomerService to CustomerService –>
    18.         <type type="Domains03.ICustomerService, Domains03"mapTo="BLL03.CustomerService, BLL03">
    19.           <lifetime type="singleton" />
    20.         </type>
    21.         <!– map ICustomerRepository to CustomerLinq2Sql –>
    22.         <type type="Domains03.ICustomerRepository, Domains03"mapTo="L2SDAL03.CustomerLinq2Sql, L2SDAL03">
    23.           <lifetime type="singleton" />
    24.         </type>
    25.       </types>
    26.     </container>
    27.   </containers>
    28. </unity>



    Line 2 – 13 คือการตั้งชื่อย่อให้ component กลุ่ม LifeTime
    Line 15 คือการกำหนด container name ที่จะบรรจุกุล่ม dependency เข้าไว้ในนี้
    Line 16 – 25 คือการแม๊พ Interface เข้ากับ Class ที่นำอินเทอเฟสไปใช้งาน โดยจะเป็น BLL และ DAL ของเรา
     


    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:15 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 07:15 GMT+7
    REPLY #5 (129290)

    ขั้นที่ 2 แก้ไขไฟล์ Global.asax
    เพราะว่าเว็บเวลาจะ start up ขึ้นมาส่วนที่ทำหน้าที่ก่อนคือไฟล์นี้ครับ โดยแก้ไขตามนี้
    Code Snippet
    1. public interface IContainerAccessor
    2. {
    3.     IUnityContainer Container { getset; }
    4. }
    5.  
    6. public class Global : System.Web.HttpApplication,IContainerAccessor
    7. {
    8.  
    9.     private static IUnityContainer _container;
    10.  
    11.     public IUnityContainer Container
    12.     {
    13.         get { return _container; }
    14.         set { _container = value; }
    15.     }
    16.  
    17.     private static void BuildContainer()
    18.     {
    19.         IUnityContainer container = new UnityContainer();
    20.         //unityContainer = new UnityContainer();
    21.         UnityConfigurationSection section
    22.             = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
    23.         section.Containers["containerOne"].Configure(container);
    24.         _container = container;
    25.     }
    26.  
    27.     private static void CleanUp()
    28.     {
    29.         if (_container != null)
    30.         {
    31.             _container.Dispose();
    32.         }
    33.     }
    34.  
    35.     void Application_Start(object sender, EventArgs e)
    36.     {
    37.         // Code that runs on application startup
    38.         BuildContainer();
    39.     }
    40.  
    41.     void Application_End(object sender, EventArgs e)
    42.     {
    43.         //  Code that runs on application shutdown
    44.         CleanUp();
    45.     }
    46.  
    47.     void Application_Error(object sender, EventArgs e)
    48.     {}
    49.     void Session_Start(object sender, EventArgs e)
    50.     {}
    51.     void Session_End(object sender, EventArgs e)
    52.     {}
    53. }


    line 1-4 เป็น interface ที่เอาไว้ให้ส่วนอื่นสามารถเรียกใช้ container ได้จาก Global
    line 6 อิมพลีเม้น IContainerAccessor ให้ Global
    line 9 เป็นการประกาศตัวแปรของ container
    line 11 เป็น Property จาก IContainerAccessor Interface
    line 17-25 เป็นการโหลด container จาก web.config ที่เราได้ตั้งค่าไว้เข้ามา
     


    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:16 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 07:16 GMT+7
    REPLY #6 (129292)
    ขั้นที่ 3 จัดการ set dependency ไปยังหน้า webpage (Setter Method)
    ซึ่งเราจะสร้าง HttpModule เอาไว้สำหรับสั่งให้ DI เซ็ทค่าต่าง ๆ เข้าไปใน Web UI ณ จุด ที่มีการเรียกใช้กลุ่ม อินเทอเฟส ในทุกครั้งที่มีการเรียกถึง page นั้น ๆ ดังนี้
    DIHttpModule.cs
    1. public class DIHttpModule : IHttpModule
    2.     {
    3.         private IUnityContainer container;
    4.  
    5.         private void ContextPreRequestHandlerExecute(objectsender, EventArgs e)
    6.         {
    7.             Page page = HttpContext.Current.CurrentHandler asPage;
    8.             if (page != null)
    9.             {
    10.                 page.PreInit += Page_PreInit;
    11.             }
    12.         }
    13.  
    14.         private void BuildUp(object o)
    15.         {
    16.             container.BuildUp(o.GetType(), o);
    17.         }
    18.  
    19.         private void Page_PreInit(object sender, EventArgs e)
    20.         {
    21.             Page page = sender as Page;
    22.  
    23.             BuildUp(page);
    24.  
    25.             BuildUpMaster(page.Master);
    26.  
    27.             BuildUpControls(page.Controls);
    28.         }
    29.  
    30.         private void BuildUpControls(ControlCollectioncontrols)
    31.         {
    32.             foreach (Control c in controls)
    33.             {
    34.                 if (c is UserControl)
    35.                     BuildUp(c);
    36.  
    37.                 BuildUpControls(c.Controls);
    38.             }
    39.         }
    40.  
    41.         private void BuildUpMaster(MasterPage page)
    42.         {
    43.             if (page != null)
    44.             {
    45.                 BuildUp(page);
    46.                 BuildUpMaster(page.Master);
    47.             }
    48.         }
    49.  
    50.         #region IHttpModule Members
    51.  
    52.         public void Init(HttpApplication context)
    53.         {
    54.             container = ((IContainerAccessor)context).Container;
    55.             context.PreRequestHandlerExecute += ContextPreRequestHandlerExecute;
    56.         }
    57.  
    58.         public void Dispose()
    59.         {
    60.         }
    61.  
    62.         #endregion
    63.     }


    เข้าไป register HttpModule ที่เราสร้างขึ้นใน web.config ภายในบริเวณ <system.web> ..  ดังนี้
    1.     <httpModules>
    2.       <add name="DIMod" type="WebUI03.IoC.DIHttpModule, WebUI03" />
    3.     </httpModules>




    ขั้นที่ 4  ทดสอบเรียกใช้งาน BLL ด้วย ICustomerService
    หลังจากที่ได้ตั้งค่าและสร้าง Helper ในส่วนของ DI กันเสร็จแล้ว คราวนี้เราก็มาใช้งานกันได้แล้วครับ โดยแก้ไขหน้า default.aspx ออกมาประมาณนี้
    image


    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:18 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 07:18 GMT+7
    REPLY #7 (129293)
    และเขียนโค้ดไว้ด้านหลังแบบนี้
    Code Snippet
    1. using System;
    2. using Domains03;
    3. using Microsoft.Practices.Unity;
    4.  
    5. namespace WebUI03
    6. {
    7.     public partial class _Default : System.Web.UI.Page
    8.     {
    9.         [Dependency]
    10.         public ICustomerService custSvc { getset; }
    11.  
    12.         protected void Button1_Click(object sender, EventArgse)
    13.         {
    14.             var cust = custSvc.GetCustomer(txtCustId.Text);
    15.             if (cust == null)
    16.             {
    17.                 lbcustid.BackColor = System.Drawing.Color.Red;
    18.                 return;
    19.             }
    20.             lbcustid.Text = cust.CustId;
    21.             lbfname.Text = cust.FName ?? "not found";
    22.             lblname.Text = cust.LName ?? "not found";
    23.         }
    24.     }
    25. }



    Line 9-10  เป็นการประกาศตัวแปรสำหรับรับ object ของ BLL เข้ามาโดยกำหนด attribute [Dependency] ไว้บนหัว property ICustomerService custSvc   เมื่อมีการเรียก page นี้ให้ทำงาน  เจ้า DIHttpModule ที่เราเขียนไว้จะมาตรวจจับ Dependency attribute และ set BLL object (BLL03.CustomerBLL) เข้ามาที่ property นี้
    Line 14  เป็นการเรียกใช้ CustomerService เพื่อดึงข้อมูลผ่าน DAL อีกทีนึง
    ผลลัพธ์ที่ได้
    image


    Conclusion

    หลังจากที่ได้ทดลองทำลองอ่านตามที่ผมนำเสนอไป หวังทุกท่านคงพอมองเห็นลู่ทางในการนำไปใช้งานกันต่อ เนื่องจากเจ้า DI นี้ช่วยเพิ่มความยืดหยุ่นให้ระบบของเราได้มากจริง ๆ อาจจะมีข้อแลกเปลี่ยนบ้างเช่น ระบบในตอน startup จะช้ากว่าปกติบ้าง และอาจจะต้องระวังเรื่องหน่วยความจำด้วย
    สำหรับตอนนี้ผมเขียนไปยาวมาก จริงแล้วยังไม่จบ ยังไม่ได้พูดถึงเรื่องการทำ Testing ในแต่ละ layer และการ integrate กับ ASP.NET MVC อีกทั้งยังมี DI Framework ที่น่าสนใจจะมาแนะนำอีกหลายยี่ห้อ  ค่อนข้างยาวครับ แต่ผมขอจบตอนไว้แค่นี้ก่อน ไว้จะมาเขียนต่อในเรื่องที่ได้บอกไปข้างต้น  สวัสดีครับ







    email : nine_biz-talk.net at hotmail dot com
     


    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:19 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 07:19 GMT+7
    REPLY #8 (129294)
    mail
    mail
    mail
       กว่าจะโพสหมด โอยยย
      เหนื่อยขนาดดดดด 


    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 07:20 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 07:20 GMT+7
    REPLY #9 (129296)
     ขอบคุณค้าบบบบ ^^


    mr.lวันที่ส่ง: 21 ก.พ. 54 08:48 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 08:48 GMT+7
    REPLY #10 (129297)
    mail
    mail
    mail
     กว่าจะอ่านหมด โอยยยยย
     เหนื่อยขนาด

    angel
    angel
    angel
     เหอ เหอ เหอ


    fireflyวันที่ส่ง: 21 ก.พ. 54 09:16 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 09:16 GMT+7
    REPLY #11 (129300)
    บทความโดยมีเรื่องราวประกอบ ช่วยให้เข้าใจ และน่าติดตาม
    ขอบคุณ ที่มีบทความดีดีแบบนี้ สำหรับนักพัฒนาในบ้านเรา


    nanoวันที่ส่ง: 21 ก.พ. 54 09:24 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 09:24 GMT+7
    REPLY #12 (129301)
    ขอบคุณคร๊าบบพี่นาย เด๋วว่างๆจะมาอ่านครับ ทำไมผมใช้ IE เปิดไม่ได้หว่า นี่ต้องใช้ FF เปิด


    tapeza555วันที่ส่ง: 21 ก.พ. 54 09:25 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 09:25 GMT+7
    REPLY #13 (129302)
    สุดๆครับ
    ดีมากเลยครับ อธิบายได้เป็นขั้นๆ ตัวอย่างชัดเจน คนที่กำลังศึกษา หรือประสบกับงานจริงๆจะเกิดประโยชน์มากๆเลยครับ


    eakachaiiiวันที่ส่ง: 21 ก.พ. 54 09:48 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 09:48 GMT+7
    REPLY #14 (129305)
    @Mr.L  ไหน css ที่ขอ ด่วน ๆ
    @Firelfy  ครับ น่าจะโดนน่ะเรื่องนี้
    @Nano  ขอบคุณครับ เรื่องนี้กินแรงสุดๆแล้ว
    @Tapeza555   ตอนต่อไปคงเน้น real world app แบบละเอียดจริง ๆ  ตอนนี้ขอแบบร่าง ๆ ไปก่อน
    @ekchaiii  ยินดีด้วยครับ ที่โดน  จะพยายามเข็ญตอนต่อไปออกมาไว ๆ


    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 11:47 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 11:47 GMT+7
    REPLY #15 (129312)
    ดีมากเลยครับ ได้ความรู้เพิ่มเติม ต้องกลับไปแก้โค๊ดเรามั่งและ *-* ก่อนอื่นขอเทสก่อน 
    angel


    figgaroวันที่ส่ง: 21 ก.พ. 54 14:02 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 14:02 GMT+7
    REPLY #16 (129329)

    @figgaro  ดีใจด้วยครับผม 

    ปล.  ถึงเพื่อนรักนักอ่าน ทุกท่าน รบกวน  feedback และ comment ให้ผมด้วยนะครับ  จะได้เอาไปปรับปรุงเนื้อหา ในบทความต่อไปด้วย
    ติได้ครับ เพราะผมจะได้ทราบว่าผิดตรงไหนจะได้แก้ไขคร้าบบ


    สำหรับ background ที่ลายตานั้น  เนื่องจากผม copy มาจาก http://Nine69.WordPress.com
    ดังนั้นหากต้องการจะอ่านอย่างสบายตามี 2 ทางคือ 
    1.  ใช้ Google Chrome ในการอ่าน
    2. หรือไปอ่านที่ http://Nine69.WordPress.com


    Nine (นาย)วันที่ส่ง: 21 ก.พ. 54 19:27 GMT+7
    วันที่ปรับล่าสุด: 21 ก.พ. 54 19:27 GMT+7
    REPLY #17 (129335)
    ขอมาช่วยกันแชร์ครับ :)
    ถ้าเราต้องการเปลี่ยน Class Implement ได้ เราก็ต้อง new Class จากข้างนอกแล้วยัดใส่แบบนี้ OhMyGod!!
    ICustomerService customerService = new CustomerService (
                                          new CustomerLinq2Sql( )
                                        , new Logging(), newASPNetCaching());
    ดังนั้นตัว Container( Spring , Unity ,Castle Windsor...) ทำให้ Code เราง่ายขึ้น ถ้ามีการเปลี่ยนก็แค่ไป Map ใหม่
    ICustomerService customerService = IoCFactory.Resolve<ICustomerService >();

    นอกเรื่อง
      ตอนนี้ผมกำลังทำ(เล่น) CassetteDev.Core เป็นเหมือนกับ Infrasructure ของ Application ให้ Layer อื่นๆเรียกใช้
    แต่ก็ไม่รู้จะใช้ IoC Container ตัวไหนดีเยอะเกิ้น ไม่อยากให้มันผูกกับอันใดอันหนึ่ง ผมเลยเลือกใช้ Service Locator ช่วยให้เปลี่ยน
    Container ได้ตามต้องการ ซึ่ง Microsoft.Practices.ServiceLocation เป็นทางเลือกที่ดีมากๆ
     CassetteDev.Core
      + DependencyInjection
        - BootStrapperManager.cs
        - CommonBootStrapper.cs
        - IoCFactory.cs

    ผมลองเอามาทำต่อจากโปรเจ็กพี่นาย ได้ประมาณนี้ครับ
    ทางฝั่ง UI (WebUI03 )
      + BootStrapper
        - WindsorServiceLocator.cs (Implement ServiceLocatorImplBase เขียนเอง ส่วน Unity มีอยู่แล้วแจ๋วจริงๆ)
        - UnityBootStrapper.cs (Implement CommonBootStrapper )
        - WindsorBootStrapper.cs (Implement CommonBootStrapper เขียนเอง)
     
    //UnityBootStrapper.cs
     public class UnityBootStrapperCommonBootStrapper {
        protected override IServiceLocator CreateServiceLocator(){
          IUnityContainer container = new UnityContainer();
          RegisterTypes(container);
          return new UnityServiceLocator(container);
        }
        private void RegisterTypes(IUnityContainer container) {
          container.RegisterType<ICustomerRepositoryCustomerLinq2Sql>();
          container.RegisterType<ICustomerServiceCustomerService>();
        }
    }

    WindsorBootStrapper.cs
    public class WindsorBootStrapper : CommonBootStrapper{
        protected override IServiceLocator CreateServiceLocator() {
             WindsorContainer container = new WindsorContainer();
             RegisterTypes(container);
             return new WindsorServiceLocator(container);
        }
        private void RegisterTypes(IWindsorContainer container) {
           container.Register(Component.For<ICustomerRepository>().ImplementedBy<CustomerLinq2Sql>());
           container.Register(Component.For<ICustomerService>().ImplementedBy<CustomerService>()); 
       }
    }
    ที่ Global.cs
    void Application_Start(object sender, EventArgs e)
    {
      BootStrapperManager.Initialize(new WindsorBootStrapper());
    //BootStrapperManager.Initialize(new UnityBootStrapper());
    }





    tapeza555วันที่ส่ง: 22 ก.พ. 54 02:44 GMT+7
    วันที่ปรับล่าสุด: 22 ก.พ. 54 02:44 GMT+7
    REPLY #18 (129354)
    @Tapeza55  ขอบใจมากน้องเทป  IDependencyResolver core ของ asp.net mvc 3 ครับ คล้าย ๆกับ Common Service Locator ที่น้องเทปยกมา
     พี่เตรียมพูดใน asp.net mvc 3.0 วันเสาร์นี้ด้วย สนใจก็แวะมาได้เน้อ


    Nine (นาย)วันที่ส่ง: 22 ก.พ. 54 11:59 GMT+7
    วันที่ปรับล่าสุด: 22 ก.พ. 54 11:59 GMT+7
    REPLY #19 (129369)
    ใช่ครับพี่นาย IDependencyResolver  กับ  IServiceLocator  น่าจะเหมือนๆ กัน
      public interface IServiceLocator : IServiceProvider
        {
            IEnumerable<TService> GetAllInstances<TService>();
            IEnumerable<object> GetAllInstances(Type serviceType);
            TService GetInstance<TService>();
            TService GetInstance<TService>(string key);
            object GetInstance(Type serviceType);
            object GetInstance(Type serviceType, string key);
        }
    ปล. ไปครับไป ไว้เจอกันครับ


    tapeza555วันที่ส่ง: 22 ก.พ. 54 15:05 GMT+7
    วันที่ปรับล่าสุด: 22 ก.พ. 54 15:05 GMT+7
    REPLY #20 (129385)
    ขอบคุณครับปูเสื่อรอตอนต่อไป


    yokeyokeวันที่ส่ง: 22 ก.พ. 54 20:28 GMT+7
    วันที่ปรับล่าสุด: 22 ก.พ. 54 20:28 GMT+7
    REPLY #21 (129416)
    @yokeyoke  แถมเตียงลูกฟูกให้เลยครับ  คงราว ๆ กลางเดือนมีนาเลยหละ 


    Nine (นาย)วันที่ส่ง: 23 ก.พ. 54 10:33 GMT+7
    วันที่ปรับล่าสุด: 23 ก.พ. 54 10:33 GMT+7
    REPLY #22 (129421)
    ทำตามแล้วมัน error ที่คำสั่ง  
    var cust = custSvc.GetCustomer(txtCustId.Text); //อยู่ที่หน้า Default.aspx.cs
    แจ้ง error ว่า {"Object reference not set to an instance of an object."} watch ดู value มีค่าเป็น null ส่วนที่โปรเจคพี่นายผม watch ดู แล้วมีค่าเป็น BLL03.CustomerService


    yokeyokeวันที่ส่ง: 23 ก.พ. 54 11:50 GMT+7
    วันที่ปรับล่าสุด: 23 ก.พ. 54 11:50 GMT+7
    REPLY #23 (129422)
    ตรวจสอบตามนี้น่ะ 
    1. web.config  ได้ map ค่า library ใน unity container หรือยัง
    2. มี dll จาก BLL, DAL ไปวางไว้ใน bin folder ของโปรเจ็ค web ui หรือยัง
    3. ที่ global.asax.cs มีคอนฟิก code ตามที่บอกรึยัง
    4. ICustomerService มี [Dependency]  attribute กำหนดวางไว้หรือยัง


    Nine (นาย)วันที่ส่ง: 23 ก.พ. 54 11:59 GMT+7
    วันที่ปรับล่าสุด: 23 ก.พ. 54 11:59 GMT+7
    REPLY #24 (129423)
    ปรับแล้วนะครับ ถ้าที่นายมีเวลาช่วยดูให้หน่อยละกันนะครับ แต่ถ้าไม่ว่างก็ไม่เป็นไรครับผมจะสร้างโปรเจคใหม่แล้วทำใหม่ดูเผื่อว่าผิดขั้นตอนไหน http://www.panitthon.ac.th:83/newtcc/files/DITestProject.zip อันนี้ผมเอาโปรเจคไว้ให้โหลด


    yokeyokeวันที่ส่ง: 23 ก.พ. 54 12:08 GMT+7
    วันที่ปรับล่าสุด: 23 ก.พ. 54 12:08 GMT+7
    REPLY #25 (129435)
    ขอบคุณครับ คุณ Nine พึ่งได้อ่านวันนี้เอง กำลังจะติเรื่อง background พอดี
    ถ้าใครไม่อยากไปเปิดที่อื่น ทำแบบนี้ซิ เอาเมาส์ลากเพื่อป้ายเป็นสีก็อ่านได้สะดวกขึ้นแล้ว
    มาเก็บข้อมูลครับ และปูเสื่อรออ่านต่อด้วย

     


    pnengวันที่ส่ง: 23 ก.พ. 54 21:22 GMT+7
    วันที่ปรับล่าสุด: 23 ก.พ. 54 21:22 GMT+7
    REPLY #26 (129482)
    สำหรับปัญหาของคุณ yokeyoke ตามตัวอย่างคุณยังขาดในส่วนของ DIHttpModule และการ Register DIHttpModule ที่ web.config ครับ
    ปัญหาเกิดจากการที่ class _Default ไม่ได้ถูกตัว UnityContainer ทำ BuildUp Object ครับ


    Kornnวันที่ส่ง: 26 ก.พ. 54 09:45 GMT+7
    วันที่ปรับล่าสุด: 26 ก.พ. 54 09:45 GMT+7
    REPLY #27 (129488)
    นิดนึงครับพี่นาย ผมอยากรู้ว่าถ้า หลังจากนายเอิธเขียนโปรแกรมตามแบบ DI แพทเทิลแล้วด้วย Unity2.0 นายเอิธกลับโดนให้แก้ส่วนของ ฺBusinessLogic และได้เพิ่มตารางมาอีกหนึ่งตาราง  นายเอิธ จะทำยังไงครับ แล้วอยากให้พี่นายแสดงให้ดูว่า การออกแบบตามแบบ DI แพทเทิล ด้วย Unity2.0 ทำให้นายเอิธ แก้ไขโปรแกรมได้โดยไม่ส่งผลกระทบกับการทำงานในส่วนอื่นยังไงน่ะครับ อยากรู้ ครับ จัดให้หน่อยนะครับ


    yokeyokeวันที่ส่ง: 26 ก.พ. 54 19:40 GMT+7
    วันที่ปรับล่าสุด: 26 ก.พ. 54 19:40 GMT+7
    REPLY #28 (129516)
    ทำไมใน dependency diagram WebUI มองที่ Domain แต่ในโปรเจคที่โหลดไปถึงมีการ Add Referenct DAL, BLL  ล่ะครับ


    yokeyokeวันที่ส่ง: 27 ก.พ. 54 22:17 GMT+7
    วันที่ปรับล่าสุด: 27 ก.พ. 54 22:17 GMT+7
    REPLY #29 (129539)
    #27
    เป็นตอนต่อไปครับเมื่อได้ใช้งานร่วมกับ asp.net mvc 3 จะมีตัวอย่างรอหน่อยเด้อค้าบ

    #28
    จริงๆแล้วไม่ต้องแอด referenceครับ เอาออกได้เลย   แต่ที่แอดไปเพราะต้องการให้ dll ทั้งสอง project ไปวางใน bin ของ WebUI เท่านั้้นเอง


    Nine (นาย)วันที่ส่ง: 28 ก.พ. 54 14:46 GMT+7
    วันที่ปรับล่าสุด: 28 ก.พ. 54 14:46 GMT+7
    REPLY #30 (129696)
    อยากทราบว่าการออกแบบด้วย Dependency Injection นี้ใน Domain Class สามารถมี Method ที่ใช้ในการทำงานเป็นของตัวเองได้ไหมครับ หรือว่าพวก Method ต่างๆ ต้องอยู่ใน BLL (คลาสที่เป็น Service) เท่านั้นครับ ขอบคุณครับ


    hongวันที่ส่ง: 7 มี.ค. 54 12:40 GMT+7
    วันที่ปรับล่าสุด: 7 มี.ค. 54 12:40 GMT+7
    REPLY #31 (129710)
    ใช้ behavior inject เข้าไปใน domain entity อีกทีครับง่ายๆ ด้วยการกำหนด interface behavior ให้กับ domain ที่ต้องการ ไว้จะมาอธิบายอีกที เครื่องจะออกแล้ว หุหุ


    Nine (นาย)วันที่ส่ง: 7 มี.ค. 54 21:33 GMT+7
    วันที่ปรับล่าสุด: 7 มี.ค. 54 21:33 GMT+7
    REPLY #32 (130084)
    DataClass ที่อยู่ใน DAL มันต้อง imprement จาก Domain แต่ถ้าใช้ Ado.net Entity Framework มันจะสร้าง class มาให้เองต้องไปปรับอะไรตรงไหนให้มัน Imprement กันหรอครับ


    yokeyokeวันที่ส่ง: 24 มี.ค. 54 17:58 GMT+7
    วันที่ปรับล่าสุด: 24 มี.ค. 54 17:58 GMT+7
    REPLY #33 (130285)
    อยากทราบว่าถ้าใช้ ADO.NET Entity Framework (EF) ตัว EF ควรจะอยู่ใน Data Layer หรือ Domain ครับ เนื่องจาก EF มีทั้ง Entity Class และส่วนการจัดการข้อมูลในตัวเดียวกัน ขอบคุณครับ


    hongวันที่ส่ง: 2 เม.ย. 54 09:45 GMT+7
    วันที่ปรับล่าสุด: 2 เม.ย. 54 09:45 GMT+7
    REPLY #34 (130302)
    ให้กำหนดที่ Domain ครับ
    แล้วค่อยนำไปใช้ใน DAL วิธีนี้เราจำเป็นต้องใช้ Code First Feature ของ EF ver4 ขึ้นไป  เพื่อที่จะนำ domain ไป implement เป็น table ครับ
    แต่หากเป็น NHibernate เขาทำได้นานแล้ว


    Nine (นาย)วันที่ส่ง: 3 เม.ย. 54 03:40 GMT+7
    วันที่ปรับล่าสุด: 3 เม.ย. 54 03:40 GMT+7
    REPLY #35 (130312)
    คุณนายครับ ถ้าจะใช้ POCO Entity แทน Code First ใน EF4 ที่ยังไม่มี Code First Feature เราสามารถนำ POCO Entity ไปไว้ที่ Domain และ ObjectContext อยู่ที่ Data Layer ได้หรือไม่ครับ ถ้าได้จะมีผลกระทบกับการใช้ Code First Feature ในอนาคตหรือไม่ครับ ขอบคุณครับ


    hongวันที่ส่ง: 4 เม.ย. 54 09:53 GMT+7
    วันที่ปรับล่าสุด: 4 เม.ย. 54 09:53 GMT+7
    REPLY #36 (130393)
    #35 สามารถวาง POCO Entity ไว้ที่ Domain และ ObjectContext ไว้ที่ DAL ได้ เมื่อต้องการใช้ Code First ก็สามารถปรับเปลี่ยน DAL ตัวใหม่ได้เลย


    dinobenzวันที่ส่ง: 7 เม.ย. 54 13:13 GMT+7
    วันที่ปรับล่าสุด: 7 เม.ย. 54 13:13 GMT+7
    REPLY #37 (130398)
    คุณสามารถใช้ t4  ตัวที่ชื่อ ADO.NET Self-Tracking Entity Generator ทำการ gen poco  ออกมาครับ
    เสร็จแล้ว ไปที ่project  ให้ right click ที่   *__Contexts.Types.tt   เลือก run custom tool 
    จากนั้นก็ move *__.Types.tt  + (*.cs ทุกตัวที่อยู่ใน + ) ไปยัง domain project





    Nine (นาย)วันที่ส่ง: 7 เม.ย. 54 16:57 GMT+7
    วันที่ปรับล่าสุด: 7 เม.ย. 54 16:57 GMT+7
    REPLY #38 (130407)
    ผมไม่ชอบวิธีการสร้าง poco ของ EF4 ครับ
    เนื่องจากต้องใช้ template ไป extract เอา model ออกมาจาก EDM ทำให้ขั้นตอนวุ่นวาย และอาจจะพลาดเอาได้ง่าย ๆ กรณีลืมเอา customtool ออกจาก .tt file
    แต่สำหรับ EF 4.1 code first ทำได้ดีครับ ดูเรียบง่ายมาก  แต่ก็ยังติดในเรื่องของความสามารถในหลายเรื่อง  คาดว่าจะปรับปรุงใน EF5  คงไม่เกิน สิ้นปีนี้คงได้ยลกัน


    Nine (นาย)วันที่ส่ง: 8 เม.ย. 54 13:35 GMT+7
    วันที่ปรับล่าสุด: 8 เม.ย. 54 13:35 GMT+7

    ไม่มีความคิดเห็น:

    แสดงความคิดเห็น