سؤال

في محاولة لجعل نمط مستودع وطبقة خدمة بسيطة حقًا هنا. (. ملاحظة: هذا مجرد البحث والتطوير.

هدفي هو تقليل مقدار تعريفات الطريقة في طبقة الخدمة الخاصة بي.

هذا هو عقد المستودع الخاص بي:

interface IFooRepository
{
   IEnumerable<Foo> Find();
   void Insert(Foo foo);
   void Update(Foo foo);
   void Delete(Foo foo);
}

لا شيء جديد هناك.

الآن ، إليك ما أحاول أن أحصل عليه في عقد خدمتي:

interface IFooDataService
{
   public IEnumerable<Foo> Find(FooSearchArgs searchArgs);
}

في الأساس ، يحتوي أي "FOO" على العديد من الخصائص (المعرف ، الاسم ، إلخ) ، والتي أود أن أتمكن من البحث عنها.

لذلك ، لا أريد أن أحصل على طريقة تجد 1x لكل خاصية مختلفة ، أريد فقط واحدة - بهذه الطريقة عندما أقوم بإنشاء خصائص إضافية لا أضطر إلى تعديل العقود.

"foosearchargs" هو مجرد poco بسيطة مع جميع خصائص "foo" المختلفة.

لذلك ، هذا ما أحاول فعله ، إليك أسئلتي:

  • هل هذا التصميم السيئ؟ إذا كان الأمر كذلك ، فما هي البدائل؟
  • كيف يمكنني تنفيذ هذا التصفية في طبقة الخدمة؟ هل سيتعين علي التحقق من خصائص "foosearchargs" التي يتم تعيينها ، ثم استمر في التصفية؟ (إذا كان هذا ، ثم Query.here ، إذا كان هذا ، Query.here ، etc) أي شخص لديه فكرة عن طريقة تمديد linq ienumerable الذكية للقيام بذلك؟ (بمعنى آخر repository.WhereMeetsSearchCriteria(fooSearchArgs))

نقدر المساعدة.

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

المحلول

نستخدم شيئًا مشابهًا جدًا. شيء واحد تحتاج إلى اتخاذ قرار بشأنه هو ما إذا كنت ستعرض Iqueryable خارج المستودع. إرجاع طريقة البحث الخاصة بك ienumerable والتي يمكن أن تكون iqueryable التي تم إرجاعها من جملة الخاص بك.

تتمثل ميزة إرجاع Iqueryable في أنه يمكنك تحسين معاييرك خارج طبقة المستودع الخاص بك.

repository.Find(predicate).Where(x => x.SomeValue == 1);

لن يتم تجميع التعبير إلا عند استخدام البيانات التي تم إرجاعها وهنا في الكذب. نظرًا لأنك تضغط فقط على قاعدة البيانات عندما تأتي بالفعل لاستخدام النتائج التي قد ينتهي بها الأمر إلى محاولة الاتصال بقاعدة البيانات بعد جلستك (NHibernate) أو تم إغلاق الاتصالات.

تفضيلي الشخصي هو استخدام نمط المواصفات حيث تقوم بتمرير طريقة البحث الخاصة بك ، ويتم استخدام كائن Ispecification لإجراء الاستعلام.

public interface ISpecification<TCandidate>
{
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source);
}

public class TestSpecification : ISpecification<TestEntity>
{
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source)
    {
        return source.Where(x => x.SomeValue == 2);
    }
}

public class ActiveRecordFooRepository: IFooRepository
{
    ...

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        ...

        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray();

        ...
    }

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First();
    }
}

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

  1. يتم تنفيذ الاستعلامات على الفور ويمنع دعوة إلى قاعدة البيانات التي يتم إجراؤها بعد إغلاق الجلسات.
  2. نظرًا لأن استفساراتك يتم تجميعها الآن في مواصفات ، فهي قابلة للاختبار للوحدة.
  3. المواصفات قابلة لإعادة الاستخدام مما يعني أنه ليس لديك تكرار رمز عند محاولة تشغيل استفسارات مماثلة وأي أخطاء في الاستعلامات تحتاج فقط إلى إصلاح في مكان واحد.
  4. مع النوع الصحيح من التنفيذ ، يمكنك أيضًا ربط مواصفاتك معًا.

repository.Find(
    firstSpecification
        .And(secondSpecification)
        .Or(thirdSpecification)
        .OrderBy(orderBySpecification));

نصائح أخرى

يمر أ Func كمعلمة إلى طريقة البحث عن طبقة الخدمة الخاصة بك ، بدلاً من foosearchargs ، خيار؟ تحتوي العدوى على طريقة (LINQ) التي تأخذ FUNC كمعلمة ، بحيث يمكنك استخدامها لتصفية النتائج.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top