2015-08-16 13 views
8

Używam EntityFramewotk i kodu pierwszego podejścia. Więc opiszę mój model tak:EntityFramework i ReadOnlyCollection

class Person 
{ 
    public long Id { get;set; } 
    public string Name { get;set; } 
    public ICollection<Person> Parents { get;set; } 
} 

Ale moja logika domeny nie pozwalają na modyfikowanie rodzice Collection (dodawanie, usuwanie), to musi być tylko do odczytu (tylko dla przykładu). EntityFramework wymaga, aby wszystkie kolekcje miały interfejs ICollection<T> i mają metodę Add (aby zmaterializować wyniki) i Remove i inne. mogę stworzyć własną kolekcję wyraźnej realizacji interfejsu:

public class ParentsCollection : ICollection<Person> 
{ 
    private readonly HashSet<Person> _collection = new HashSet<Person>(); 
    void ICollection<Person>.Add(Person item) 
    { 
     _collection.Add(item); 
    } 

    bool ICollection<Person>.Remove(Person item) 
    { 
     return _collection.Remove(item); 
    } 

    //...and others 
} 

Ukrywa Add i Remove metod, ale nie chroni w ogóle. Ponieważ zawsze mogę rzucić na ICollection i wywołać metodę zabronioną.

Więc moje pytanie brzmi:

  • Czy istnieje sposób, aby pracować z kolekcji tylko do odczytu w EntityFramework?
+0

Możliwy duplikat [Entity Framework czytać tylko zbiory] (http://stackoverflow.com/questions/11191103/entity-framework-read-only-collections) – Machet

Odpowiedz

1

Krótka odpowiedź: nr. I byłoby to dziwne, że możesz to (dobrze, NHibernate może ustawić prywatne pola klasy, a to oznacza, że ​​możesz go odsłonić przy użyciu publicznej własności enkapsulacji pola jako kolekcji tylko do odczytu ... cóż, możesz obejść tę sytuację w EF zbyt.? Entity Framework Many to many through containing object BTW, ja nie proponuję ci to podejście, ponieważ jak można dodać nowych rodziców, jeśli jest to własność prywatna)

W każdym razie uważam, że obiekt domeny powinna być tylko do odczytu , ponieważ na koniec dnia obiekt domeny opisuje encję w domenie i powinieneś mieć do niej dostęp i ją modyfikować.

Alternatywnym rozwiązaniem jest zaprojektowanie interfejsu narazić Parents jak IReadOnlyList<Person>, a także IPerson ze wszystkimi Person członków wyjątkiem Parents i powrót Person jak IPerson:

public interface IHasParents 
{ 
    IReadOnlyList<Person> Parents { get; } 
} 

public interface IPerson : IHasParents 
{ 
    long Id { get; set; } 
    string Name { get; set; } 
} 

i wdrożenie IPerson niejawnie na Person wyjątkiem Parents że zostanie zaimplementowane jawnie. Kiedy trzeba zwrócić Person gdzieś wrócisz IPerson zamiast Person:

public IPerson CreatePerson(string name, IEnumerable<Persons> parents) 
{ 
    Person person = new Person { Name = name, Parents = parents }; 

    // Persistence stuff 

    return person; 
} 

Można argumentować, że być może uda się przygnębiony IPerson do Person, ale w tym momencie chciałbym odpowiedzieć informacją, że trzeba podążaj za własnymi konwencjami kodowania: jeśli zdefiniowałeś, że nigdy nie wrócisz Person, ale IPerson, to zrobiłbym to w ten sposób w całej bazie kodu, a jeśli potrzebujesz właściwości do pisania, która może być zapisana, Parents, to zamiast tego wrócisz Person (a unikniesz rzuć później!).

+0

dziękuję za odpowiedź, było użyteczne – Backs

+0

@Backs Bez problemu !!! –

+0

jak nikt inny nie odpowiedział, Ja zaznaczę odpowiedź – Backs

1

Cóż, jest sposób. Nie jest to piękne, ponieważ dodaje dodatkowe elementy w modelu domeny, ale po prostu sprawdziłem i to działa.

Wszystkie kredyty należą do Owen Craig.

http://owencraig.com/mapping-but-not-exposing-icollections-in-entity-framework/

przykład w pigułce

Wyobraź mamy istniejącego modelu z organizacją => Pracownicy już utworzonych.

Aby zastosować tę technikę, musimy zmienić modelu organizacji kawałek:

// this is the main collection that will be persisted, mark it as protected 
protected virtual ICollection<Employee> EmployeesInternal { get; private set; } = new List<Employee>(); 

// this will expose collection contents to public, seemingly unneccessary `Skip` statement will prevent casting back to Collection 
public IEnumerable<Employee> Employees => EmployeesInternal.Skip(0); 

// this is property accessor that will be used to define model and/or in `Include` statements, could be marked as internal if your domain/persistance/services are in the same assembly 
public static Expression<Func<Organization, ICollection<Employee>>> EmployeeAccessor = f => f.EmployeesInternal; 

Zmień biegle config w kontekście bazy danych:

modelBuilder.Entity<Organization>().HasMany(Organization.EmployeeAccessor).WithRequired(); 

Zmień dowolny zawierać oświadczenia w przypadku, gdy nie są używane LazyLoading

var organizations = organizationRepository.GetAll().Include(Organization.EmployeeAccessor) 

Happy DDD!

4

Możesz odsłonić właściwości kolekcji prywatnej do EF, pozwalając na mapowanie i kwerendy, przy jednoczesnym zachowaniu właściwego hermetyzacji członków i relacji w obiekcie domeny. To trochę brudny, ale to działa:

public class Customer 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    public IEnumerable<Order> Orders 
    { 
     get { return _orders.AsEnumerable(); } 
    } 

    private List<Order> _orders { get; set; } 

    public Customer() 
    { 
     _orders = new List<Order>(); 
    } 

    public static Expression<Func<Customer, ICollection<Order>>> OrderMapping 
    { 
     get { return c => c._orders; } 
    } 
} 

Odwzorowanie wykorzystuje następnie:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    base.OnModelCreating(modelBuilder); 
    modelBuilder.Entity<Customer>().HasMany(Customer.OrderMapping); 
} 

Takie podejście jest opisane tutaj: http://ardalis.com/exposing-private-collection-properties-to-entity-framework

Powiązane problemy