Frage

Ich bin in den Prozess meine ASP.NET MVC-Anwendung entwerfen und ich lief über ein paar interessante Gedanken.

Viele Proben Ich habe gesehen, beschreiben und verwenden Sie das Repository-Muster (IRepository), so ist dies die Art, wie ich es tat, während ich MVC lernte.

Jetzt weiß ich, was es alles zu tun, ich in meinem aktuellen Design und Wundern suchen beginnen, wenn es der beste Weg zu gehen.

Zur Zeit habe ich eine grundlegende IUserRepository, die Methoden wie FindById() definiert, SaveChanges(), etc.

Zur Zeit, wenn ich an die Lade / Abfrage der Benutzertabelle in der DB will, das tue ich etwas entlang der Linien der folgenden Optionen:

    private IUserRepository Repository;

    public UserController()
        : this(new UserRepository())
    { }

    [RequiresAuthentication]
    [AcceptVerbs(HttpVerbs.Get)]
    public ActionResult Edit(string ReturnUrl, string FirstRun)
    {
        var user = Repository.FindById(User.Identity.Name);

        var viewModel = Mapper.Map<User, UserEditViewModel>(user);
        viewModel.FirstRun = FirstRun == "1" ? true : false;

        return View("Edit", viewModel);
    }

    [AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken(Salt = "SaltAndPepper")]
    public ActionResult Edit(UserEditViewModel viewModel, string ReturnUrl)
    {
        //Map the ViewModel to the Model
        var user = Repository.FindById(User.Identity.Name);

        //Map changes to the user
        Mapper.Map<UserEditViewModel, User>(viewModel, user);

        //Save the DB changes
        Repository.SaveChanges();

        if (!string.IsNullOrEmpty(ReturnUrl))
            return Redirect(ReturnUrl);
        else
            return RedirectToAction("Index", "User");
    }

Jetzt verstehe ich nicht ganz, wie MVC in Bezug funktioniert eine Steuerung zu erstellen, wenn ein Benutzer auf einen Link erstellt (nicht sicher, ob es einen Controller pro Benutzer oder 1-Controller pro Anwendung), so ist ich nicht positiv von der beste Maßnahme.

fand ich eine große Frage in Bezug auf die Verwendung eines generischen Repository-Schnittstelle IRepository<T> hier und haben auch die Idee eines statischen RepositoryFactory auf einer Reihe von Blogs scheinen. Grundsätzlich nur 1 Instanz des Repository gehalten wird immer und es wird über diese Fabrik erhalten

Also meine Frage dreht sich um, wie die Menschen es tun dort apps, und was als gute Praxis.

Do Menschen haben individuelle repositorys basierend auf jedem Tisch (IUserRepository)?
Haben sie eine generische IRepository<T> benutzen?
Haben sie eine statische Repository Fabrik benutzen?
Oder etwas anderes ganz?

EDIT: Ich habe gerade realisiert, ich sollte wohl auch fragen:

Ist mit einem privaten IRepository auf jedem Controller ein guter Weg zu gehen? oder soll ich instanziiert ein neues IRepository jedes Mal, wenn ich will, es benutzen?

BOUNTY EDIT: Ich bin eine Prämie beginnen einige weitere Perspektiven zu bekommen (nicht, dass Tims nicht hilfreich war).

Ich bin neugierig zu wissen, was die Menschen in ihrem MVC apps tun oder was sie denken, ist eine gute Idee.

War es hilfreich?

Lösung

Einige sehr offensichtliche Probleme mit der Idee eines generischen IRepository<T>:

  • Es geht davon aus, dass jedes Unternehmen die gleiche Art von Schlüssel verwendet, die in fast alle nicht-trivialen System nicht wahr ist. Einige Unternehmen werden GUIDs verwenden, können andere haben eine Art von natürlichen und / oder zusammengesetzten Schlüssel. NHibernate kann dies recht gut unterstützen, aber Linq to SQL ist ziemlich schlecht es -. Sie haben ziemlich viel hackish Code zu schreiben, automatische Tastenbelegung zu tun

  • Es bedeutet, dass jedes Repository nur mit genau einem Entitätstyp umgehen kann und unterstützt nur die banalsten Operationen. Wenn ein Repository eine so einfache CRUD degradiert wird Wrapper es hat sehr wenig Gebrauch überhaupt. Genauso gut könnte man die Hand nur dem Kunden ein IQueryable<T> oder Table<T>.

  • Es wird davon ausgegangen, dass Sie exakt die gleichen Vorgänge auf jeder Einheit. In Wirklichkeit sehr weit geht dies von der Wahrheit entfernt sein. Sicher, vielleicht Sie wollen, dass Order durch seine ID zu bekommen, aber wahrscheinlicher, wollen Sie eine Liste von Order Objekte für einen bestimmten Kunden zu bekommen und innerhalb eines bestimmten Zeitraums. Die Vorstellung von einer völlig generic IRepository<T> nicht für die Tatsache nicht zulassen, dass Sie fast sicher verschiedene Arten von Abfragen auf verschiedene Arten von Einheiten ausgeführt werden sollen.

Der ganze Sinn des Repository-Musters ist ein Abstraktion über gemeinsame Datenzugriffsmuster zu erstellen. Ich denke, dass einige Programmierer mit dem Erstellen von Repositories langweilig wird, so dass sie sagen: „Hey, ich weiß, ich werde ein über-Repository erstellen, die jeden Entitätstyp umgehen kann!“ Das ist sehr gut, außer, dass das Repository ziemlich nutzlos für 80% ist, was Sie zu tun versuchen. Es ist in Ordnung als Basisklasse / Schnittstelle, aber wenn das der volle Umfang der Arbeit, die Sie tun, dann sind Sie einfach nur faul (und garantiert Zukunft Kopfschmerzen) zu sein.


Im Idealfall könnte ich mit einem generischen Repository, das in etwa so aussieht starten:

public interface IRepository<TKey, TEntity>
{
    TEntity Get(TKey id);
    void Save(TEntity entity);
}

Sie werden feststellen, dass diese nicht ein List oder GetAll Funktion haben - das ist, weil es ist absurd zu glauben, dass es akzeptabel ist, die Daten aus einer ganzen Tabelle abzurufen sofort überall im Code. Dies ist, wenn Sie benötigen, gehen in bestimmten Repositorys zu starten:

public interface IOrderRepository : IRepository<int, Order>
{
    IEnumerable<Order> GetOrdersByCustomer(Guid customerID);
    IPager<Order> GetOrdersByDate(DateTime fromDate, DateTime toDate);
    IPager<Order> GetOrdersByProduct(int productID);
}

Und so weiter - Sie bekommen die Idee. So können wir die „generische“ Repository für, wenn wir jemals wirklich brauchen die unglaublich simpel abrufen-by-id Semantik, aber im Allgemeinen sind wir nie wirklich gehen, die um passieren, schon gar nicht auf eine Controller-Klasse.


Jetzt als für Controller, haben Sie das Recht zu tun, haben Sie sonst so ziemlich die ganze Arbeit zunichte gemacht Sie alle haben nur in der Zusammenstellung der Repositories.

Eine Steuerung Bedürfnisse , um seine Repository von der Außenwelt . Der Grund, warum Sie diese Repositorys erstellt, so dass Sie eine Art von Inversion of Control tun können. Ihr Ziel ist hier aus einem Repository für einen anderen zu tauschen können - z. B. Unit-Tests zu tun, oder wenn Sie Schalter von Linq to SQL Entity Framework zu einem bestimmten Zeitpunkt in der Zukunft entscheiden

Ein Beispiel für dieses Prinzip ist:

public class OrderController : Controller
{
    public OrderController(IOrderRepository orderRepository)
    {
        if (orderRepository == null)
            throw new ArgumentNullException("orderRepository");
        this.OrderRepository = orderRepository;
    }

    public ActionResult List(DateTime fromDate, DateTime toDate) { ... }
    // More actions

    public IOrderRepository OrderRepository { get; set; }
}

Mit anderen Worten: die Controller hat keine Ahnung, wie ein Repository erstellen , noch sollte es. Wenn Sie einen Repository-Bau dort los haben, wodurch es koppelt, dass Sie wirklich nicht wollen. Der Grund, dass die ASP.NET MVC-Controller Beispiel parameterlos Konstrukteure haben, die konkrete Repositorys erstellt ist, dass die Seiten ohne kompilieren und ausführen zu können, müssen Sie zu zwingen, ein ganzes Dependency Injection-Framework einzurichten.

Aber in einer Produktionsstätte, wenn Sie nicht in der Repository-Abhängigkeit durch einen Konstruktor oder öffentliches Eigentum vorbei, dann verschwenden Sie Ihre Zeit Repositorys überhaupt mit, da die Controller sind nach wie vor eng gekoppelt an die Datenbank layer. Sie müssen Schreibtestcode wie diese in der Lage sein:

[TestMethod]
public void Can_add_order()
{
    OrderController controller = new OrderController();
    FakeOrderRepository fakeRepository = new FakeOrderRepository();
    controller.OrderRepository = fakeRepository; //<-- Important!
    controller.SubmitOrder(...);
    Assert.That(fakeRepository.ContainsOrder(...));
}

Sie können dies nicht tun, wenn Ihre OrderController wird ausgeschaltet und sein eigenes Repository zu schaffen. Dieses Testverfahren ist keine Datenzugriff tun soll, es macht Sie sicher, dass der Controller die richtige Repository-Methode basiert auf den Aufruf der Aktion.


Dies ist nicht DI noch wohlgemerkt, das ist nur fälscht / spöttisch. Wo DI ins Bild kommt, wenn Sie entscheiden, dass Linq to SQL ist nicht genug für Sie tun, und Sie wirklich die HQL in NHibernate wollen, aber es wird Sie 3 Monate bis Port alles zu übernehmen, und Sie möchten in der Lage sein tun, um diese ein Repository zu einem Zeitpunkt. So zum Beispiel eines DI-Framework wie Ninject , alles, was Sie tun müssen, ist dies zu ändern:

Bind<ICustomerRepository>().To<LinqToSqlCustomerRepository>();
Bind<IOrderRepository>().To<LinqToSqlOrderRepository>();
Bind<IProductRepository>().To<LinqToSqlProductRepository>();

An:

Bind<ICustomerRepository>().To<LinqToSqlCustomerRepository>();
Bind<IOrderRepository>().To<NHibernateOrderRepository>();
Bind<IProductRepository>().To<NHibernateProductRepository>();

Und Sie, jetzt alles, was auf IOrderRepository hängt die NHibernate Version nutzt, haben Sie nur zu ändern haben eine Zeile Code als potenziell Hunderte von Linien gegenüber. Und wir sich die Linq to SQL und NHibernate Versionen Seite ausgeführt wird, Stück Funktionalität über Stück portieren, ohne jemals etwas in der Mitte ein.


So alle Punkte zusammenfassen ich gemacht habe:

  1. Verlassen Sie sich nicht ausschließlich auf eine generische Schnittstelle IRepository<T>. Die meisten Funktionen, die Sie von einem Repository wollen, ist spezifische , nicht generic . Wenn Sie eine IRepository<T> auf den oberen Ebenen der Klasse / Interface-Hierarchie enthalten sein sollen, ist das in Ordnung, aber Controller hängen von sollten spezifische Repositories, so dass Sie nicht am Ende zu Ihrem Code in 5 verschiedenen ändern Orte, wenn Sie feststellen, dass die generische Repository wichtige Methoden fehlt.

  2. Controller sollte Repositories von außen akzeptieren, nicht ihre eigenen erstellen. Dies ist ein wichtiger Schritt bei der Kopplung zu beseitigen und die Verbesserung der Testbarkeit.

  3. Normalerweise werden Sie die Regler mit einem Dependency Injection-Framework verkabeln wollen, und viele von ihnen können sich nahtlos in ASP.NET MVC integriert werden. Wenn das zu viel für Sie in nehmen, dann auf die Dest sollten Sie eine Art von statischen Service-Provider verwenden, so dass Sie alle Repository-Erzeugungslogik zentralisieren können. (Auf lange Sicht, werden Sie wahrscheinlich finden es einfacher, nur zu lernen und einen DI Framework verwenden).

Andere Tipps

Do Menschen haben individuelle repositorys basierend auf jedem Tisch (IUserRepository)? Ich neige dazu, ein Repository für jedes Aggregat haben, nicht jede Tabelle.

Haben sie verwenden, um eine generische IRepository? wenn möglich, ja

Haben sie eine statische Repository Fabrik verwenden? Ich ziehe die Injektion einer Repository-Instanz über einen IOC-Container

Hier ist, wie ich es bin mit. Ich verwende IRepository für alle Operationen, die an alle meine Repositories gemeinsam sind.

public interface IRepository<T> where T : PersistentObject
{
    T GetById(object id);
    T[] GetAll();
    void Save(T entity);
}

, und ich benutze auch eine dedizierte eine ITRepository für jedes Aggregat für den Betrieb, dass ist zu diesem Repository eindeutig. Zum Beispiel für Benutzer I IUserRepository verwenden Methode hinzuzufügen, die UserRepository verschieden sind:

public interface IUserRepository : IRepository<User>
{
    User GetByUserName(string username);
}

Die Umsetzung wird wie folgt aussehen:

public class UserRepository : RepositoryBase<User>, IUserRepository
{
    public User GetByUserName(string username)
    {
        ISession session = GetSession();
        IQuery query = session.CreateQuery("from User u where u.Username = :username");
        query.SetString("username", username);

        var matchingUser = query.UniqueResult<User>();

        return matchingUser;
    }
}


public class RepositoryBase<T> : IRepository<T> where T : PersistentObject
{
    public virtual T GetById(object id)
    {
        ISession session = GetSession();
        return session.Get<T>(id);
    }

    public virtual T[] GetAll()
    {
        ISession session = GetSession();
        ICriteria criteria = session.CreateCriteria(typeof (T));
        return criteria.List<T>().ToArray();
    }

    protected ISession GetSession()
    {
        return new SessionBuilder().GetSession();
    }

    public virtual void Save(T entity)
    {
        GetSession().SaveOrUpdate(entity);
    }
}

als in dem Usercontroller aussehen wie:

public class UserController : ConventionController
{
    private readonly IUserRepository _repository;
    private readonly ISecurityContext _securityContext;
    private readonly IUserSession _userSession;

    public UserController(IUserRepository repository, ISecurityContext securityContext, IUserSession userSession)
    {
        _repository = repository;
        _securityContext = securityContext;
        _userSession = userSession;
    }
}

als das Repository instanziiert wird mit Dependency Injection Mustern benutzerdefinierten Controller Factory verwenden. Ich verwende StructureMap als meine Abhängigkeit Injektionsschicht.

Die Datenbankschicht ist NHibernate. ISession ist das Tor zur der Datenbank in dieser Sitzung.

Ich schlage vor, Sie zu sehen CodeCampServer Struktur, können Sie eine Menge lernen von ihm.

Ein weiteres Projekt, dass Sie fro lernen kann, ist es Who Can Help Me . Was ich grabe nicht genug in ihm noch nicht.

Do Menschen haben individuelle repositorys basierend auf jedem Tisch (IUserRepository)?

Ja, das war die bessere Wahl aus 2 Gründen:

  • meine DAL basiert auf Linq-to-Sql (aber meine DTOs sind Schnittstellen auf der Basis der LTS Einheiten)
  • die durchgeführten Operationen sind Atom (Hinzufügen ist eine atomare Operation, Speichern ist eine andere, etc.)

Haben sie verwenden, um eine generische IRepository?

Ja, ausschließlich , inspiriert auf DDD Entity / Value Schema I erstellt IRepositoryEntity / IRepositoryValue und ein generisches IRepository für andere Sachen.

Haben sie eine statische Repository Fabrik verwenden?

Ja und nein: Ich verwende einen IOC-Container über eine statische Klasse aufgerufen. Nun ja ... wir können sagen, es ist eine Art Fabrik.

Hinweis : Ich entwarf diese Architektur auf meinem eigenen und ein Kollege von mir fand es so toll, dass wir derzeit unsere ganze Gesellschaft Rahmen dieses Modell zu schaffen (ja, es ist ein junges Unternehmen). Dies ist definitiv etwas wert versuchen, auch wenn ich das Gefühl, dass Rahmenbedingungen dieser Art werden von den großen Akteuren veröffentlicht werden.

Sie können eine hervorragende finden Allgemein Repopsitory Bibliothek, die geschrieben wurde, es zu ermöglichen, als WebForms asp verwendet werden: Object auf Codeplex: MultiTierLinqToSql

Jeder meines Controller verfügt über private Repositories für die Maßnahmen, die sie zur Unterstützung benötigen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top