วิธีรักษาข้อมูลสถานะ COM+ ใน Delphi
หลิว เสี่ยวหมิง (ซิเฟอร์หลิว)
ปัญหาเริ่มต้นดังนี้: ฉันต้องเขียน COM+ เพื่อเชื่อมต่อกับฐานข้อมูลต่างๆ เพื่อนบางคนอาจบอกว่าควรสร้าง COM+ สำหรับแต่ละฐานข้อมูล แต่ไม่สามารถทำได้ในระบบของฉัน เรากำลังสร้างระบบช่วยเหลือด้านการศึกษาและผู้ใช้คือโรงเรียน (รวมถึงครู นักเรียน และผู้ปกครองในโรงเรียนด้วย) เราสร้างฐานข้อมูลสำหรับแต่ละโรงเรียนเหมือนกัน แน่นอนว่าเรายังมีฐานข้อมูลการจัดการเพื่อประสานความสัมพันธ์ระหว่างฐานข้อมูลด้วย ทุกครั้งที่มีการเพิ่มผู้ใช้ในโรงเรียน เราจะเปิดใช้งานฐานข้อมูลใหม่เพื่อให้ลูกค้าใช้งาน กล่าวคือ จำนวนฐานข้อมูลของเราเพิ่มขึ้นอย่างต่อเนื่อง และเราจะมีลูกค้าเพียงรายเดียวเท่านั้น เราจะไม่พัฒนาฐานข้อมูลที่แตกต่างกันสำหรับแต่ละโรงเรียน ฝั่งไคลเอ็นต์ เรามี COM+ เพียงชุดเดียว แทนที่จะพัฒนาชุดเดียวสำหรับแต่ละฐานข้อมูล ดังนั้นฉันจึงต้องปล่อยให้มันเชื่อมต่อกับฐานข้อมูลที่แตกต่างกันตามข้อมูลประจำตัวของผู้ใช้ใน COM+
แน่นอนว่า COM+ นี้ควรมีวิธีการอนุญาตให้ผู้เรียก (ซึ่งอาจเป็นแอปพลิเคชันไคลเอนต์หรือมิดเดิลแวร์อื่นๆ) สามารถเลือกฐานข้อมูลที่จะเชื่อมต่อได้ ในทางปฏิบัติ เราจะสอบถามฐานข้อมูลในไลบรารีการจัดการตาม ID ของผู้ใช้ ชื่อฐานข้อมูลแล้วเชื่อมต่อกับฐานข้อมูลผู้ใช้ ที่นี่ เพื่อลดความซับซ้อนของปัญหา เราคิดว่าผู้โทรทราบชื่อของฐานข้อมูลอยู่แล้ว และร้องขอให้เรียกฐานข้อมูลนี้โดยตรง
เพิ่มสมาชิกส่วนตัว DBName:string ลงในคลาส COM+ เพื่อบันทึกชื่อของฐานข้อมูลที่จะเชื่อมต่อ เราควรจัดเตรียมวิธีการดังกล่าวเพื่อตั้งค่าของมันด้วย ตอนแรกผมเขียนแบบนี้
กระบวนการ TmtsDBConn.ConnectTo (sDBName:string);
เริ่ม
พยายาม
DBName:=sDBName;
ตั้งค่าให้เสร็จสมบูรณ์;
ยกเว้น
ตั้งค่ายกเลิก;
จบ;
จบ;
จากนั้นใส่ตัวควบคุม ADOConnection, ADODataSet และ DataSetProvider ไว้ในนั้น โดยตั้งชื่อว่า adoc, adods และ dsp ตามลำดับ ตั้งค่าความสัมพันธ์การเชื่อมต่อระหว่างกัน ตั้งค่าสตริงการเชื่อมต่อของ adoc เป็นฐานข้อมูลการเชื่อมต่อ "DB1" ซึ่งเป็นค่าเริ่มต้น จากนั้นในเหตุการณ์ BeforeConnect ของ adoc:
adoc.ConnectionString:=ConnectStringA+'Initial Catalog='+DBName+';'+ConnectStringC;
ConnectStringA และ ConnectStringC ที่นี่คือค่าคงที่สตริงที่ตั้งค่าไว้ล่วงหน้าเพื่อสร้างสตริงการเชื่อมต่อแบบไดนามิก ดังนี้:
ค่าคงที่
ConnectStringA='Provider=SQLOLEDB.1;PassWord=2003;ข้อมูลความปลอดภัยคงอยู่=True;ID ผู้ใช้=sa;';
ConnectStringB='แค็ตตาล็อกเริ่มต้น=DB1;';
ConnectStringC='แหล่งข้อมูล=server3;ใช้ขั้นตอนสำหรับการจัดเตรียม=1;แปลอัตโนมัติ=True;ขนาดแพ็คเก็ต=4096;รหัสเวิร์กสเตชัน=LXM;ใช้การเข้ารหัสสำหรับข้อมูล=False;แท็กด้วยการจัดเรียงคอลัมน์เมื่อเป็นไปได้=False';
คอมไพล์และติดตั้ง COM+ นี้ จากนั้นเขียนโปรแกรมไคลเอนต์เพื่อเรียกมัน
ใส่ DCOMConnection ในโปรแกรมไคลเอ็นต์ เชื่อมต่อและเขียนเซิร์ฟเวอร์ COM+ ใส่ ClientDataSet ตั้งค่าคุณสมบัติ RemoteServer และ Provider จากนั้นเขียนคำสั่ง SQL ใน CommandText จากนั้น ใส่ตัวควบคุมแหล่งข้อมูลและตัวควบคุม DBGrid เพื่อสร้างการเชื่อมต่อระหว่างกัน ในที่สุดก็ใส่ปุ่มและในเหตุการณ์ Click:
Dcomconnection1.Connected:=true;
Dcomconnection1.AppServer.connect('DB2');
ClientDataset1.Active:=true;
Dcomconnection1.Connected:=false;
รหัสนี้มีไว้เพื่อทดสอบว่าข้อมูลในฐานข้อมูล DB2 สามารถเข้าถึงได้หรือไม่ แต่ผลที่ได้คือเมื่อคลิกปุ่มจะมีการรายงานข้อผิดพลาดอยู่เสมอ สาเหตุคืออะไร
กลับไปที่โปรเจ็กต์ COM+ แก้ไขจุดบกพร่อง ตั้งค่าเบรกพอยต์ใน ConnectTo และ adocBeforeConnect และพบว่าโปรแกรมดำเนินการ
DBName:=sDBName;
เมื่อดำเนินการค่าของ DBName จะถูกตั้งค่าเป็น "DB2" อย่างแน่นอน แต่เมื่อดำเนินการ
adoc.ConnectionString:=ConnectStringA+'Initial Catalog='+DBName+';'+ConnectStringC;
DBName กลายเป็นสตริงว่างอีกครั้ง ดังนั้นจึงเกิดข้อผิดพลาด
เหตุใดค่าของ DBName จึงหายไป ปรากฎว่ามีการเรียกเมธอด SetComplete ใน ConnectTo เมธอด SetComplete คิดว่า COM+ ได้เสร็จสิ้นงานแล้ว และจะปล่อยออบเจ็กต์ COM+ ดังนั้น เมื่อเชื่อมต่อกับฐานข้อมูล COM+ ใหม่จะถูกสร้างขึ้น และ DBName ของมันก็แน่นอน โมฆะ. .
ฉันพบสาเหตุ เปลี่ยน SetComplete เป็น EnableCommit แล้วรันไคลเอ็นต์ ในที่สุดมันก็รันได้สำเร็จและดึงข้อมูลในฐานข้อมูล DB2
อย่างไรก็ตาม ในโปรแกรมไคลเอ็นต์ ฉันใส่ ClientDataSet อื่น หลังจากเปิด ClientDataSet1 ฉันเปิด ClientDataSet2 และต้องการเข้าถึงข้อมูลใน DB2 ต่อไป แต่มีการรายงานข้อผิดพลาดอื่น เปลี่ยนโปรแกรมเป็น
Dcomconnection1.AppServer.connect('DB2');
ClientDataset1.Active:=true;
ClientDataset1.Active:=false;
ClientDataset1.Active:=true;
แม้ว่าจะใช้ ClientDataSet เดียวเท่านั้น ข้อผิดพลาดจะยังคงเกิดขึ้นเมื่อเปิดอีกครั้งหลังจากปิดแล้ว
แต่ถ้าลูกค้าเขียน
Dcomconnection1.AppServer.connect('DB2');
ClientDataset1.Active:=true;
Dcomconnection1.AppServer.connect('DB2');
ClientDataset2.Active:=true;
สามารถดำเนินการได้สำเร็จ แต่นี่ดูน่าเกลียดมาก เหตุใด COM+ จึงเผยแพร่ตัวเองหลังจากเชื่อมต่อกับฐานข้อมูลแล้ว
ปรากฎว่า TmtsDataModule มีแอตทริบิวต์การทำให้สมบูรณ์อัตโนมัติ และค่าเริ่มต้นเป็นจริง ดังนั้นหลังจากเชื่อมต่อกับฐานข้อมูลแล้ว จะยังคงเผยแพร่ตัวเอง
หลังจากตั้งค่าการทำให้สมบูรณ์อัตโนมัติเป็นเท็จ ข้อผิดพลาดยังคงเกิดขึ้น การติดตามในเหตุการณ์ OnActivate ของ COM+ พบว่าเมื่อมีการเปิดใช้งาน คุณลักษณะการทำให้สมบูรณ์อัตโนมัติถูกตั้งค่าเป็นจริงโดยอัตโนมัติ ดังนั้นหลังจากที่เชื่อมต่อกับฐานข้อมูลเป็นครั้งแรก จะยังคงเผยแพร่ ตัวมันเอง
ในเหตุการณ์ OnOnActivate ของ COM+ ให้เขียน:
การทำให้สมบูรณ์อัตโนมัติ:=false;
ไม่มีปัญหาหากไคลเอนต์เชื่อมต่อเพียงครั้งเดียวและเข้าถึงฐานข้อมูลหลายครั้ง
แต่ในกรณีนี้ COM+ จะไม่ถูกนำออกใช้โดยอัตโนมัติ คุณต้องเพิ่มวิธีการ COM+, SetComplete ในวิธีนี้ แล้วเรียกวิธีนี้เพื่อปล่อย COM+ หลังจากที่ไคลเอนต์เสร็จสิ้นด้วย COM+
หลังจากการสำรวจข้างต้น เราได้ข้อสรุปดังนี้: ใน COM+ หากคุณต้องการรักษาข้อมูลสถานะ คุณต้องดำเนินการบางอย่าง เนื่องจาก COM+ เป็นแบบไร้สถานะโดยค่าเริ่มต้น ทุกครั้งที่ไคลเอ็นต์เรียกใช้งาน ระบบจะตัดสินว่าหรือไม่ มันควรจะปล่อยตัวมันเอง ถ้าเราไม่ต้องการให้มันถูกปล่อย เราก็ต้องเข้าไปแทรกแซงด้วยตนเอง และสุดท้าย เราก็จะต้องปล่อยมันด้วยตนเอง