2013-12-12 16 views
5

Jest to więc rodzaj tępego pytania, ale pozwól mi sprawdzić, czy mogę to wyjaśnić stosunkowo prosto. Powiedzmy, że mam następujący interfejs:Wdrażanie ICollection <ISomething> z konkretnym typem w celu zaspokojenia Entity Framework

public interface IFoo 
{ 
    ICollection<IBar> Bars { get; set; } 
} 

Które I następnie wdrożyć z:

public class Foo : IFoo 
{ 
    public virtual ICollection<IBar> Bars { get; set; } 
} 

Tylko Entity Framework nie może pracować z interfejsów, więc prawie całkowicie ignoruje tę właściwość nawigacji. W celu uzyskania EF go rozpoznać, muszę go zmienić na:

public virtual ICollection<Bar> Bars { get; set; } 

Gdzie Bar byłaby moja realizacji IBar. Tylko, że nie implementuje interfejsu, który chce IBar nie Bar.

Rozważmy teraz nieco inny scenariusz, gdzie Mam tylko podstawowy klucz obcy:

public interface IFoo 
{ 
    IBar Bar { get; set; } 
} 

public class Foo : IFoo 
{ 
    public virtual IBar Bar { get; set; } 
} 

sam problem, ale tutaj mogę go rozwiązać dodając:

public class Foo : IFoo 
{ 
    public virtual Bar Bar { get; set; } 
    IBar IFoo.Bar 
    { 
     get { return Bar; } 
     set { Bar = (Bar)value; } 
    } 
} 

EF jest szczęśliwy, ponieważ ma konkretny typ i interfejs jest szczęśliwy, ponieważ ma implementację z IBar. Problem polega na tym, że nie mogę wymyślić, jak zastosować tę samą logikę z ICollection<IBar>, ponieważ (ICollection<Bar>)value podnosi wyjątek mówiąc "Nie można niejawnie przekonwertować typu ICollection<Bar> na ICollection<IBar>".

Jak właściwie wykonać rzutowanie?

UPDATE

Więc nie zwracając szczególną uwagę na wystarczającą gdzie błąd był wygenerowany. To naprawdę narzekało na bit get { return Bars; }. Udało mi się pozbyć tego błędu, zmieniając go do:

public class Foo : IFoo 
{ 
    public virtual ICollection<Bar> Bars { get; set; } 
    ICollection<IBar> IFoo.Bars 
    { 
     get { return (ICollection<IBar>)Enumeration.Cast<IBar>(Bars); } 
     set { Bars = (ICollection<Bar>)value; } 
    } 
} 

To wydaje się nieco hokey do mnie jednak, jakbym tylko maskowanie błąd i tworząc małą bombę zegarową dla siebie. Byłbym wdzięczny za wszelkie przemyślenia lub alternatywne rozwiązania.

+0

jestem ciekaw: Jaka jest twoja sytuacja, w której trzeba by to zrobić ? – IronMan84

+0

Mam obiekt, który implementuje interfejs, który odwołuje się tylko do innych interfejsów, a nie konkretnych typów. –

+0

Czy twój getter i seter rzeczywiście działają?Spodziewam się, że zgłoszony zostanie wyjątek InvalidCastException dla przynajmniej niektórych poprawnych instancji kolekcji. – hvd

Odpowiedz

2

Aby umożliwić pracę kowariancji/kontrawariancji zdefiniować właściwości nawigacyjnych jak enumerables w moich interfejsów:

public interface IFoo 
{ 
    IEnumerable<IBar> Bars { get; } 
} 

public class Foo : IFoo 
{ 
    public virtual ICollection<Bar> Bars { get; set; } 
    IEnumerable<IBar> IFoo.Bars 
    { 
     return this.Bars; 
    } 
} 

Jest jeszcze tyle do zapytań LINQ EF zbudowanych na typy interfejsów.

+0

Cóż, to ma sens. A co z seterem? Czy przewidujesz jakiekolwiek problemy z moją wartością 'set {Bars = (ICollection ); } '. Czy jest w porządku, czy jest jakaś lepsza metoda? –

+0

Nie jestem pewien co do setera, nie potrzebuję tego. Moje dbcontexts są wyrażone w języku konkretnej klasy, podczas gdy repozytoria są zbudowane wokół interfejsów. Repozytoria zezwalają na wyrażenia LINQ z językiem interfejsu (do tego wystarczą moduły pobierające), natomiast inserty są implementowane na poziomie repozytorium (który działa na konkretnych typach/właściwościach). –

+0

Napisałbym ustawiający z dynamicznym sprawdzeniem rzutu i wyjątkiem, gdy rzutowanie "wartości" nie powiedzie się. –

0

używam rodzajowe adapter interfejsu jak ten

public class InterfaceCollectionAdapter<TConcrete, TInterface> : ICollection<TInterface> where TConcrete : TInterface 
{ 
    private Func<ICollection<TConcrete>> _gettor; 
    private Action<ICollection<TConcrete>> _settor; 
    private Func<ICollection<TConcrete>> _factory; 

    private ICollection<TConcrete> Wrapped 
    { 
     get 
     { 
      var value = _gettor(); 

      if (value == null && _settor != null) 
      { 
       value = (_factory != null) 
        ? _factory() 
        : new List<TConcrete>(); 

       _settor(value); 
      } 

      return value; 
     } 
    } 

    public InterfaceCollectionAdapter(Func<ICollection<TConcrete>> gettor, Action<ICollection<TConcrete>> settor = null, Func<ICollection<TConcrete>> factory = null) 
    { 
     _gettor = gettor; 
     _settor = settor; 
     _factory = factory; 
    } 

    public void Add(TInterface item) 
    { 
     Wrapped.Add((TConcrete)item); 
    } 

    public void Clear() 
    { 
     Wrapped.Clear(); 
    } 

    public bool Contains(TInterface item) 
    { 
     return Wrapped.Contains((TConcrete)item); 
    } 

    public void CopyTo(TInterface[] array, int arrayIndex) 
    { 
     var wrapped = Wrapped; 
     foreach (var item in wrapped) 
     { 
      array[arrayIndex++] = (TInterface)item; 
     } 
    } 

    public int Count 
    { 
     get { return Wrapped.Count; } 
    } 

    public bool IsReadOnly 
    { 
     get { return Wrapped.IsReadOnly; } 
    } 

    public bool Remove(TInterface item) 
    { 
     return Wrapped.Remove((TConcrete)item); 
    } 

    public IEnumerator<TInterface> GetEnumerator() 
    { 
     var wrapped = Wrapped; 
     foreach (var item in wrapped) 
      yield return item; 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

Następnie w POCO po prostu zrobić to

public Foo() 
{ 
    public Foo() 
    { 
     _bars = new InterfaceCollectionAdapter<Bar, IBar>(() => this.Bars, (value) => { this.Bars = value; }); 
    } 

    private InterfaceCollectionAdapter<Bar, IBar> _bars; 

    [NotMapped] 
    ICollection<IBar> IFoo.Bars 
    { 
     get 
     { 
      return _bars; 
     } 
    } 
+0

Mam nawet "mądrzejszą" wersję, która automatycznie powiadamia o potrzebnych wirtualnych obiektach EF, więc nie masz nowego wywołania List () w swoim konstruktorze. Odpowiedź zaktualizowana. Zobacz historię edycji dla prostszej wersji. – IDisposable

Powiązane problemy