1c 8 ธุรกรรมจากแหล่งภายนอก ลูกค้าขาจร. การแบ่งส่วนเพื่อรับการซื้อซ้ำ ต้นทุนสินค้าในการสั่งซื้อ

โดยไม่คำนึงถึงตัวเลือกการทำงานที่เลือก (ไฟล์หรือไคลเอนต์ - เซิร์ฟเวอร์) ระบบ 1C: Enterprise รับประกันการทำงานกับข้อมูลที่เก็บไว้ในฐานข้อมูลโดยใช้กลไกการทำธุรกรรม

ธุรกรรม- นี่คือลำดับการดำเนินการจัดการข้อมูลที่แบ่งแยกไม่ได้จากมุมมองของผลกระทบต่อฐานข้อมูล มันทำงานบนพื้นฐานทั้งหมดหรือไม่มีเลยและย้ายฐานข้อมูลจากสถานะองค์รวมหนึ่งไปยังสถานะองค์รวมอื่น หากการกระทำธุรกรรมอย่างใดอย่างหนึ่งไม่สามารถดำเนินการได้ด้วยเหตุผลบางประการหรือเกิดการหยุดชะงักของระบบบางประเภท ฐานข้อมูลจะกลับสู่สถานะก่อนที่ธุรกรรมจะเริ่มต้น (ธุรกรรมจะถูกย้อนกลับ)

ระบบ 1C: Enterprise เรียกธุรกรรมโดยปริยายเมื่อดำเนินการใด ๆ ที่เกี่ยวข้องกับการแก้ไขข้อมูลที่เก็บไว้ในฐานข้อมูล ตัวอย่างเช่น ตัวจัดการเหตุการณ์ทั้งหมดที่อยู่ในโมดูลออบเจ็กต์และชุดระเบียนที่เกี่ยวข้องกับการแก้ไขข้อมูลฐานข้อมูลจะถูกเรียกในธุรกรรม ธุรกรรมยังอ่านออบเจ็กต์ประเภทต่อไปนี้: วัตถุ Planobena, เอกสาร - เรื่องเครื่องบิน, หนังสืออ้างอิง, Planwid - การคำนวณลักษณะและคำแปล, แผน - ตัวนับ - หัวเรื่อง, ขั้นตอนของคณะกรรมการ, วัตถุ, เครื่องคั้น, การลงทะเบียนของการลงทะเบียน, การลงทะเบียน -Knock -Racing, Registachgontinarinapers, การลงทะเบียนของการลงทะเบียน, การรายงานซ้ำ , คำนวณใหม่ได้ บันทึก. ในกรณีนี้ ในโหมดล็อคที่ได้รับการจัดการ ล็อคที่ใช้ร่วมกันจะถูกติดตั้งโดยค่าของการลงทะเบียนสำหรับชุดของบันทึก และโดยค่าการเลือกสำหรับชุดของบันทึกของการลงทะเบียนข้อมูลอิสระ

นอกจากนี้ นักพัฒนายังสามารถทำงานกับธุรกรรมได้อย่างชัดเจน เมื่อต้องการทำเช่นนี้ ใช้ขั้นตอนบริบทส่วนกลาง StartTransaction(), CommitTransaction() และ CancelTransaction()

การใช้การเรียกธุรกรรมที่ชัดเจน

วิธี เริ่มธุรกรรม()ช่วยให้คุณสามารถเปิดธุรกรรมได้ การเปลี่ยนแปลงข้อมูลฐานข้อมูลทั้งหมดที่เกิดจากคำสั่งที่ตามมาสามารถยอมรับหรือปฏิเสธโดยสิ้นเชิงได้ หากต้องการยอมรับการเปลี่ยนแปลงทั้งหมด ให้ใช้เมธอด กระทำธุรกรรม(). หากต้องการยกเลิกการเปลี่ยนแปลงทั้งหมดที่ทำในธุรกรรมที่เปิดอยู่ ให้ใช้วิธีการ ยกเลิกธุรกรรม(). หากโทรตามจำนวนวิธี เริ่มธุรกรรม()เกินจำนวนการเรียกเมธอด กระทำธุรกรรม()หรือ ยกเลิกธุรกรรม()จากนั้นระบบจะทำการเรียกเมธอดโดยนัย ยกเลิกธุรกรรม()ในกรณีต่อไปนี้:

● เมื่อสิ้นสุดการดำเนินการของภาษาในตัว (ตัวจัดการเหตุการณ์ การเชื่อมต่อภายนอก เซิร์ฟเวอร์อัตโนมัติ)

● เมื่อถ่ายโอนการควบคุมจากเซิร์ฟเวอร์ไปยังไคลเอนต์

หากโทรตามจำนวนวิธี กระทำธุรกรรม() หรือ ยกเลิกธุรกรรม()เกินจำนวนการเรียกเมธอด เริ่มธุรกรรม()จากนั้นเมื่อดำเนินการเรียกวิธีการที่ไม่จำเป็น กระทำธุรกรรม()หรือ ยกเลิกธุรกรรม()ข้อยกเว้นจะถูกส่งออกไป ดังนั้นรูปแบบการทำงานกับธุรกรรมค่ะ ปริทัศน์อาจมีลักษณะเช่นนี้:

พยายาม

เริ่มธุรกรรม();

// ลำดับของคำสั่ง

กระทำธุรกรรม();

ข้อยกเว้น

ยกเลิกธุรกรรม();

สิ้นสุดความพยายาม;

เมื่อใช้โครงร่างดังกล่าว คุณควรจำไว้ว่าข้อผิดพลาดทั้งหมดที่เกิดขึ้นขณะทำงานกับฐานข้อมูลนั้นไม่ได้รับการประมวลผลโดยระบบในลักษณะเดียวกัน โดยทั่วไป ข้อผิดพลาดฐานข้อมูลทั้งหมดสามารถแบ่งออกเป็นสองประเภท:

● ไม่สามารถกู้คืนได้

● สามารถกู้คืนได้

ข้อผิดพลาดที่ไม่สามารถกู้คืนได้- นี่คือข้อผิดพลาด หากเกิดขึ้น การทำงานปกติของระบบ 1C:Enterprise อาจหยุดชะงัก เช่น ข้อมูลอาจเสียหาย หากเกิดข้อผิดพลาดที่ไม่สามารถกู้คืนได้ การดำเนินการของระบบ 1C:Enterprise จะสิ้นสุดลงไม่ว่าในกรณีใด หากเกิดข้อผิดพลาดที่ไม่สามารถกู้คืนได้ในระหว่างการทำธุรกรรม การเปลี่ยนแปลงทั้งหมดที่ทำโดยเป็นส่วนหนึ่งของธุรกรรมนั้นจะถูกยกเลิกโดยระบบ

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

พยายาม... ข้อยกเว้น... EndTry

การเรียกธุรกรรมที่ซ้อนกัน

ภายในธุรกรรมที่กำลังดำเนินการอยู่ คุณสามารถเข้าถึงขั้นตอนต่างๆ ได้ เริ่มธุรกรรม(), กระทำธุรกรรม()และ ยกเลิกธุรกรรม(). ตัวอย่างเช่น สามารถใช้รูปแบบการโทรต่อไปนี้:

เริ่มธุรกรรม();

เริ่มธุรกรรม();

กระทำธุรกรรม();

// การเรียกธุรกรรมที่ซ้อนกัน

เริ่มธุรกรรม();

กระทำธุรกรรม();

กระทำธุรกรรม();

อย่างไรก็ตาม การเรียกดังกล่าวไม่ได้หมายถึงการเริ่มต้นธุรกรรมใหม่ภายในธุรกรรมที่ดำเนินการอยู่แล้ว

ความสนใจ!ระบบ 1C:Enterprise ไม่รองรับธุรกรรมแบบซ้อนซึ่งหมายความว่าเฉพาะธุรกรรมเท่านั้นที่จะมีผลเสมอ ระดับสูง.

ธุรกรรมทั้งหมดที่ถูกเรียกภายในธุรกรรมที่เปิดอยู่แล้วนั้นแท้จริงแล้วเป็นส่วนหนึ่งของธุรกรรมเดียวกัน แทนที่จะสร้างธุรกรรมที่ซ้อนกัน ดังนั้นการเลิกทำการเปลี่ยนแปลงที่ทำในธุรกรรมที่ซ้อนกันในท้ายที่สุดจะไม่เลิกทำการเปลี่ยนแปลงในธุรกรรมที่ซ้อนกันในท้ายที่สุด แต่จะเลิกทำการเปลี่ยนแปลงทั้งหมดในธุรกรรมระดับบนสุด ในเวลาเดียวกัน การเปลี่ยนแปลงที่เกิดขึ้นในธุรกรรมที่ซ้อนกันจะถูกละเว้น

ผลกระทบของธุรกรรมต่อการทำงานของออบเจ็กต์ซอฟต์แวร์

โดยทั่วไป ออบเจ็กต์ซอฟต์แวร์ที่ใช้โดยระบบ 1C:Enterprise จะโปร่งใสอย่างยิ่งต่อธุรกรรมฐานข้อมูล กล่าวอีกนัยหนึ่ง ธุรกรรมฐานข้อมูลสามารถถูกเรียกได้เมื่อดำเนินการ วิธีการต่างๆ วัตถุซอฟต์แวร์อย่างไรก็ตาม ตัวอย่างเช่น การดำเนินการที่ทำโดยฐานข้อมูลเมื่อย้อนกลับธุรกรรมโดยทั่วไปจะไม่ส่งผลกระทบต่อธุรกรรมที่เกี่ยวข้อง ซอฟต์แวร์ วัตถุ

จากนี้ไปเมื่อยกเลิกธุรกรรมฐานข้อมูลผู้พัฒนา (หากจำเป็น) จะต้องตรวจสอบให้แน่ใจอย่างอิสระว่ามีการเปลี่ยนแปลงข้อมูลที่เกี่ยวข้องอย่างเพียงพอ ซอฟต์แวร์ วัตถุ ซึ่งสามารถทำได้โดยการอ่านข้อมูลออบเจ็กต์ทั้งหมดอีกครั้ง หรือโดยการเปลี่ยนแปลงรายละเอียดออบเจ็กต์ของโปรแกรมบางส่วน

มีข้อยกเว้นสำหรับกฎนี้ เนื่องจากความจำเพาะของแอปพลิเคชันที่สำคัญของออบเจ็กต์ซอฟต์แวร์ของระบบ 1C:Enterprise ในบางกรณี การย้อนกลับการเปลี่ยนแปลงที่ทำในฐานข้อมูลอาจยังคงส่งผลกระทบต่อค่าคุณสมบัติของคุณสมบัติที่เกี่ยวข้อง ซอฟต์แวร์ วัตถุ สิ่งนี้เกิดขึ้นในกรณีต่อไปนี้:

● เมื่อธุรกรรมถูกยกเลิก คุณลักษณะการผ่านรายการเอกสารจะเรียกคืนค่าที่อยู่ก่อนเริ่มธุรกรรม

● หากออบเจ็กต์ถูกสร้างขึ้นและเขียนในธุรกรรม จากนั้นเมื่อธุรกรรมถูกย้อนกลับ ค่าอ้างอิงจะถูกล้าง

● หากออบเจ็กต์ถูกสร้างขึ้นนอกธุรกรรม และเมื่อบันทึกในธุรกรรม จะมีการใช้รหัส/หมายเลขที่สร้างขึ้นโดยอัตโนมัติ จากนั้นเมื่อธุรกรรมถูกยกเลิก รหัส/หมายเลขจะถูกล้าง

2017-08-12

สร้างธุรกรรมแบบกำหนดเองสำหรับการรักษาออบเจ็กต์ OM

การแนะนำ

ฉันคิดว่าที่ปรึกษา SAP ที่ใช้งานได้หลายคนประสบปัญหาในการบำรุงรักษาออบเจ็กต์การจัดการองค์กร กล่าวคือการทำธุรกรรม พีพี01

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

ตาราง T77S0 กลุ่ม "TCODE"

เมื่อตั้งค่าวัตถุวัตถุ OM คุณอาจสัมผัสการตั้งค่าที่อยู่ในเส้นทางต่อไปนี้ สโปร:

IMG: การจัดการบุคลากร -> การจัดการองค์กร -> การตั้งค่าพื้นฐาน -> การปรับปรุงโมเดลข้อมูล -> รักษาประเภทออบเจ็กต์

ที่นี่คุณสร้างวัตถุ OM ใหม่ตั้งชื่อเลือกไอคอนและกำหนดการตั้งค่าบางอย่างสำหรับวัตถุเหล่านั้น... ในขณะนี้เราสนใจโหนด " คีย์ประเภทวัตถุ + ธุรกรรม"

มุมมองการตั้งค่าบางส่วนจะเปิดต่อหน้าคุณ T77S0ด้วยค่ากลุ่มที่กรองแล้ว

ควรค่าแก่การเอาใจใส่เป็นกลุ่ม TCODEซึ่งหากคุณมองอย่างใกล้ชิด คุณจะพบชื่อทางเทคนิคของธุรกรรมที่คุณน่าจะต้องดำเนินการด้วยมากที่สุด นอกจากนี้ในคอลัมน์ ค่าระบุประเภทของออบเจ็กต์ที่มีจุดประสงค์ในการทำธุรกรรมโดยเฉพาะ

มีอะไรพิเศษเกี่ยวกับธุรกรรมเหล่านี้?

ด้วยการใช้ธุรกรรมที่ออกแบบมาเพื่อรักษาชนิดของออบเจ็กต์ที่เฉพาะเจาะจง คุณไม่จำเป็นต้องเลือกออบเจ็กต์ชนิดเดียวกันเหล่านี้ที่พร้อมใช้งานตามค่าเริ่มต้นในธุรกรรมอีกต่อไป พีพี01. นั่นคือโดยการเปิดตัวธุรกรรม เป็นต้น ปอ09,คุณเริ่มทำงานกับวัตถุเช่นทันที

การสร้างธุรกรรมใหม่สำหรับออบเจ็กต์การจัดการองค์กรของคุณเอง

ในโพสต์ก่อนหน้าของฉัน ฉันได้พูดคุยเกี่ยวกับวิธีที่คุณสามารถสร้างออบเจ็กต์ OM ใหม่ + เพิ่มการค้นหาเชิงโครงสร้างได้

ฉันจะไม่ไปไกลจากเนื้อหานี้ เพื่อเป็นการสาธิต ฉันจะสร้างธุรกรรมใหม่เพื่อรักษาออบเจ็กต์ 91.

การกำหนดประเภทวัตถุใหม่ใน T77S0

กำหนดชื่อของธุรกรรมในอนาคตในมุมมองการตั้งค่า T77S0

ค่า "ZP91M" ในกรณีนี้คือชื่อของธุรกรรมในอนาคตสำหรับการบำรุงรักษาออบเจ็กต์ 91 . บันทึกการเปลี่ยนแปลงของคุณ

การสร้างธุรกรรมใหม่เพื่อรักษาวัตถุ OM

ผ่านการทำธุรกรรม สพ93สร้างธุรกรรมเพื่อรักษาวัตถุของคุณ ด้านล่างนี้เป็นส่วนย่อยของวิดีโอพร้อมลำดับการดำเนินการที่ต้องดำเนินการเพื่อสร้างธุรกรรมที่เกี่ยวข้อง

สังเกตค่าที่ใช้สำหรับเขตข้อมูล โปรแกรม, หมายเลขหน้าจอ,วัตถุการอนุญาต. ตอนนี้เริ่มการทำธุรกรรมใหม่

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

ครั้งล่าสุดที่เราดู วิธีที่ง่ายที่สุดใช้ภาษา 1C ในตัว ในการฝึกฝน การทำธุรกรรมมักใช้ร่วมกับการออกแบบมากขึ้น ในกรณีที่เกิดข้อผิดพลาด สามารถดำเนินการโค้ดต่อไปได้ เช่นเดียวกับการแจ้งข้อความแสดงข้อผิดพลาดที่เพียงพอแก่ผู้ใช้ และเขียนข้อมูลลงในบันทึกการลงทะเบียนหรือไฟล์บันทึกเพื่อการวิเคราะห์ในภายหลังโดยผู้ดูแลระบบ

หากเราหันไปใช้เอกสารทางเทคนิคหรือดิสก์ ITS เราจะเห็นว่า 1C แนะนำวิธีการต่อไปนี้ในการจัดการธุรกรรมในความพยายาม

พยายาม //1. จุดเริ่มต้นของการทำธุรกรรมเริ่มธุรกรรม() ; //2. บล็อกของการดำเนินการที่ทำในธุรกรรม //3. หากการดำเนินการทั้งหมดประสบความสำเร็จ เราจะทำธุรกรรมกระทำธุรกรรม() ; ข้อยกเว้น //4. หากเกิดข้อผิดพลาดขณะดำเนินการโค้ด ให้ยกเลิกธุรกรรมยกเลิกธุรกรรม() ; //5. หากจำเป็นให้บันทึกไว้ในสมุดบันทึก //6. หากจำเป็น ให้แสดงข้อความแก่ผู้ใช้ความพยายามสิ้นสุด ;

จริงๆ แล้วโค้ดไม่จำเป็นต้องมีคำอธิบายพิเศษใดๆ ถ้ากำลังดำเนินการ ความพยายามเมื่อรันโค้ดธุรกรรมเกิดข้อผิดพลาดเราจะตกอยู่ในบล็อกทันที ข้อยกเว้น, เช่น. ก่อนวิธี กระทำธุรกรรม()เราแค่ไปไม่ถึงที่นั่น ยกเว้นกรณีนี้ เราจะยกเลิกธุรกรรมตามนั้น และหากจำเป็น ให้แสดงข้อความแสดงข้อผิดพลาดและเขียนข้อมูลลงในบันทึก ขอแนะนำอย่างยิ่งให้บันทึกข้อผิดพลาดลงในสมุดบันทึก โดยเฉพาะอย่างยิ่งสำหรับการดำเนินการที่ดำเนินการโดยที่ผู้ใช้ไม่มีส่วนร่วม (เช่น งานประจำ) ซึ่งจะช่วยให้คุณสามารถวิเคราะห์ข้อผิดพลาดได้ในภายหลัง แทนที่จะบันทึก คุณสามารถจัดเตรียมข้อความที่จะส่งถึงผู้ดูแลระบบทางอีเมลได้

ตอนนี้ ด้วยความรู้ใหม่ เราลองแก้ไขโค้ดที่กล่าวถึงในบทความเกี่ยวกับ . ฉันขอเตือนคุณว่าเราพิจารณารายการในไดเร็กทอรีแล้ว สินค้าและลงทะเบียนข้อมูล ราคาตามโครงการดังต่อไปนี้:

&บนเซิร์ฟเวอร์โดยไม่มีบริบทเริ่มธุรกรรม() ; //บันทึกผลิตภัณฑ์ใหม่สินค้า = ไดเรกทอรี สินค้า. สร้างรายการ() ; ผลิตภัณฑ์. Name = "เจาะรู" ; ผลิตภัณฑ์. เขียน() ; //เขียนราคา RecordSet = ผู้ลงทะเบียนข้อมูล ราคา. CreateRecordSet() ; NewRecord = ชุดระเบียน เพิ่ม() ; สถิติใหม่. ระยะเวลา = CurrentDate() ; สถิติใหม่. สินค้า = สินค้า ลิงค์; สถิติใหม่. จำนวน = 100 ; ชุดระเบียน เขียน() ; กระทำธุรกรรม() ; สิ้นสุดขั้นตอน

ตอนนี้เรามาวางธุรกรรมไว้ในบล็อก พยายามยกเว้น. เป็นไปได้มากว่าข้อผิดพลาดสามารถเกิดขึ้นได้เฉพาะเมื่อเขียนไปยังไดเร็กทอรีหรือการลงทะเบียนข้อมูลเท่านั้น การเตรียมการเบื้องต้นเอามันออกไปนอกธุรกรรม

&บนเซิร์ฟเวอร์โดยไม่มีบริบทขั้นตอน RunTransactionOnServer() //สร้างผลิตภัณฑ์ใหม่สินค้า = ไดเรกทอรี สินค้า. สร้างรายการ() ; ผลิตภัณฑ์. Name = "เจาะรู" ; //สร้างบันทึกพร้อมราคา RecordSet = ผู้ลงทะเบียนข้อมูล ราคา. CreateRecordSet() ; NewRecord = ชุดระเบียน เพิ่ม() ; สถิติใหม่. ระยะเวลา = CurrentDate() ; สถิติใหม่. จำนวน = 100 ; //ดำเนินการธุรกรรมด้วยความพยายามพยายามเริ่มธุรกรรม(); ผลิตภัณฑ์. เขียน() ; สถิติใหม่. สินค้า = สินค้า ลิงค์; ชุดระเบียน เขียน() ; กระทำธุรกรรม() ; ข้อยกเว้น ยกเลิกธุรกรรม() ; ข้อความ = MessageToUser ใหม่; ข้อความ. ข้อความ = ; ข้อความ. เพื่อรายงาน() ; เข้าสู่ระบบการลงทะเบียน( "เกิดข้อผิดพลาดขณะบันทึกสินค้าและราคา") ; ความพยายามสิ้นสุด ; สิ้นสุดขั้นตอน

สิ่งที่ไม่ควรทำ

ผู้ที่เพิ่งเริ่มทำงานกับธุรกรรมมักมีความปรารถนาที่จะทำเช่นนี้

เริ่มธุรกรรม() ; พยายามเริ่มธุรกรรม(); //บล็อกการดำเนินการ CommitTransaction() ; ข้อยกเว้น ยกเลิกธุรกรรม() ; ความพยายามสิ้นสุด ; พยายามเริ่มธุรกรรม(); //บล็อกการดำเนินการ CommitTransaction() ; ข้อยกเว้น ยกเลิกธุรกรรม() ; ความพยายามสิ้นสุด ; กระทำธุรกรรม() ;

หรืออยู่ในวง

เริ่มธุรกรรม() ; สำหรับแต่ละข้อมูลจาก Data Array Loop พยายามเริ่มธุรกรรม () ; ข้อมูล. เขียน() ; กระทำธุรกรรม() ; ข้อยกเว้น ยกเลิกธุรกรรม() ; ความพยายามสิ้นสุด ; เอ็นด์ไซเคิล ; กระทำธุรกรรม() ;

เมื่อมองแวบแรก เราทำทุกอย่างตามคำแนะนำของบริษัท 1C แต่ความจริงก็คือแพลตฟอร์ม 1C ไม่รองรับธุรกรรมที่ซ้อนกัน นั่นคือในทางเทคนิคแล้วสามารถเขียนแบบนี้ได้ แต่ในขณะเดียวกัน ธุรกรรมที่ซ้อนกันทั้งหมดไม่ได้สร้างธุรกรรมใหม่ แต่เป็นของธุรกรรมระดับบนสุดเดียวกัน ด้วยวิธีนี้ หากหนึ่งในธุรกรรมที่ซ้อนกันล้มเหลว จะไม่สามารถคอมมิตธุรกรรมที่ซ้อนกันถัดไปได้ ระบบจะแสดงข้อความดังนี้: “มีข้อผิดพลาดเกิดขึ้นแล้วในธุรกรรมนี้!”. มาสาธิตสิ่งนี้ด้วยตัวอย่างกัน สมมติว่าเราตัดสินใจบันทึกสินค้าสองรายการ โดยแต่ละรายการอยู่ในธุรกรรมของตัวเอง และมาทำธุรกรรมเหล่านี้ซ้อนอยู่ในรายการที่สามกัน ต่อไปเราจะทำให้เกิดข้อผิดพลาดปลอมในธุรกรรมครั้งแรกโดยใช้วิธีการ ยกข้อยกเว้น:

&บนเซิร์ฟเวอร์โดยไม่มีบริบทขั้นตอน RunTransactionOnServer() StartTransaction() ; พยายามเริ่มธุรกรรม(); สินค้า = ไดเรกทอรี สินค้า. สร้างรายการ() ; ผลิตภัณฑ์. ชื่อ = "ตาราง" ; ผลิตภัณฑ์. เขียน() ; ยกข้อยกเว้น "รายการสินค้าผิดพลาด"; กระทำธุรกรรม() ; ข้อยกเว้น ยกเลิกธุรกรรม() ; ข้อความ = MessageToUser ใหม่; ข้อความ. ข้อความ = ErrorDescription() พยายามเริ่มธุรกรรม() ; สินค้า = ไดเรกทอรี สินค้า. สร้างรายการ() ; ผลิตภัณฑ์. ชื่อ = "เก้าอี้" ; ผลิตภัณฑ์. เขียน() ; กระทำธุรกรรม() ; ข้อยกเว้น ยกเลิกธุรกรรม() ; ข้อความ = MessageToUser ใหม่; ข้อความ. ข้อความ = คำอธิบายข้อผิดพลาด () ; ข้อความ. เพื่อรายงาน() ; ความพยายามสิ้นสุด ; กระทำธุรกรรม() ; สิ้นสุดขั้นตอน

จากการดำเนินการตามขั้นตอนนี้ เราจะเห็นสิ่งต่อไปนี้ในหน้าต่างข้อความ:

(ExternalProcessing.TransactionsAtTrying.Form.Form.Form(20)): เกิดข้อผิดพลาดในการเขียนรายการ (ExternalProcessing.TransactionsAtTrying.Form.Form.Form (40)): ข้อผิดพลาดเมื่อเรียกวิธีบริบท (เขียน): มีข้อผิดพลาดเกิดขึ้นแล้วในธุรกรรมนี้!

ดังนั้นการจัดระเบียบธุรกรรมที่ซ้อนกันใน 1C จึงไม่มีประโยชน์อย่างยิ่ง

ตัวเลือกที่เป็นไปได้

ตอนนี้เรากลับไปที่ตัวเลือกที่เราบันทึกผลิตภัณฑ์และราคาไว้ หากเรามีข้อผิดพลาดขณะทำธุรกรรม จะเป็นการยากที่จะเข้าใจว่าเกิดขึ้นที่จุดใด - เมื่อบันทึกผลิตภัณฑ์หรือเมื่อบันทึกราคา เนื่องจากทั้งสองเกิดขึ้นภายในความพยายามเดียวกัน เพื่อพิจารณาว่าข้อผิดพลาดเกิดขึ้นที่ใด เราจำเป็นต้องรวมการดำเนินการเขียนแต่ละครั้งด้วยความพยายามของตัวเองและหลีกเลี่ยงธุรกรรมที่ซ้อนกัน เมื่อต้องการทำเช่นนี้ เราแนะนำตัวแปรบูลีน การปฏิเสธและขึ้นอยู่กับมูลค่าของมันเมื่อสิ้นสุดการดำเนินการทั้งหมด เราจะกระทำหรือยกเลิกธุรกรรม

&บนเซิร์ฟเวอร์โดยไม่มีบริบทขั้นตอน RunTransactionOnServer() // เริ่มการทำธุรกรรมปฏิเสธ = เท็จ ; เริ่มธุรกรรม() ; //พยายามบันทึกสินค้าพยายามผลิตภัณฑ์ = ไดเรกทอรี สินค้า. สร้างรายการ() ; ผลิตภัณฑ์. Name = "เจาะรู" ; ผลิตภัณฑ์. เขียน() ; ข้อยกเว้นล้มเหลว = True ; ข้อความ = MessageToUser ใหม่; ข้อความ. ข้อความ = "สินค้าบันทึกข้อผิดพลาด"; ข้อความ. เพื่อรายงาน() ; ความพยายามสิ้นสุด ; //พยายามบันทึกราคา AttemptRecordSet = ผู้ลงทะเบียนข้อมูล ราคา. CreateRecordSet() ; NewRecord = ชุดระเบียน เพิ่ม() ; สถิติใหม่. ระยะเวลา = CurrentDate() ; สถิติใหม่. สินค้า = สินค้า ลิงค์; สถิติใหม่. จำนวน = 100 ; ชุดระเบียน เขียน() ; ข้อยกเว้นล้มเหลว = True ; ข้อความ = MessageToUser ใหม่; ข้อความ. ข้อความ = "เกิดข้อผิดพลาดขณะบันทึกราคา"; ข้อความ. เพื่อรายงาน() ; ความพยายามสิ้นสุด ; // ยืนยันหรือยกเลิกธุรกรรมหากไม่ล้มเหลว ให้ CommitTransaction() ; มิฉะนั้น ยกเลิกธุรกรรม() ; สิ้นสุดถ้า ; สิ้นสุดขั้นตอน

เราสามารถทำสิ่งเดียวกันได้เมื่อเราวนซ้ำและเขียนข้อมูลใดๆ ลงในลูป ในกรณีนี้ เราจะสามารถรับรายการข้อมูลทั้งหมดที่มีข้อผิดพลาดได้ ถ้ามี

ในการเตรียมพร้อมสำหรับการรับรองผู้เชี่ยวชาญ 1C ในวันสองหัวข้อที่สำคัญมากและเป็นสากล - การบล็อก ฉันอยากจะดูบางสิ่งบางอย่างโดยที่แนวคิดข้างต้นเป็นไปไม่ได้ - ธุรกรรม DBMS

ธุรกรรม- ลำดับการกระทำที่เชื่อมโยงกันอย่างมีเหตุผลและแบ่งแยกไม่ได้ การทำธุรกรรมสามารถดำเนินการให้เสร็จสิ้นทั้งหมดหรือไม่ได้ทำเลยก็ได้ ในการทำธุรกรรมใน DBMS จะใช้วิธี COMMIT

ตัวอย่างทั่วไปของธุรกรรมคือการโอน เงินจากบัญชีหนึ่งไปยังอีกบัญชีหนึ่ง:

  1. เริ่มการทำธุรกรรม
  2. อ่านจำนวนเงินในบัญชีหมายเลข 123
  3. ลดยอดเงินในบัญชี 123 ลง 100 รูเบิล
  4. บันทึกยอดเงินในบัญชีหมายเลข 123;
  5. อ่านจำนวนเงินในบัญชีหมายเลข 321
  6. เพิ่มยอดเงินของคุณ 100 รูเบิล;
  7. บันทึกจำนวนเงินใหม่ในบัญชี 321
  8. กระทำธุรกรรม

รับบทเรียนวิดีโอ 267 บทเรียนบน 1C ฟรี:

ดังที่เราเห็นแล้วว่าหากธุรกรรมไม่เสร็จสมบูรณ์ก็ไม่มีความหมาย

ข้อกำหนดหลัก (ACID) สำหรับ DBMS ของธุรกรรม

ชุดข้อกำหนดที่พบบ่อยที่สุดชุดหนึ่งสำหรับธุรกรรมและ DBMS ของธุรกรรมคือชุด ACID (Atomicity, Consistency, Isolation, Durability) สิ่งเหล่านี้คือคุณสมบัติที่ธุรกรรมใด ๆ ต้องมี:

  • อะตอมมิกซิตี— ไม่ควรบันทึกธุรกรรมเพียงบางส่วน
  • ความสม่ำเสมอ- ระบบอยู่ในสถานะสม่ำเสมอก่อนการทำธุรกรรมเริ่มต้นและจะต้องคงอยู่ในสถานะที่สอดคล้องกันหลังจากการทำธุรกรรมเสร็จสิ้น
  • การแยกตัว— ในระหว่างการดำเนินการธุรกรรม ธุรกรรมแบบขนานไม่ควรส่งผลกระทบต่อผลลัพธ์
  • ความทนทาน- ในกรณีที่เกิดความล้มเหลว การเปลี่ยนแปลงที่ทำโดยธุรกรรมที่เสร็จสมบูรณ์จะต้องถูกบันทึกไว้หลังจากที่ระบบกลับสู่การทำงาน

ธุรกรรมใน 1C

ธุรกรรมใน 1C 8.3 และ 8.2 ถูกสร้างขึ้นโดยอัตโนมัติและอธิบายโดยนักพัฒนา

คุณสามารถใช้เมธอด TransactionActive() เพื่อดูว่าธุรกรรมนั้นมีการใช้งานอยู่หรือไม่

ตัวอย่างของธุรกรรมอัตโนมัติคือการประมวลผลการผ่านรายการเอกสาร การเขียนรายการไดเรกทอรีไปยังฐานข้อมูล การเขียนชุดบันทึกการลงทะเบียนข้อมูล ฯลฯ

ชื่อเรื่องดูติดหูแต่ก็จบแล้ว ฉันจะบอกทันทีว่าเราจะพูดถึง 1C เรียนผู้ใช้ 1C คุณไม่รู้วิธีทำงานกับธุรกรรมและไม่เข้าใจว่าข้อยกเว้นคืออะไร ฉันมาถึงข้อสรุปนี้โดยการมองผ่าน จำนวนมากรหัส 1C เกิดในป่าขององค์กรในประเทศ ใน การกำหนดค่าทั่วไปทั้งหมดนี้ดีพอแล้ว แต่โค้ดที่กำหนดเองจำนวนมากจนน่าตกใจนั้นถูกเขียนอย่างไร้ความสามารถจากมุมมองของฐานข้อมูล คุณเคยเห็นข้อผิดพลาด “ธุรกรรมนี้พบข้อผิดพลาดแล้ว” หรือไม่? ถ้าใช่ ชื่อของบทความก็มีผลกับคุณเช่นกัน ในที่สุดเรามาดูกันว่าธุรกรรมคืออะไรและจะจัดการอย่างไรให้ถูกต้องเมื่อทำงานกับ 1C

ทำไมเราต้องส่งเสียงเตือน?

ขั้นแรก เรามาดูกันว่าข้อผิดพลาด "ข้อผิดพลาดเกิดขึ้นแล้วในธุรกรรมนี้" คืออะไร ในความเป็นจริงนี่คือสิ่งที่ง่ายมาก: คุณกำลังพยายามทำงานกับฐานข้อมูลภายในธุรกรรมที่ย้อนกลับ (ยกเลิก) แล้ว ตัวอย่างเช่น มีการเรียกเมธอด CancelTransaction ที่ไหนสักแห่ง และคุณกำลังพยายามคอมมิตมัน


ทำไมมันถึงแย่ล่ะ? เพราะ ข้อผิดพลาดนี้ไม่ได้บอกคุณว่าปัญหาเกิดขึ้นจริงที่ไหน เมื่อฝ่ายสนับสนุนได้รับภาพหน้าจอพร้อมข้อความดังกล่าวจากผู้ใช้ และโดยเฉพาะอย่างยิ่งสำหรับ รหัสเซิร์ฟเวอร์โดยที่บุคคลไม่ทำงานแบบโต้ตอบคือ... ฉันต้องการเขียน "ข้อผิดพลาดร้ายแรง" แต่ฉันคิดว่านี่เป็นคำศัพท์ที่ไม่มีใครสนใจอีกต่อไป... นี่คือตูด นี่เป็นข้อผิดพลาดในการเขียนโปรแกรม นี่ไม่ใช่ความผิดพลาดแบบสุ่ม นี่เป็นข้อผิดพลาดที่ต้องแก้ไขทันที เนื่องจากเมื่อกระบวนการเซิร์ฟเวอร์เบื้องหลังของคุณล่มในเวลากลางคืนและบริษัทเริ่มสูญเสียเงินอย่างรวดเร็ว “มีข้อผิดพลาดเกิดขึ้นแล้วในธุรกรรมนี้” คือสิ่งสุดท้ายที่คุณต้องการเห็นในบันทึกการวินิจฉัย


แน่นอนว่ามีความเป็นไปได้ที่บันทึกทางเทคโนโลยีของเซิร์ฟเวอร์ (ซึ่งเปิดใช้งานอยู่ในการใช้งานจริงใช่ไหม) จะช่วยวินิจฉัยปัญหาได้ทางใดทางหนึ่ง แต่ตอนนี้ฉันไม่สามารถนึกถึงตัวเลือกอื่นนอกเหนือจากในหัวของฉันได้ - เป็นไปได้อย่างไร เพื่อค้นหาสาเหตุที่แท้จริงของข้อผิดพลาดนี้ในนั้น แต่เหตุผลที่แท้จริงก็คือเหตุผลหนึ่ง - โปรแกรมเมอร์ Vasya ได้รับข้อยกเว้นในการทำธุรกรรมและตัดสินใจว่าครั้งหนึ่งไม่ใช่ความคิดที่ไม่ดี "แค่คิดว่า มันเป็นความผิดพลาด เรามาเริ่มกันเลยดีกว่า"

ธุรกรรมใน 1C คืออะไร

การเขียนเกี่ยวกับความจริงเบื้องต้นอาจดูกระอักกระอ่วนใจ แต่ดูเหมือนจะจำเป็นเพียงเล็กน้อย ธุรกรรมใน 1C เหมือนกับธุรกรรมใน DBMS สิ่งเหล่านี้ไม่ใช่ธุรกรรม “1C” พิเศษ แต่เป็นธุรกรรมใน DBMS ตามแนวคิดทั่วไปของธุรกรรม พวกเขาสามารถดำเนินการทั้งหมดหรือไม่ดำเนินการเลยก็ได้ การเปลี่ยนแปลงตารางฐานข้อมูลทั้งหมดที่เกิดขึ้นภายในธุรกรรมสามารถยกเลิกได้ทันที ราวกับว่าไม่มีอะไรเกิดขึ้น


ถัดไปคุณต้องเข้าใจว่า 1C ไม่รองรับธุรกรรมที่ซ้อนกัน ตามความเป็นจริง พวกเขาไม่รองรับ "ใน 1C" แต่ไม่รองรับเลย อย่างน้อย DBMS เหล่านั้นที่ 1C สามารถทำงานได้ ตัวอย่างเช่น ธุรกรรมที่ซ้อนกันไม่มีอยู่ใน MS SQL และ Postgres การเรียก "ซ้อนกัน" แต่ละครั้งไปยัง StartTransaction เพียงเพิ่มตัวนับธุรกรรม และการเรียกแต่ละครั้งไปยัง "CommitTransaction" เพียงลดตัวนับนี้ พฤติกรรมนี้อธิบายไว้ในหนังสือและบทความหลายเล่ม แต่ข้อสรุปจากพฤติกรรมนี้ดูเหมือนจะไม่ได้รับการวิเคราะห์อย่างเพียงพอ พูดอย่างเคร่งครัดใน SQL มีสิ่งที่เรียกว่า SAVEPOINT แต่ 1C ไม่ได้ใช้ และสิ่งนี้ค่อนข้างเฉพาะเจาะจง



ขั้นตอน รหัสที่มีประโยชน์และสำคัญมาก (รายการลิงก์ไดเรกทอรี) StartTransaction(); สำหรับแต่ละลิงก์จากรายการไดเร็กทอรี ลิงก์ Loop Directory Object = Link.GetObject(); Directory Object.SomeField = "ฉันถูกเปลี่ยนจากโค้ดโปรแกรม"; วัตถุไดเรกทอรีเขียน (); สิ้นสุดรอบ; กระทำธุรกรรม(); สิ้นสุดขั้นตอน

รหัสเป็นภาษาอังกฤษ

ไม่เชิง. ฉันไม่ต้องการทำซ้ำตัวอย่างเป็นภาษาอังกฤษเพียงเพื่อสร้างความสนุกสนานให้กับแฟน ๆ ของสงครามศักดิ์สิทธิ์และสงครามศักดิ์สิทธิ์


คุณอาจจะเขียนโค้ดแบบนี้ใช่ไหม? ตัวอย่างโค้ดที่ให้มามีข้อผิดพลาด อย่างน้อยสาม คุณรู้หรือไม่ว่าอันไหน? ฉันจะพูดทันทีเกี่ยวกับอันแรก มันเกี่ยวข้องกับการล็อควัตถุและไม่เกี่ยวข้องโดยตรงกับธุรกรรม ประมาณวินาที - ต่อมาเล็กน้อย ข้อผิดพลาดที่สามคือการหยุดชะงักซึ่งจะเกิดขึ้นระหว่างการดำเนินการโค้ดนี้แบบขนาน แต่นี่เป็นหัวข้อสำหรับบทความแยกต่างหาก เราจะไม่พิจารณาในตอนนี้เพื่อไม่ให้โค้ดซับซ้อน คำสำคัญสำหรับกูเกิ้ล: ล็อคแบบควบคุมการหยุดชะงัก.


โปรดทราบว่ารหัสนั้นเรียบง่าย มีสิ่งนี้มากมายในระบบ 1C ของคุณ และมีข้อผิดพลาดอย่างน้อย 3 ข้อในคราวเดียว ลองคิดดูว่ามีข้อผิดพลาดกี่ข้อในสถานการณ์ที่ซับซ้อนมากขึ้นในการทำงานกับธุรกรรมที่เขียนโดยโปรแกรมเมอร์ 1C ของคุณ :)

ล็อควัตถุ

ดังนั้นความผิดพลาดครั้งแรก ใน 1C มีการล็อควัตถุ ที่เรียกว่า "มองโลกในแง่ดี" และ "มองโลกในแง่ร้าย" ฉันไม่รู้ว่าใครเป็นคนบัญญัติคำนี้ ฉันคงฆ่าเขาไปแล้ว :) เป็นไปไม่ได้เลยที่จะจำได้ว่าใครรับผิดชอบอะไร มีการเขียนเกี่ยวกับรายละเอียด เช่นเดียวกับในวรรณกรรมด้านไอทีทั่วไปอื่นๆ


สาระสำคัญของปัญหาคือในตัวอย่างโค้ดที่ระบุ วัตถุฐานข้อมูลมีการเปลี่ยนแปลง แต่ในเซสชันอื่น อาจมีผู้ใช้แบบโต้ตอบ (หรือเธรดพื้นหลังใกล้เคียง) ซึ่งจะเปลี่ยนวัตถุนี้ด้วย ที่นี่ หนึ่งในคุณอาจได้รับข้อผิดพลาด "รายการได้รับการแก้ไขหรือลบ" หากสิ่งนี้เกิดขึ้นในเซสชันแบบโต้ตอบ ผู้ใช้จะเกาก้น สบถ และพยายามเปิดแบบฟอร์มอีกครั้ง หากสิ่งนี้เกิดขึ้นในเธรดพื้นหลัง คุณจะต้องค้นหามันในบันทึก และอย่างที่ทราบกันดีว่าสมุดบันทึกนั้นช้าและมีเพียงไม่กี่คนในอุตสาหกรรมของเราเท่านั้นที่ตั้งค่าสแต็ก ELK สำหรับบันทึก 1C... (เราเป็นหนึ่งในผู้ที่ตั้งค่าและช่วยเหลือผู้อื่นในการตั้งค่า :) )


กล่าวโดยสรุป นี่เป็นข้อผิดพลาดที่น่ารำคาญ และไม่ควรทำเลย ดังนั้นมาตรฐานการพัฒนาจึงระบุไว้อย่างชัดเจนว่าก่อนที่จะเปลี่ยนวัตถุจำเป็นต้องวางล็อควัตถุไว้โดยใช้ปุ่ม " วัตถุไดเรกทอรีล็อค()" จากนั้น เซสชันที่เกิดขึ้นพร้อมกัน (ซึ่งต้องทำสิ่งนี้ด้วย) จะไม่สามารถเริ่มการดำเนินการอัพเดตได้ และจะได้รับความล้มเหลวที่ควบคุมตามที่คาดหวังไว้

และตอนนี้เกี่ยวกับการทำธุรกรรม

เราได้จัดการกับข้อผิดพลาดแรกแล้ว มาดูข้อผิดพลาดที่สองกันดีกว่า


หากคุณไม่ได้ระบุการตรวจสอบข้อยกเว้นในวิธีนี้ ข้อยกเว้น (เช่น มีแนวโน้มมากในวิธีการ "Write()") จะทำให้คุณออกจากระบบ วิธีนี้ โดยไม่ต้องทำธุรกรรมให้เสร็จสิ้น. ข้อยกเว้นจากวิธี "เขียน" อาจเกิดขึ้นได้ด้วยเหตุผลหลายประการ เช่น การตรวจสอบแอปพลิเคชันบางอย่างในตรรกะทางธุรกิจถูกทริกเกอร์ หรือการล็อกอ็อบเจ็กต์ที่กล่าวมาข้างต้นเกิดขึ้น อย่างไรก็ตาม ข้อผิดพลาดที่สองบอกว่า: รหัสที่เริ่มต้นการทำธุรกรรมจะไม่รับผิดชอบต่อความสมบูรณ์ของมัน



นั่นคือสิ่งที่ผมจะเรียกว่าปัญหานี้ ในตัววิเคราะห์โค้ด 1C แบบคงที่ที่ใช้ SonarQube เรายังสร้างการวินิจฉัยดังกล่าวแยกกันอีกด้วย ตอนนี้ฉันกำลังพัฒนามัน และจินตนาการของโปรแกรมเมอร์ 1C ซึ่งมีโค้ดมาหาฉันเพื่อวิเคราะห์ บางครั้งทำให้ฉันตกใจและทึ่ง...


ทำไม เนื่องจากข้อยกเว้นที่เกิดขึ้นที่ด้านบนสุดภายในธุรกรรมใน 90% ของกรณีจะไม่อนุญาตให้ธุรกรรมนี้เกิดขึ้นและจะนำไปสู่ข้อผิดพลาด ควรเข้าใจว่า 1C จะย้อนกลับธุรกรรมที่ยังไม่เสร็จสิ้นโดยอัตโนมัติหลังจากส่งคืนจากโค้ดสคริปต์เป็นระดับโค้ดแพลตฟอร์มเท่านั้น ตราบใดที่คุณอยู่ในระดับรหัส 1C ธุรกรรมจะยังคงทำงานอยู่


ขึ้นไปอีกระดับหนึ่งใน call stack:


ขั้นตอนสำคัญรหัส() LinkList = GetLinkListWhere(); VeryUsefulAndImportantCode (รายการลิงก์); สิ้นสุดขั้นตอน

ดูสิ่งที่เกิดขึ้น วิธีการแก้ปัญหาของเราถูกเรียกจากภายนอก สูงขึ้นจากสแต็ก ในระดับของวิธีนี้ นักพัฒนาไม่ทราบว่าจะมีธุรกรรมใดๆ ภายในวิธี Very Useful and Import Code หรือไม่ แล้วถ้ามีจะเสร็จทั้งหมดหรือเปล่า...เราทุกคนมาที่นี่เพื่อสันติภาพและห่อหุ้มใช่ไหม? ผู้เขียนเมธอด "ImportantCode" ไม่ควรคิดว่าจะเกิดอะไรขึ้นภายในเมธอดที่เขาเรียก อันเดียวกับที่ธุรกรรมถูกประมวลผลไม่ถูกต้อง ด้วยเหตุนี้ ความพยายามในการทำงานกับฐานข้อมูลหลังจากที่มีข้อยกเว้นถูกส่งออกมาจากภายในธุรกรรมมักจะส่งผลให้เกิดสิ่งต่อไปนี้: “ในธุรกรรมนี้ blah blah…”

กระจายธุรกรรมข้ามวิธี

กฎข้อที่สองของรหัส "ปลอดภัยในการทำธุรกรรม": จำนวนการอ้างอิงธุรกรรมที่จุดเริ่มต้นของวิธีการและจุดสิ้นสุดจะต้องมีค่าเดียวกัน. คุณไม่สามารถเริ่มธุรกรรมด้วยวิธีหนึ่งและสิ้นสุดด้วยวิธีอื่นได้ อาจเป็นไปได้ที่จะพบข้อยกเว้นสำหรับกฎนี้ แต่นี่จะเป็นโค้ดระดับต่ำบางประเภทที่เขียนโดยผู้ที่มีความสามารถมากกว่า โดยทั่วไปคุณไม่สามารถเขียนแบบนี้ได้


ตัวอย่างเช่น:


ขั้นตอนสำคัญรหัส() LinkList = GetLinkListWhere(); VeryUsefulAndImportantCode (รายการลิงก์); กระทำธุรกรรม(); // ตั๋วสู่นรก การสนทนาอย่างจริงจังกับผู้เขียนเกี่ยวกับความสัมพันธ์ด้านแรงงานที่ซับซ้อนของเรา สิ้นสุดขั้นตอน

ข้างต้นเป็นรหัสอึที่ยอมรับไม่ได้ คุณไม่สามารถเขียนวิธีการเพื่อให้ผู้โทรจดจำและติดตามธุรกรรมที่เป็นไปได้ (หรือน่าจะเป็นไปได้ - ใครจะรู้) ภายในวิธีอื่นที่โทร นี่เป็นการละเมิดการห่อหุ้มและการแพร่กระจายของรหัสสปาเก็ตตี้ที่ไม่สามารถติดตามได้ด้วยความมีสติ


เป็นเรื่องสนุกเป็นพิเศษที่ต้องจำไว้ว่าโค้ดจริงมีขนาดใหญ่กว่าตัวอย่างสังเคราะห์ 3 บรรทัดมาก การค้นหาการเริ่มต้นและสิ้นสุดธุรกรรมในระดับการซ้อนหกระดับ - สิ่งนี้จะกระตุ้นการสนทนาอย่างใกล้ชิดกับผู้เขียนโดยตรง

กำลังพยายามแก้ไขโค้ด

ให้เรากลับไปใช้วิธีเดิมแล้วลองแก้ไขดู ฉันจะบอกทันทีว่าเราจะไม่แก้ไขการล็อควัตถุในตอนนี้ เพียงเพื่อไม่ให้โค้ดตัวอย่างซับซ้อน

แนวทางแรกของชื่อเล่น 1C ทั่วไป

โดยทั่วไปแล้วโปรแกรมเมอร์ 1C รู้ว่าอาจมีข้อยกเว้นเกิดขึ้นเมื่อทำการบันทึก พวกเขายังกลัวข้อยกเว้นจึงพยายามจับให้ได้ทั้งหมด ตัวอย่างเช่นเช่นนี้:


ขั้นตอน รหัสที่มีประโยชน์และสำคัญมาก (รายการลิงก์ไดเรกทอรี) StartTransaction(); สำหรับแต่ละลิงก์จากรายการไดเร็กทอรี ลิงก์ Loop Directory Object = Link.GetObject(); Directory Object.SomeField = "ฉันถูกเปลี่ยนจากโค้ดโปรแกรม"; พยายาม DirectoryObject.Write (); บันทึกข้อยกเว้นข้อผิดพลาด ("ไม่สามารถเขียนองค์ประกอบ %1", ลิงก์); ดำเนินการต่อ; สิ้นสุดความพยายาม; สิ้นสุดรอบ; กระทำธุรกรรม(); สิ้นสุดขั้นตอน

สิ่งต่างๆดีขึ้นแล้วใช่ไหม? หลังจากทั้งหมดตอนนี้ ข้อผิดพลาดที่เป็นไปได้บันทึกได้รับการประมวลผลและแม้กระทั่งบันทึก ข้อยกเว้นจะไม่เกิดขึ้นอีกต่อไปเมื่อเขียนวัตถุ และในบันทึกคุณสามารถดูได้ว่าวัตถุใดฉันไม่ได้ขี้เกียจเกินไปและรวมลิงก์ไว้ในข้อความแทนข้อความสั้น ๆ ว่า "ข้อผิดพลาดในการเขียนไดเร็กทอรี" เนื่องจากนักพัฒนาที่รีบร้อนมักจะชอบเขียน กล่าวอีกนัยหนึ่ง มีข้อกังวลสำหรับผู้ใช้และความสามารถที่เพิ่มขึ้น


อย่างไรก็ตาม ผู้ใช้ 1C ที่มีประสบการณ์ที่นี่จะบอกว่าไม่ ยังไม่ดีขึ้นเลย ในความเป็นจริงไม่มีอะไรเปลี่ยนแปลงและอาจแย่ลงไปอีก ในวิธี "เขียน ()" แพลตฟอร์ม 1C จะเริ่มธุรกรรมการเขียนและธุรกรรมนี้จะถูกซ้อนที่เกี่ยวข้องกับของเราแล้ว และหากในขณะที่ทำงานกับฐานข้อมูล 1C ธุรกรรมย้อนกลับ (เช่น ข้อยกเว้นตรรกะทางธุรกิจถูกส่งออกไป) ธุรกรรมระดับบนสุดของเราจะยังคงถูกทำเครื่องหมายว่า "เสียหาย" และไม่สามารถกระทำได้ เป็นผลให้โค้ดนี้ยังคงเป็นปัญหาอยู่ และเมื่อคุณพยายามคอมมิตโค้ดจะแสดง "มีข้อผิดพลาดเกิดขึ้นแล้ว"


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


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

วิธีการทำงานกับธุรกรรมใน 1C

ไม่ใช่เรื่องฟุ่มเฟือยที่จะเตือนคุณถึงสิ่งที่ 1C โดยทั่วไปมอบให้เราในการทำงานกับธุรกรรม ต่อไปนี้เป็นวิธีการที่รู้จักกันดี:

  • เริ่มธุรกรรม()
  • กระทำธุรกรรม()
  • ยกเลิกธุรกรรม()
  • ธุรกรรมที่ใช้งานอยู่()

3 วิธีแรกชัดเจนและทำตามชื่อบอก วิธีสุดท้ายจะส่งกลับค่า True หากตัวนับธุรกรรมมีค่ามากกว่าศูนย์


และมีคุณสมบัติที่น่าสนใจคือ วิธีการออกจากธุรกรรม (ยอมรับและยกเลิก) จะส่งข้อยกเว้นหากนับธุรกรรมเป็นศูนย์ นั่นคือหากคุณเรียกหนึ่งในนั้นนอกธุรกรรม ข้อผิดพลาดจะเกิดขึ้น


วิธีการใช้วิธีการเหล่านี้อย่างถูกต้อง? ง่ายมาก: คุณต้องอ่านกฎที่กำหนดไว้ข้างต้น:


จะปฏิบัติตามกฎนี้ได้อย่างไร? มาลองกัน:


เราเข้าใจแล้วข้างต้นว่าวิธี Do Something อาจเป็นอันตรายได้ มันอาจทำให้เกิดข้อยกเว้นบางประเภท และธุรกรรมจะ "คลาน" จากวิธีการของเรา เอาล่ะ มาเพิ่มตัวจัดการข้อยกเว้นที่เป็นไปได้:


เริ่มธุรกรรม(); ลองทำอะไรสักอย่าง(); ข้อยกเว้น // แต่ฉันควรเขียนอะไรที่นี่ สิ้นสุดความพยายาม; กระทำธุรกรรม();

เยี่ยม เราตรวจพบข้อผิดพลาดที่เกิดขึ้น แต่เราควรทำอย่างไรกับมัน เขียนข้อความลงบันทึก? บางทีถ้ารหัสบันทึกข้อผิดพลาดควรอยู่ในระดับนี้ทุกประการและเรากำลังรอข้อผิดพลาดอยู่ที่นี่ และถ้าไม่? จะเกิดอะไรขึ้นถ้าเราไม่คาดหวังข้อผิดพลาดใดๆ ที่นี่ จากนั้นเราควรส่งข้อยกเว้นนั้นออกไปและปล่อยให้สถาปัตยกรรมอีกชั้นหนึ่งจัดการกับมัน ซึ่งทำได้โดยใช้ตัวดำเนินการ "CauseException" โดยไม่มีอาร์กิวเมนต์ ใน Javascript ของคุณ การดำเนินการนี้ทำในลักษณะเดียวกันกับตัวดำเนินการ Throw


เริ่มธุรกรรม(); ลองทำอะไรสักอย่าง(); ข้อยกเว้น ThrowException; สิ้นสุดความพยายาม; กระทำธุรกรรม();

ดังนั้น เดี๋ยวก่อน... ถ้าเราโยนข้อยกเว้นออกไปอีก แล้วเหตุใดจึงต้องมีความพยายามเลย? นี่คือเหตุผล: กฎบังคับให้เรารับรองว่าธุรกรรมที่เราเริ่มต้นจะเสร็จสมบูรณ์


เริ่มธุรกรรม(); ลองทำอะไรสักอย่าง(); ข้อยกเว้นยกเลิกธุรกรรม(); โยนข้อยกเว้น; สิ้นสุดความพยายาม; กระทำธุรกรรม();

ตอนนี้ดูเหมือนว่าจะสวยงาม อย่างไรก็ตาม เราจำได้ว่าเราไม่ไว้ใจโค้ด Do Something() จะเกิดอะไรขึ้นถ้าผู้เขียนภายในไม่ได้อ่านบทความนี้และไม่รู้วิธีทำงานกับธุรกรรม? จะเกิดอะไรขึ้นถ้าเขาไปที่นั่นและเรียกวิธี CancelTransaction หรือในทางกลับกัน ยืนยันมัน? มันสำคัญมากสำหรับเราสิ่งนั้น ตัวจัดการข้อยกเว้นไม่ได้เกิดข้อยกเว้นใหม่มิฉะนั้นข้อผิดพลาดเดิมจะหายไปและการแก้ไขปัญหาจะเป็นไปไม่ได้ และเราจำได้ว่าเมธอด Commit และ Cancel สามารถส่งข้อยกเว้นได้หากไม่มีธุรกรรมอยู่ นี่คือจุดที่เมธอด TransactionActive มีประโยชน์

รุ่นสุดท้าย

สุดท้ายนี้ เราสามารถเขียนโค้ดเวอร์ชัน "ปลอดภัยในการทำธุรกรรม" ที่ถูกต้องได้ เขาอยู่ที่นี่:


**UPD: ความคิดเห็นแนะนำตัวเลือกที่ปลอดภัยกว่าเมื่อ CommitTransaction อยู่ภายในบล็อกความพยายาม ตัวเลือกเฉพาะนี้แสดงไว้ที่นี่ ก่อนหน้านี้ Fixation ตั้งอยู่หลังบล็อก Attempt-Exception


เริ่มธุรกรรม(); ลองทำอะไรสักอย่าง(); กระทำธุรกรรม(); ข้อยกเว้นหาก TransactionIsActive() แล้ว CancelTransaction(); สิ้นสุดถ้า; โยนข้อยกเว้น; สิ้นสุดความพยายาม;

เดี๋ยวก่อน ไม่เพียงแต่ “ยกเลิกธุรกรรม” เท่านั้นที่สามารถสร้างข้อผิดพลาดได้ เหตุใด "CommitTransaction" จึงไม่อยู่ในเงื่อนไขเดียวกันกับ "TransactionActive" อีกครั้งโดยใช้กฎเดียวกัน: รหัสที่เริ่มต้นธุรกรรมควรรับผิดชอบในการทำให้เสร็จสมบูรณ์ธุรกรรมของเราไม่จำเป็นต้องเป็นธุรกรรมแรกๆ เสมอไป แต่สามารถซ้อนกันได้ ในระดับนามธรรมของเรา เราจำเป็นต้องใส่ใจเกี่ยวกับธุรกรรมของเราเท่านั้น คนอื่นทั้งหมดไม่ควรสนใจเรา พวกเขาเป็นคนแปลกหน้า เราไม่ควรรับผิดชอบต่อพวกเขา ไม่ควรอย่างยิ่ง ไม่ควรพยายามกำหนดระดับตัวนับธุรกรรมจริง สิ่งนี้จะทำลายการห่อหุ้มอีกครั้งและนำไปสู่ตรรกะการจัดการธุรกรรมที่ "เปื้อน" เราตรวจสอบกิจกรรมในตัวจัดการข้อยกเว้นเท่านั้น และเพื่อให้แน่ใจว่าตัวจัดการของเราเท่านั้น จะไม่สร้างข้อยกเว้นใหม่ "ซ่อน" อันเก่า.

รายการตรวจสอบการปรับโครงสร้างใหม่

มาดูสถานการณ์ทั่วไปบางส่วนที่ต้องมีการแทรกแซงโค้ดกัน


ลวดลาย:


เริ่มธุรกรรม(); ทำอะไรสักอย่าง(); กระทำธุรกรรม();

ห่อหุ้มไว้ในการออกแบบที่ "ปลอดภัย" พร้อมความพยายาม รักษาชีวิต และโยนข้อยกเว้น


ลวดลาย:


ถ้า NotTransactionActive() จากนั้น StartTransaction()EndIf

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


ตัวเลือกที่คล้ายกันโดยประมาณ:


หากธุรกรรม Active() ดังนั้น CommitTransaction() EndIf

ในทำนองเดียวกัน: การทำธุรกรรมตามเงื่อนไขเป็นเรื่องแปลก ทำไมถึงมีเงื่อนไขที่นี่? อะไร คนอื่นสามารถบันทึกธุรกรรมนี้ไปแล้ว? เหตุผลในการพิจารณาคดี


ลวดลาย:


StartTransaction() ในขณะที่ Select.Next() Loop // อ่านวัตถุโดยการอ้างอิง // เขียนวัตถุ EndCycle; กระทำธุรกรรม();
  1. แนะนำการล็อคแบบควบคุมเพื่อหลีกเลี่ยงการหยุดชะงัก
  2. เข้าสู่การโทรไปยังวิธีการบล็อก
  3. ห่อด้วย "ลอง" ตามที่แสดงด้านบน

ลวดลาย:


StartTransaction() ในขณะที่ Select.Next() พยายามวนซ้ำ Object.Write(); รายงานข้อยกเว้น ("ไม่สามารถเขียนได้"); สิ้นสุดความพยายาม; สิ้นสุดรอบ; กระทำธุรกรรม();

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

ในที่สุด

อย่างที่คุณอาจเดาได้แล้ว ฉันเป็นหนึ่งในคนที่ชื่นชอบแพลตฟอร์ม 1C และการพัฒนาบนมัน แน่นอนว่ามีข้อร้องเรียนเกี่ยวกับแพลตฟอร์มนี้โดยเฉพาะอย่างยิ่งในสภาพแวดล้อม Highload แต่โดยทั่วไปแล้วจะช่วยให้คุณสามารถพัฒนาแอปพลิเคชันระดับองค์กรคุณภาพสูงมากได้ในราคาไม่แพงและรวดเร็ว นำเสนอ ORM, GUI, เว็บอินเตอร์เฟส, การรายงาน และอื่นๆ อีกมากมายตั้งแต่แกะกล่อง ในความคิดเห็นเกี่ยวกับHabréพวกเขามักจะเขียนสิ่งที่หยิ่งยโสดังนั้นผู้ชาย - ปัญหาหลักของ 1C ในฐานะระบบนิเวศไม่ใช่แพลตฟอร์มหรือผู้ขาย นี่เป็นเกณฑ์ที่ต่ำเกินไปสำหรับการเข้าร่วม ซึ่งทำให้ผู้คนสามารถเข้าสู่อุตสาหกรรมที่ไม่เข้าใจว่าคอมพิวเตอร์ ฐานข้อมูล ไคลเอนต์-เซิร์ฟเวอร์ เครือข่าย และอื่นๆ คืออะไร 1C ทำให้การพัฒนาแอปพลิเคชันระดับองค์กรง่ายเกินไป ภายใน 20 นาที ฉันสามารถเขียนระบบบัญชีสำหรับการซื้อ/การขายด้วยรายงานที่ยืดหยุ่นและเว็บไคลเอ็นต์ได้ หลังจากนี้ มันง่ายสำหรับฉันที่จะคิดกับตัวเองว่าในขอบเขตที่ใหญ่ขึ้น คุณสามารถเขียนในลักษณะเดียวกันได้มาก 1C จะทำทุกอย่างเป็นการภายในฉันไม่รู้จะทำอย่างไร แต่ก็น่าจะทำได้ ขอผมเขียนว่า "StartTransaction()"....

เพิ่มแท็ก