มีประโยชน์จริง ๆ กับ Repository หรือไม่?


28

กำลังอ่านบทความบางอย่างเกี่ยวกับข้อดีของการสร้างแหล่งเก็บข้อมูลทั่วไปสำหรับแอปใหม่ ( ตัวอย่าง ) แนวคิดนี้ดูดีเพราะให้ฉันใช้พื้นที่เก็บข้อมูลเดียวกันเพื่อทำสิ่งต่าง ๆ สำหรับเอนทิตีที่แตกต่างกันหลายรายการพร้อมกัน:

IRepository repo = new EfRepository(); // Would normally pass through IOC into constructor 
var c1 = new Country() { Name = "United States", CountryCode = "US" };
var c2 = new Country() { Name = "Canada", CountryCode = "CA" };
var c3 = new Country() { Name = "Mexico", CountryCode = "MX" };
var p1 = new Province() { Country = c1, Name = "Alabama", Abbreviation = "AL" };
var p2 = new Province() { Country = c1, Name = "Alaska", Abbreviation = "AK" };
var p3 = new Province() { Country = c2, Name = "Alberta", Abbreviation = "AB" };
repo.Add<Country>(c1);
repo.Add<Country>(c2);
repo.Add<Country>(c3);
repo.Add<Province>(p1);
repo.Add<Province>(p2);
repo.Add<Province>(p3);
repo.Save();

อย่างไรก็ตามการใช้งานส่วนที่เหลือของพื้นที่เก็บข้อมูลมีความเชื่อมั่นอย่างมากใน Linq:

IQueryable<T> Query();
IList<T> Find(Expression<Func<T,bool>> predicate);
T Get(Expression<Func<T,bool>> predicate);
T First(Expression<Func<T,bool>> predicate);
//... and so on

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

ฉันพยายามเขียนRepositoryเวอร์ชันPetaPocoแต่ PetaPoco ไม่รองรับ Linq Expressions ซึ่งทำให้การสร้างอินเทอร์เฟซ IRepository ทั่วไปนั้นค่อนข้างไร้ประโยชน์เว้นแต่คุณจะใช้สำหรับพื้นฐาน GetAll, GetById, เพิ่ม, อัปเดต, ลบและบันทึก วิธีการและใช้มันเป็นคลาสฐาน จากนั้นคุณต้องสร้างแหล่งเก็บข้อมูลเฉพาะด้วยวิธีการเฉพาะเพื่อจัดการคำสั่ง "ที่" ทั้งหมดที่ฉันสามารถส่งผ่านเป็นเพรดิเคตได้

รูปแบบ Generic Repository มีประโยชน์สำหรับทุกอย่างที่อยู่นอก Entity Framework หรือไม่? ถ้าไม่ใช่ทำไมบางคนถึงใช้มันแทนการทำงานโดยตรงกับ Entity Framework?


ลิงค์เดิมไม่ได้สะท้อนรูปแบบที่ฉันใช้ในโค้ดตัวอย่างของฉัน นี่คือ ( ลิงก์ที่อัปเดต )


3
บางสิ่งที่น่าสนใจayende.com/blog/3955/repository-is-the-new-singleton
kenny

1
คำถามเกี่ยวกับ "advatange ของ repository ทั่วไป" จริงๆหรือคือมากกว่า "วิธีซ่อนเคียวรีที่ซับซ้อนหลังอินเทอร์เฟซ repository ทั่วไป" หรือไม่? เป็นไปได้ไหมถ้าอินเตอร์เฟสและการใช้งานขึ้นอยู่กับ linq? Repositoryapi ของเรามีวิธี QueryByExample ที่เป็นอิสระจากเทคนิคการค้นหาอย่างสมบูรณ์และจะอนุญาตให้เปลี่ยนการใช้งาน
k3b

"มันช่วยให้ฉันใช้พื้นที่เก็บข้อมูลเดียวกันเพื่อทำสิ่งต่าง ๆ สำหรับเอนทิตีที่แตกต่างกันหลายประเภท" => เป็นฉันหรือคุณแค่เข้าใจว่าเป็นที่เก็บข้อมูลทั่วไป (รวมถึงบทความที่กล่าวถึง) ให้ฉันเก็บข้อมูลทั่วไปมีความหมายเสมอ templating Repos ทั้งหมดด้วยระดับหรืออินเตอร์เฟซไม่ได้มีพื้นที่เก็บข้อมูลเดียวเช่นการจัดการความคงทนของหน่วยงานทั้งหมดสิ่งที่ประเภทของพวกเขา ...
guillaume31

คำตอบ:


35

พื้นที่เก็บข้อมูลทั่วไปนั้นไร้ประโยชน์ (และ IMHO ก็ไม่ดีเช่นกัน) สำหรับ Entity Framework มันไม่ได้นำคุณค่าเพิ่มเติมใด ๆ มาให้กับสิ่งที่มีอยู่แล้วโดยIDbSet<T>(ซึ่งเป็น btw. repository ทั่วไป)

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

อาร์กิวเมนต์ทั่วไปอันดับที่สองเกี่ยวกับการทดสอบหน่วยที่ง่ายขึ้นก็เป็นสิ่งที่ผิดเช่นกันเพราะการเยาะเย้ยพื้นที่เก็บข้อมูล / ตั้งค่าด้วยหน่วยความจำในที่เก็บข้อมูลแทนที่ผู้ให้บริการ Linq ด้วยอีกคนหนึ่งซึ่งมีความสามารถแตกต่างกัน ผู้ให้บริการ Linq-to-entity รองรับคุณสมบัติย่อยของ Linq เพียงอย่างเดียว - แม้จะไม่สนับสนุนวิธีการทั้งหมดที่มีในIQueryable<T>ส่วนต่อประสาน การแบ่งทรีนิพจน์ระหว่าง data access layer และ business logic layer ป้องกันการแกล้งข้อมูล access layer โดยต้องแยกตรรกะการสืบค้นออก

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


เพียงแค่คำถามสั้น ๆ NHibernate ISessionก็สามารถใช้เพื่อวัตถุประสงค์ในการทดสอบหน่วยทั่วไปได้อย่างง่ายดายและฉันก็ยินดีที่จะทิ้ง 'repository' เมื่อทำงานกับ EF ด้วย - แต่มีวิธีที่ง่ายกว่าและตรงไปตรงมาในการสร้างมันขึ้นมาใหม่? สิ่งที่คล้ายกับISessionและISessionFactory'ทำให้ไม่มีIDbContextเท่าที่ผมสามารถบอกได้ ...
Patryk Ćwiek

ไม่มีไม่มีIDbContext- หากคุณต้องการIDbContextคุณสามารถสร้างและนำไปใช้กับบริบทที่ได้รับ
Ladislav Mrnka

ดังนั้นคุณจึงบอกว่าสำหรับแอป MVC ทุกวัน IDbSet <T> ควรจะมีที่เก็บข้อมูลทั่วไปที่ดีพอที่จะลืมรูปแบบของที่เก็บได้อย่างสมบูรณ์ ยกเว้นสถานการณ์พิเศษเช่นที่คุณไม่ได้รับอนุญาตให้อ้างอิง DAL ใด ๆ ในโครงการ MVC ของคุณ
ProfK

1
คำตอบที่ดี. นักพัฒนาบางคนสร้าง abstractions อีกชั้นโดยไม่มีเหตุผล IMO, EF ไม่ต้องการพื้นที่เก็บข้อมูลทั่วไปหรือหน่วยงาน (เป็นแบบ build-in)
Ashraf

1
IDbSet <T> เป็นพื้นที่เก็บข้อมูลทั่วไปที่มีการพึ่งพา Entity Framework ซึ่งชนิดของการเอาชนะวัตถุประสงค์ของการใช้พื้นที่เก็บข้อมูลทั่วไปเพื่อนามธรรมออกไปพึ่งพาการพึ่งพา Entity Framework
Joel McBeth

7

ปัญหาไม่ใช่รูปแบบที่เก็บ การมีนามธรรมระหว่างการรับข้อมูลและวิธีการที่คุณได้รับมันเป็นสิ่งที่ดี

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

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


ดังนั้นจึงเหมาะสมกว่าที่จะมี Repository ต่อวัตถุข้อมูลหรือกลุ่มของวัตถุที่เกี่ยวข้องอย่างแน่นหนาด้วยวิธีการเฉพาะเช่น GetById (int id), SortedList () ฯลฯ ? ฉันจะส่งคืนรายการของวัตถุข้อมูลจากที่เก็บหรือเปลี่ยนเป็นวัตถุทางธุรกิจด้วยเขตข้อมูลที่จำเป็นด้วยหรือไม่ คินด้าคิดว่านั่นเป็นสิ่งที่เกิดขึ้นในชั้นบริการ / ธุรกิจ
Sam

1
@sam - ฉันมักจะชอบที่เก็บที่เฉพาะเจาะจงมากกว่านี้ใช่ หากพวกเขากำลังทำการแปลหรือไม่ขึ้นอยู่กับสิ่งที่มีแนวโน้มที่จะเปลี่ยนแปลง หากโดเมนของคุณได้รับการกำหนดอย่างดีฉันจะคืนสิ่งต่าง ๆ ในรูปแบบของโดเมน ถ้าข้อมูลถูกกำหนดอย่างดีฉันจะคืนสิ่งต่าง ๆ ในรูปของโครงสร้างข้อมูล ถ้าไม่ใช่ฉันก็มีองค์กรที่ทำหน้าที่เป็นพื้นฐานที่กำหนดไว้อย่างดีในการสร้างและปรับให้เข้ากับ / จากนั้น
Telastyn

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

@EricKing ฉันมีเหมือนกันและมันก็ดี แต่มันก็มีแนวโน้มที่จะรั่วไหลบางสิ่งที่เป็นนามธรรมเนื่องจากสิ่งทั่วไปมีแนวโน้มที่จะมีอยู่เพียงเพราะสามัญของวิธีการจัดเก็บข้อมูล (GetByID เช่นต้องใช้ตารางที่มีรหัส)
Telastyn

@Telastyn ใช่ฉันจะเห็นด้วยกับที่ แต่มันเกิดขึ้นกับที่เก็บเฉพาะเช่นกัน abstractions ที่รั่วไหลไม่ได้เฉพาะเจาะจงกับที่เก็บทั่วไป (ว้าวฉันจะใช้ถ้อยคำที่งุ่มง่ามอีกหรือไม่?)
Eric King

2

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

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

วิธีที่มีประสิทธิภาพที่สุดในการสร้างชั้นข้อมูลทั่วไปคือการทราบแหล่งข้อมูลประเภทต่าง ๆ ที่แอปพลิเคชันจะใช้ล่วงหน้า อย่างที่คุณเห็นสมมติว่า LINQ หรือ SQL นั้นเป็นเรื่องทั่วไปอาจเป็นปัญหาได้ การพยายามติดตั้งเพิ่มเติมที่เก็บข้อมูลใหม่อาจส่งผลให้เกิดการเขียนใหม่

[แก้ไข: เพิ่มรายการต่อไปนี้]

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


1

การมีเลเยอร์โค้ดด้านบนฐานข้อมูลนั้นคุ้มค่าในเกือบทุกกรณี ฉันมักจะชอบรูปแบบ "GetByXXXXX" ในรหัสดังกล่าว - มันช่วยให้คุณปรับการค้นหาตามความต้องการได้ตามต้องการในขณะที่ทำให้ UI นั้นปราศจากอินเตอร์เฟซข้อมูลที่ยุ่งเหยิง

การใช้ประโยชน์จากยาชื่อสามัญเป็นเกมที่ยุติธรรมอย่างแน่นอน - การมีLoad<T>(int id)วิธีการที่เหมาะสม แต่การสร้างคลังเก็บรอบ ๆ LINQ นั้นเทียบเท่ากับการลดคำสั่ง sql ลงทุกหนทุกแห่งในปี 2010 ด้วยความปลอดภัยที่เพิ่มขึ้นเล็กน้อย


0

ด้วยลิงก์ที่มีให้ฉันเห็นว่ามันอาจเป็น wrapper ที่มีประโยชน์สำหรับ a DataServiceContextแต่ไม่ลดการปรับแต่งโค้ดหรือปรับปรุงความสามารถในการอ่าน นอกจากนี้การเข้าถึงDataServiceQuery<T>ถูกกีดขวาง จำกัด ความยืดหยุ่นในการและ.Where() .Single()หรือมีAddRange()ทางเลือกให้ และไม่ได้Delete(Predicate)จัดเตรียมไว้ซึ่งอาจมีประโยชน์ ( repo.Delete<Person>( p => p.Name=="Joe" );เพื่อลบ Joe-s) เป็นต้น

สรุป: API ดังกล่าวขัดขวาง API ดั้งเดิมและ จำกัด การใช้งานง่ายๆ


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

อัปเดตลิงก์แล้วที่ด้านล่างของคำถาม
Sam

-1

ฉันจะพูดว่า "ใช่" Generic Repository ที่ได้รับการพัฒนามาเป็นอย่างดีขึ้นอยู่กับรุ่นของข้อมูลของคุณสามารถทำให้ data access level leaner, neater และมีประสิทธิภาพมากขึ้น

อ่านบทความชุดนี้ (โดย@ chris-pratt ):

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.