c# - ما هو IRepository وما الذي يتم استخدامه؟




asp.net-mvc vb.net (5)

ما هو IRepository؟ لماذا يتم استخدامه ، وأمثلة وجيزة وبسيطة لن يضر.


Answers

IRepository ليس نوعًا محددًا في إطار .Net. عادة عندما ترى واجهة مسمى بها ، يستخدم البرنامج نمط مستودع التخزين ( https://web.archive.org/web/20110503184234/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/10/08/the-repository-pattern.aspx ). بشكل عام عندما يستخدم الأشخاص هذا النمط ، سيقومون بإنشاء واجهة تتقيد بها جميع المستودعات. هناك العديد من الفوائد للقيام بذلك. بعض الفوائد هي كود de copupling واختبار الوحدة.

ومن الشائع أيضًا أن يتم ذلك حتى يمكن الاستفادة منه مع IoC ( http://en.wikipedia.org/wiki/Inversion_of_control ).


يعزز MVC فصل المخاوف ، ولكن هذا لا يتوقف عند مستوى MVC.

الوصول إلى البيانات هو مصدر قلق في حد ذاته. يجب أن يتم ذلك في M من MVC ، أي النموذج. إن كيفية بناء النموذج الخاص بك أمر متروك لك ، ولكن الناس عادة ما يتبعون أنماطًا مجربة ومختبرة (لماذا إعادة اختراع العجلة؟). نمط السجل هو المعيار الحالي. ومع ذلك ، لا تتوقع صيغة بسيطة ، لأن الاختلافات كثيرة مثل وجود مطورين ، تقريبًا.

IRepository عبارة عن واجهة تقوم بإنشائها (وهي ليست جزءًا من MVC أو ASP.NET أو .NET). إنه يسمح لك "بفصل" مستودعاتك عن التطبيقات الحقيقية. الفصل جيد لأنه يعني رمزك ...:

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

لذلك ، بعد أن باعتك الفصل ، فإن الإجابة على سؤالك هي أن IRepository عبارة عن واجهة تقوم بإنشائها وأنك ترث المستودعات الخاصة بك. يعطيك التسلسل الهرمي للفئة موثوق للعمل معه.

أنا عموما استخدام IREPository عامة. أي:

IRepository

حيث Tentity ، حسنا ، كيان. الكود الذي أستخدمه هو:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Wingspan.Web.Mvc
{
    public interface IRepository<TEntity> where TEntity : class
    {
        List<TEntity> FetchAll();
        IQueryable<TEntity> Query {get;}
        void Add(TEntity entity);
        void Delete(TEntity entity);
        void Save();
    }
}

التنفيذ الفعلي لهذه الواجهة سيكون:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;

using Wingspan.Web.Mvc;

namespace ES.eLearning.Domain
{
    public class SqlRepository<T> : IRepository<T> where T : class
    {
        DataContext db;
        public SqlRepository(DataContext db)
        {
            this.db = db;
        }

        #region IRepository<T> Members

        public IQueryable<T> Query
        {
            get { return db.GetTable<T>(); }
        }

        public List<T> FetchAll()
        {
            return Query.ToList();
        }

        public void Add(T entity)
        {
            db.GetTable<T>().InsertOnSubmit(entity);
        }

        public void Delete(T entity)
        {
            db.GetTable<T>().DeleteOnSubmit(entity);
        }

        public void Save()
        {
            db.SubmitChanges();
        }

        #endregion
    }
}

هذا يسمح لي بالكتابة:

SqlRepository<UserCourse> UserCoursesRepository = new SqlRepository<UserCourse>(db);

حيث db هو مثيل DataContext تم حقنه في ، على سبيل المثال ، خدمة.

مع UserCoursesRepository يمكنني الآن كتابة أساليب في فئة الخدمة الخاصة بي مثل:

public void DeleteUserCourse(int courseId)
        {
            var uc = (UserCoursesRepository.Query.Where(x => x.IdUser == UserId && x.IdCourse == courseId)).Single();
            UserCoursesRepository.Delete(uc);
            UserCoursesRepository.Save();
        }

والآن في وحدات التحكم الخاصة بي ، يمكنني فقط كتابة:

MyService.DeleteUserCourse(5);
MyService.Save();

أي أن تطوير تطبيقك يصبح أكثر من خط تجميع يؤدي إلى وحدة تحكم بسيطة جدًا. يمكن اختبار كل قطعة من خط التجميع بشكل مستقل عن أي شيء آخر ، لذلك يتم إخفاء البق في البرعم.

إذا كانت هذه إجابة طويلة غير منطقية ، فذلك لأن الإجابة الحقيقية هي:

اشتر كتاب ستيفن ساندرسون Pro ASP.NET MVC 2 Framework وتعلم التفكير في MVC.


المستودع هو عبارة عن تجريد يمثل أي مخزن بيانات اعتباطي ومعمد كما لو كان في مجموعة ذاكرة للكائنات .

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

لذا فإن IRepository هو عبارة عن عقد واجهة يحدد كيفية عمل شفرة Api لرمز العميل للتفاعل مع المستودع. غالبًا ما يشتمل هذا على إضافة وتحديث وحذف والحصول على عقود ، على سبيل المثال ، هذا المثال الشائع جدًا لعقد المخزون:

public interface IRepository<TEntity> where TEntity : class
{
    List<TEntity> GetAll();
    void Add(TEntity entity);
    void Delete(TEntity entity);
    void Save();
}

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

أولاً ، لن تستخدم عادةً مستودعًا بنفسك ، فمن المحتمل أنك ستستخدمه مع وحدة نمط العمل ، لذلك لا يجب أن يحتوي المستودع على طريقة Save (). قد يكون لديك طريقة Update(T entity) - ولكن لماذا؟ سيتم تلقائيًا تحديث / تحديث الكائن الذي تتلقاه من المستودع تمامًا مثل أي كائن آخر ستتلقاه من أي نوع من أنواع الكائنات نظرًا لأنك قمت باسترداد المراجع إلى الكائنات نفسها. (على سبيل المثال: إذا كان TEntity الخاص بك هو كائن Person ، وكنت تحصل على شخص "Chuck" ، وقمت بتغيير اسمه الأخير من "Bartowski" إلى "Carmichael" ، فمن المفترض أن يكون المستودع قد تم تحديثه بالفعل. العقل ، لا يوجد شيء خاطئ في تنفيذ طريقة Update(T entity) .)

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

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

public interface IRepository<TEntity> where TEntity : class
{

    List<TEntity> GetAll();
    List<TEntity> Get(Func<TEntity, bool> where);
    void Insert(TEntity entity);
    void Insert(IEnumerable<TEntity> entities);
    void Remove(TEntity entity);
    void Remove(IEnumerable<TEntity> entities);

    void SyncDisconnected(TEntity entity, bool forDeletion = false);
    void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}

إذا قمت بتحديد فئة أساسية لجميع الكيانات الخاصة بك ، دعنا نسميها DomainObject ، DomainObject حقل Id ، فيمكنك القيام بما يلي:

public interface IRepository<TEntity> where TEntity : DomainObject
{
    TEntity GetById(object Id);

    List<TEntity> GetAll();
    List<TEntity> Get(Func<TEntity, bool> where);
    void Insert(TEntity entity);
    void Insert(IEnumerable<TEntity> entities);
    void Remove(TEntity entity);
    void Remove(IEnumerable<TEntity> entities);

    void SyncDisconnected(TEntity entity, bool forDeletion = false);
    void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}

إذا لم تعجبك المعلمة الاختيارية forDeletion ، يمكنك إضافة طريقة تسمح بمزامنة الكائنات المحذوفة أيضًا:

    void SyncDisconnectedForDeletion(TEntity entity);

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

يمكنك تنفيذ هذه الواجهة ضد أي مستودع لأي مخزن بيانات أساسي ، متصل أو غير متصل ، بما في ذلك عمليات تجريد أخرى إلى مخازن البيانات الأساسية مثل Entity Framework.


IRepository عبارة عن واجهة تحددها عندما تريد تطبيق نمط مستودع التخزين. كما ذكرBrian Ball ، إنه ليس جزءًا من .NET. إنه واجهة تقوم بإنشائها.

يوصي المطورون الذين يستخدمون نمط مستودع التخزين على نطاق واسع باستخدام واجهة للتنفيذ. على سبيل المثال ، في التطبيق الذي أقوم بتطويره الآن ، لدي 5 مستودعات. 4 محددة و 1 عام. يرث كل واحد من IRepository مما يضمن أنني لن تواجه مشكلات في الطريق مع وجود اختلافات في عمليات التنفيذ.

بقدر ما أمثلة التعليمات البرمجية ، سأحاول:

interface IRepository<T> where T : class {
    IQueryable<T> Select();
}

تم التنفيذ كمستودع عام:

public class Repository<T> : IRepository<T> where T : class {
    public IQueryable<T> Select() {
        return this.ObjectContext.CreateObjectSet<T>();
    }
}

تم التنفيذ كمستودع متخصص:

public class EmployeeRepository : IRepository<Employee> {
    public IQueryable<Employee> Select() {
        return this.ObjectContext.Employees;
    }
}

يقوم كل من Repository<T> و EmployeeRepository بتنفيذ IRepository ، ومع ذلك يذهبون حول إجراء الاستعلام بشكل مختلف قليلاً. يجب أن يقوم المستودع العام بإنشاء مجموعة كائنات من T قبل محاولة القيام بأي شيء.

ضع في اعتبارك أنه من المفترض أن يكون Repository<T> مقفلاً على الواجهة ، حيث يمكن لتطبيق EmployeeRepository تنفيذ طرق أكثر تخصصًا لتحقيق منطق أكثر تعقيدًا.

آمل أن يكون هذا يساعدك قليلا.


What can you do about it?

There is a lot of good answers here explaining what a null reference is and how to debug it. But there is very little on how to prevent the issue or at least make it easier to catch.

Check arguments

For example, methods can check the different arguments to see if they are null and throw an ArgumentNullException , an exception obviously created for this exact purpose.

The constructor for the ArgumentNullException even takes the name of the parameter and a message as arguments so you can tell the developer exactly what the problem is.

public void DoSomething(MyObject obj) {
    if(obj == null) 
    {
        throw new ArgumentNullException("obj", "Need a reference to obj.");
    }
}

Use Tools

There are also several libraries that can help. "Resharper" for example can provide you with warnings while you are writing code, especially if you use their attribute: NotNullAttribute

There's "Microsoft Code Contracts" where you use syntax like Contract.Requires(obj != null) which gives you runtime and compile checking: Introducing Code Contracts .

There's also "PostSharp" which will allow you to just use attributes like this:

public void DoSometing([NotNull] obj)

By doing that and making PostSharp part of your build process obj will be checked for null at runtime. See: PostSharp null check

Plain Code Solution

Or you can always code your own approach using plain old code. For example here is a struct that you can use to catch null references. It's modeled after the same concept as Nullable<T> :

[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T: class
{
    private T _value;

    public T Value
    {
        get
        {
            if (_value == null)
            {
                throw new Exception("null value not allowed");
            }

            return _value;
        }
        set
        {
            if (value == null)
            {
                throw new Exception("null value not allowed.");
            }

            _value = value;
        }
    }

    public static implicit operator T(NotNull<T> notNullValue)
    {
        return notNullValue.Value;
    }

    public static implicit operator NotNull<T>(T value)
    {
        return new NotNull<T> { Value = value };
    }
}

You would use very similar to the same way you would use Nullable<T> , except with the goal of accomplishing exactly the opposite - to not allow null . وهنا بعض الأمثلة:

NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null

NotNull<T> is implicitly cast to and from T so you can use it just about anywhere you need it. For example, you can pass a Person object to a method that takes a NotNull<Person> :

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull<Person> person)
{
    Console.WriteLine(person.Value.Name);
}

As you can see above as with nullable you would access the underlying value through the Value property. Alternatively, you can use an explicit or implicit cast, you can see an example with the return value below:

Person person = GetPerson();

public static NotNull<Person> GetPerson()
{
    return new Person { Name = "John" };
}

Or you can even use it when the method just returns T (in this case Person ) by doing a cast. For example, the following code would just like the code above:

Person person = (NotNull<Person>)GetPerson();

public static Person GetPerson()
{
    return new Person { Name = "John" };
}

Combine with Extension

Combine NotNull<T> with an extension method and you can cover even more situations. Here is an example of what the extension method can look like:

[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
    public static T NotNull<T>(this T @this) where T: class
    {
        if (@this == null)
        {
            throw new Exception("null value not allowed");
        }

        return @this;
    }
}

And here is an example of how it could be used:

var person = GetPerson().NotNull();

جيثب

For your reference I made the code above available on GitHub, you can find it at:

https://github.com/luisperezphd/NotNull

Related Language Feature

C# 6.0 introduced the "null-conditional operator" that helps with this a little. With this feature, you can reference nested objects and if any one of them is null the whole expression returns null .

This reduces the number of null checks you have to do in some cases. The syntax is to put a question mark before each dot. Take the following code for example:

var address = country?.State?.County?.City;

Imagine that country is an object of type Country that has a property called State and so on. If country , State , County , or City is null then address will be null . Therefore you only have to check whether address is null`.

It's a great feature, but it gives you less information. It doesn't make it obvious which of the 4 is null.

Built-in like Nullable?

C# has a nice shorthand for Nullable<T> , you can make something nullable by putting a question mark after the type like so int? .

It would be nice if C# had something like the NotNull<T> struct above and had a similar shorthand, maybe the exclamation point (!) so that you could write something like: public void WriteName(Person! person) .





c# asp.net-mvc vb.net