سؤال

أنا أتساءل كيف يمكن التعامل بشكل صحيح حريصة التحميل المشكلة المعقدة كائن البيانية عند استخدام نمط مستودع.هذا ليس ORM المشكلة بالتحديد أعتقد.

المحاولة الأولى:

public interface IProductRepository : IRepository<Product>
{
  Product GetById(int id);
  IProductRepository WithCustomers();
}

هذا من شأنه أن تعمل بشكل جيد ، ولكن من شأنها أن تنطوي على تكرار نفسي في كل وقت (كتابة مخصص 'مع' أساليب في مستودع تطبيقات في كل مكان).

النهج القادم:

public interface IRepository<T> where T : IAggregateRoot
{
  ...
  void With(Expression<Func<T, object>> propToExpand);
}

With طريقة إضافة عنصر إلى مجموعة خاصة والتي سيتم استخدامها في وقت لاحق لمعرفة ما الدعائم يجب أن تكون حريصة تحميلها عند استرداد اللازمة الجهة/الجهات.

هذا النوع يعمل على ما يرام.ولكن لم يعجبني الاستخدام:

productRepository.With(x=>x.Customer);
productRepository.With(x=>x.Price);
productRepository.With(x=>x.Manufacturer);
var product = productRepository.GetById(id);

في الأساس - المشكلة هي أن ليس هناك تسلسل.كنت أود أن يكون مثل هذا:

var product = productRepository
  .With(x=>x.Customer)
  .With(x=>x.Price)
  .With(x=>x.Manufacturer)
  .GetById(id);

لم أتمكن من تحقيق هذا.حتى لو لست متأكدا إذا كان هذا الحل من شأنه أن تكون أنيقة.

وهذا يؤدي إلى الأفكار التي أنا في عداد المفقودين شيء أساسي (عدم وجود أمثلة في أي مكان).هناك طرق مختلفة في كيفية التعامل مع هذا ؟ ما هي أفضل الممارسات ؟

هل كانت مفيدة؟

المحلول

مشكلة مثيرة للاهتمام وأنا متأكد من أن كنت لا تواجه مشكلة مع هذا (أنا absolutelty يكون).

بالنسبة لي فإن السؤال الحقيقي هو:أين تريد أن تضع حريصة التحميل المنطق ؟

خارج مستودع في رمز العميل

var product = productRepository
.With(x=>x.Customer)
.With(x=>x.Price)
.With(x=>x.Manufacturer)
.GetById(id);

أنا لا أعتقد أن هذا أمر جيد تصميم البرمجيات:يبدو أن هذا يمكن أن تسبب "الموت عن طريق ألف تخفيضات" إن هذه التركيبات هي منتشرة خلال التطبيق كله.

أو داخل مستودع.على سبيل المثال:

interface IProductRepository {
    Product GetById(int id);
    Product GetByIdWithCustomers(int i);
}

لذلك رمز العميل الخاص بك سوف تبدو مثل هذا:

var product = productRepository.GetByIdWithCustomers(id);

عادة أقوم واحد BaseRepository التي قد الأساسية فقط عمليات الخام تعريف:

public class BaseRepository<TEntity, TPrimaryKey> {
    public void Save(TEntity entity) { ... }
    public void Delete(TEntity entity) { ... }
    public TEntity Load(TPrimaryKey id) { ... } // just gets the entity by primary key
}

ثم تمتد هذه الفئة الأساسية / واجهة من أجل توفير أساليب محددة من أجل جلب كائنات المجال.النهج الخاص بك يبدو أن يذهب إلى حد ما مشابهة الاتجاه.

public class MediaRepository : BaseRepository<Media, int> {
    public long CountMediaWithCategories() { ... }
    public IList<Media> MediaInCategories(IList<Category> categories) { .... }
}

الشيء الجيد:كل ORM الاشياء (حريصة التحميل التكوين ، جلب عمق الخ) يتم تغليف في مستودع الدرجة رمز العميل يحصل فقط مجموعة النتائج.

حاولت العمل مع عامة جدا مستودعات وكأنها تحاول أن تفعل, ولكن أنا في الغالب انتهى من كتابة استفسارات محددة و مستودعات نطاقي الكائنات.

نصائح أخرى

var product = productRepository
 .With(x=>x.Customer)
 .With(x=>x.Price)
 .With(x=>x.Manufacturer)
 .GetById(id);

وأستطيع أن أفهم رغبة لتحديد عمق الاستعلام من الرسم البياني الكائن على غرار ما سبق ولكن أعتقد قد يكون هناك طريقة أسهل للقيام بذلك. ماذا عن بدلا من اختيار لإرجاع المنتج (مع العملاء والسعر والشركة المصنعة) التي ID ط العودة بكل بساطة هذا المنتج - وجميع تلك الأشياء الأخرى هي خصائص تحميل كسول من المنتج

وأنا تحقيق ذلك "الوصول الرسم البياني الكامل" من قبل "تسلسل" من قبل طراز كائن POCO في بلدي طبقة الوصول إلى البيانات. بهذه الطريقة لا أحتاج لمعرفة كمية البيانات المحملة حريصة على الانسحاب في وقت واحد، أنا فقط أسأل لماذا أحتاج من الرسم البياني الكائن، ونموذج يعرف ماذا يتم تحميل وما يحتاج تتعافى بالإضافة إلى ذلك من DAL. نلقي نظرة على الموقع هذه <لأ href =" https://stackoverflow.com/questions/1614396/prefetching-data-in-with-linq-to-sql-ioc-and-repository-pattern/ 1695618 # 1695618 "> href="https://stackoverflow.com/questions/1669607/is-this-repository-pattern-efficient-with-linq-to-sql/1695561#1695561"> إجابات - أحاول أن أشرح وجهة نظري هناك. إذا كنت بحاجة الى مزيد من التوضيح واسمحوا لي أن أعرف وأنا سوف تعديل هذه الإجابة.

وانها مسألة قديمة ولكن ربما يمكن أن يساعد شخص ما. قضيت بعض الوقت لإيجاد aproach جيد، وهنا هو ما وجدتها في C #:

وIRepository.cs:

public interface IRepository<TEntity> where TEntity : class
{
    IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> where
                              , params Expression<Func<TEntity, object>>[] properties);
}

وRepository.cs

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{    
    private readonly DbSet<TEntity> _dbset;

    public Repository(DbSet<TEntity> dbset)
    {
        _dbset = dbset;
    }

    public virtual IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> where
                              , Expression<Func<TEntity, object>>[] properties)
    {
        if (where == null) 
            throw new ArgumentNullException(nameof(where));    
        if (properties == null) 
            throw new ArgumentNullException(nameof(properties));

        var query = _dbset as IQueryable<TEntity>; // _dbSet = dbContext.Set<TEntity>()

        query = properties
                   .Aggregate(query, (current, property) => current.Include(property));

        return query.AsNoTracking().Where(where).ToList();
    }
}

وكيفية استخدام:

var repository = new Repository<User>();
var users = repository.GetAll(p => p.Id == 1, d => d.Address, d => d.Carts);

المرجع: رابط

أستطيع أن أقدر ما كنت تحاول القيام به ، ولكن كنت إلى حد ما بعد الأساسي مستودع-نمط.

الحد الأدنى من مستودع واجهة قد تشمل أساليب:

  • GetById
  • إضافة
  • إزالة

إذا قمت بإضافة طرق إضافية على أعلى من ذلك ، يمكنك بدء تشغيل في الحالات التي واجهة ليس بالضرورة معنى من إجمالي الجذور.

في بعض الأحيان انها مجرد ليس ممكنا أن يكون جميلة تماما API.إذا ما كنت قد يعمل "جيدة بما فيه الكفاية" بالنسبة لك, وأود أن تذهب معها.إذا كنت بحاجة للحصول على بعيدا عن نمط مستودع لتوفير أفضل API برنامج ضد تفعل ذلك!

نمط مستودع ليس كل / في نهاية كل حل.في بعض الأحيان تحتاج حلا مختلفا.

إذا كنت تريد أن تشير إلى كل من يشمل تحتاج خارج المخزون الخاص بك، يمكنك سرد بارامس اختياري (C #) لكل أسلوب عام:

TEntity Find(Func<TEntity, bool> expression, params string[] eagerLoads);

وثم على الطبقة العميل الخاص بك:

IProductRepository.Find(x => x.Id == id, "Customer", "Price")

إذا كنت تريد أن تكون نوع آمن، تعداد الكيانات الخاص بك:

public enum BusinessEntities { Customer, Price, Manufacturer }

IProductRepository.Find(x => x.Id == id, BusinessEntities.Customer.ToString(), BusinessEntities.Price.ToString())

وأعتقد أنه من مسؤولية العميل لطرح خصيصا لما تريد. عام مستودع ينبغي أن مجرد التعامل مع CRUD الأساسي.

في لBaseRepository.cs يمكنك إنشاء هذا الأسلوب:

public async Task<IEnumerable<T>> GetWithChild(string child)
{
    return await _entities.Include(child).ToListAsync();
}

في API بلدي لقد نفذت أيضا طبقة الخدمة ولكن من API I ببساطة استدعاء هذا الأسلوب وتمريرها اسم المتغير ليتم تحميلها.

ومن الواضح في الوضع الخاص بك، فإنك سوف تحتاج إلى تضمين عدد قليل من أكثر السلاسل.

ونشرت لي جوابا في وقت سابق ولكن ما زلت لم يكن راضيا عن الحل. حتى هنا هو أفضل حل.

وفي BaseRepository.cs

public async Task<IEnumerable<T>> GetAll(params Expression<Func<T, object>>[] properties)
{
      IQueryable<T> query = _entities;

      query = properties.Aggregate(query, (current, property) => current.Include(property));

      return await query.AsNoTracking().ToListAsync();
}

ويمكنك ببساطة استخدام الأسلوب كما يلي

await _service.GetAll(x => x.Customer, x => x.Price, x => x.Manufacturer); 
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top