ASP.MVC: repozytorium które odzwierciedla IQueryable ale nie Linq do SQL, DDD jak pytac

Chcę utworzyć repozytorium DDD, które zwróci IQueryable encje, które pasują Linq do bazowych klas SQL, bez żadnych relacji. Mogę łatwo zwrócić encje minus relacje z Linq select new {pole, pole,... projekcja. Jak zakodować klasę repozytorium Entity? Jak zwrócić obiekt z repozytorium mający klasę repozytorium, a nie klasę Linq do SQL i nadal wypełnić go wieloma encjami z zaznaczenia Linq? Jak mam się do tego odnieść? powrót klasy w moim ViewModel?

Jestem w tym zupełnie nowy, stąd oczywiste błędy. Czy brakuje mi łodzi i mam zwracać tylko kompletne byty z repozytorium, a nie projekcję? Nadal musiałbym rozebrać relacje Linq do SQL, zanim wyślę je z repozytorium. Jestem całkowicie poza bazą? Naprawdę chcę zachować typ danych IQueryable.

Na przykład Mój kod Linq do SQL w moim repozytorium:

public class MiniProduct
{
    public MiniProduct( string ProductNo, string Name, string Description, double Price)
    {    this.ProductNo = ProductNo;
         this.Name = Name;
         this.Description = Description;
         this.Price = Price;
    }
}

public IQueryable<MiniProduct> GetProductsByCategory( string productCategory)
{
    return ( from p in db.Products
             from c in db.Categories
             from pc in db.ProductCategories
             where c.CategoryName == productCategory &&
                   c.CatID == pc.CatID &&
                   pc.ProductID == p.ProductID
             select new { p.ProductNo, p.Name, p.Description, p.Price } );
    // how to return IQueryable<MiniProduct> instead of IQueryable<AnonymousType>???
}

I w widoku (starając się mocno wpisać ViewModel) jaki byłby typ danych mojego modelu i jak odwoływać się z widoku?

<% Page Inherits="System.Web.Mvc.ViewPage<MyStore.Models.MiniProduct>" %>

Edit:

Cottsak wzmocnił kod i sprawił, że działał, więc zarabia pole wyboru. Mark Seemann zwrócił jednak uwagę, że technika ta spowoduje skutki uboczne. Miał rację w tym projektowaniu lub podkręcaniu twojego POCO jest złe. Po tym, jak kod zadziałał, zrobiłem o tonę więcej obiektów encji, co powoduje niepotrzebne komplikacje. Ostatecznie zmieniłem kod, aby odzwierciedlić Sugestie Marka.

Aby dodać do sugestii Cottsaka: my repozytorium return value was IQueryable. Typ referencji modelu dyrektywy strony to

Inherits="System.Web.Mvc.ViewPage<IQueryable<MyStore.Models.MiniProduct>>"

Pola Modelu były dostępne przez:

Model.SingleOrDefault().ProductNo
Model.SingleOrDefault().Name
...

I to doprowadziło do

foreach (MyStore.Models.MiniProduct myproduct in Model) {}

Dziękuję wam obojgu za odpowiedzi.

Author: Ruben Bartelink, 2009-11-09

2 answers

Spróbuj czegoś takiego:

public class AnnouncementCategory : //...
{
    public int ID { get; set; }
    public string Name { get; set; }
}

.. i w repo:

public IQueryable<AnnouncementCategory> GetAnnouncementCategories()
{
    return from ac in this._dc.AnnouncementCategories
           let announcements = this.GetAnnouncementsByCategory(ac.ID)
           select new AnnouncementCategory
           {
               ID = ac.ID,
               Name = ac.Text,
               Announcements = new LazyList<Announcement>(announcements)
           };
}

private IQueryable<Announcement> GetAnnouncementsByCategory(int categoryID)
{
    return GetAnnouncements().Where(a => a.Category.ID == categoryID);
}

W ten sposób, zamiast rzutować na anonimowy Typ, rzutuję na nową instancję mojej klasy AnnouncementCategory. Możesz zignorować funkcję GetAnnouncementsByCategory, Jeśli chcesz, jest ona używana do łańcucha kolekcji powiązanych obiektów Announcement dla każdej kategorii, ale w taki sposób, aby mogły być leniwie ładowane za pomocą IQueryable (np. tak, że kiedy[10]}do W końcu zadzwonić na tej nieruchomości, nie muszę dzwonić do całego kolekcja. mogę zrobić więcej LINQ na tym filtrowanie).

 8
Author: Matt Kocaj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-09 08:49:03

Zakładając, że Twoje klasy LINQ do SQL (L2S) są automatycznie generowane i odzwierciedlają bazową bazę danych, krótka odpowiedź brzmi: nie ujawniaj IQueryable żadnej z Twoich klas L2S - byłaby to nieszczelna abstrakcja.

Nieco dłuższa odpowiedź:

Celem repozytoriów jest ukrycie dostępu do danych za abstrakcją, aby można było zastąpić lub zmienić kod dostępu do danych niezależnie od modelu domeny. To nie będzie możliwe, gdy bazujesz na Interfejs repozytorium na typach zdefiniowanych w określonej implementacji ( komponent DAC oparty na danych L2S) - nawet jeśli możesz dostarczyć nową implementację interfejsu repozytorium, musisz odwołać się do swojego DAC L2S. Nie byłoby to szczególnie przyjemne, gdybyś nagle zdecydował się przełączyć na LINQ to Entities lub usługę Azure Table Storage.

Obiekty domeny powinny być definiowane w sposób neutralny technologicznie. Najlepiej zrobić to jako zwykłe stare obiekty C# (POCO).

Ponadto wystawianie IQueryable daje możliwość wykonywania projekcji. To może brzmieć atrakcyjnie, ale w rzeczywistości jest raczej niebezpieczne w kontekście DDD.

W DDD, powinniśmy projektować Obiekty domeny tak, aby hermetyzowały logikę domeny i zapewniały wszystkie niezmienniki.

Jako przykład rozważ pojęcie podmiotu (nie podmiotu LINQ do podmiotu Entitities, ale podmiotu DDD). Encje są prawie zawsze identyfikowane za pomocą stałego identyfikatora, więc jednym z powszechnych niezmienników jest to, że identyfikator musi być zdefiniowany. Możemy napisać podstawową klasę encji w ten sposób:

public abstract class Entity
{
    private readonly int id;

    protected Entity(int id)
    {
        if(id <= 0)
        {
            throw new ArgumentOutOfRangeException();
        }
        this.id = id;
    }

    public int Id
    {
        get { return this.id; }
    }
}

Taka klasa ładnie wymusza swoje niezmienniki(w tym przypadku ID musi być liczbą dodatnią). Może być wiele innych, bardziej specyficznych dla domeny niezmienników zaimplementowanych w poszczególnych klasach, ale wiele z nich jest bardzo prawdopodobne, że będzie sprzeczne z koncepcją projekcji: możesz być w stanie zdefiniować projekcję, która pomija pewne właściwości (takie jak ID), ale ulegnie awarii w czasie wykonywania ponieważ domyślne wartości dla typów (np. 0 dla int) będą rzucać wyjątki.

Innymi słowy, nawet jeśli potrzebujesz tylko pewnych właściwości obiektu domeny, sensowne będzie tylko uwodnienie go w całości, ponieważ tylko w ten sposób możesz zaspokoić jego niezmienniki.

W każdym razie, jeśli często stwierdzasz, że musisz wybrać tylko niektóre części obiektów swojej domeny, może to być spowodowane tym, że naruszają one zasadę jednej odpowiedzialności. To może być lepsze pomysł rozlania klasy domeny na dwie lub więcej klas.

Podsumowując, ujawnianie IQueryable brzmi jak bardzo atrakcyjna strategia, ale przy obecnej implementacji L2S doprowadzi to do wyciekających abstrakcji i anemicznych modeli domen.

W następnej wersji Entity Framework powinniśmy uzyskać wsparcie POCO, więc może to nas zbliżyć do tego celu. Jednak, jeśli O mnie chodzi, ława przysięgłych nadal nie ma z tego powodu.


Dla dokładniejszego omówienia bardzo podobny temat, patrz IQueryable to Tight Coupling .

 29
Author: Mark Seemann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-26 06:20:38