2013-12-12 3 views
5

Also, das ist eine Art eine stumpfe Frage, aber lassen Sie mich sehen, ob ich es relativ einfach auslegen kann. Können sagen, ich die folgende Schnittstelle habe:Implementierung einer ICollection <ISomething> mit einem konkreten Typ Entity Framework zu erfüllen

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

die mit mir dann implementieren:

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

Nur Entity Framework kann nicht mit Schnittstellen arbeiten, so dass es ignoriert ziemlich vollständig diese Navigations Eigenschaft. Um sie zu erkennen EF zu bekommen, muss ich es ändern:

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

Wo Bar würde meine Implementierung von IBar sein. Nur, das kann die Schnittstelle nicht implementieren, die IBar nicht Bar will. Jetzt

, betrachten ein etwas anderes Szenario, wo ich nur einen grundlegenden Fremdschlüssel bekam:

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

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

gleiches Problem, aber hier kann ich es löst durch Zugabe:

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

EF ist glücklich, weil es einen konkreten Typ hat und die Schnittstelle ist glücklich, weil es eine Implementierung mit IBar hat. Das Problem ist, dass ich nicht herausfinden kann, wie man dieselbe Logik mit einem ICollection<IBar> anwendet, weil (ICollection<Bar>)value eine Ausnahme mit der Aufschrift "Kann Typ ICollection<Bar> zu ICollection<IBar> nicht implizit konvertieren" nicht auslösen kann.

Wie soll ich die Besetzung richtig machen?

UPDATE

Also, ich zahle nicht nahe genug, um die Aufmerksamkeit auf, wo der Fehler erzeugt wurde. Es beschwerte sich tatsächlich über das get { return Bars; } Bit. Ich konnte durch die Fehler loszuwerden Ändern Sie es an:

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; } 
    } 
} 

dass, obwohl ein wenig kitschig mir scheint, wie ich nur die Fehlermaskierung und eine kleine Zeitbombe für mich zu schaffen. Ich würde mich über irgendwelche Gedanken oder alternativen Lösungen freuen.

+0

tun: Was ist Ihre Situation, in der Sie dies tun müssten ? – IronMan84

+0

Ich habe eine Entität, die eine Schnittstelle implementiert, die nur auf andere Schnittstellen verweist, nicht auf konkrete Typen. –

+0

Funktioniert Ihr Getter und Setter tatsächlich?Ich würde erwarten, dass eine "InvalidCastException" für mindestens einige gültige Sammlungsinstanzen ausgelöst wird. – hvd

Antwort

2

Um die Kovarianz/Kontra Arbeit lasse ich Navigationseigenschaften wie enumerables in meiner Schnittstellen definieren:

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; 
    } 
} 

Diese auf Interface-Typen gebaut noch genug für EF LINQ-Abfragen ist.

+0

Nun, das macht Sinn. Was ist mit dem Setter? Sehen Sie irgendwelche Probleme mit meinem 'set {Bars = (ICollection ) Wert; } '. Ist es in Ordnung, oder gibt es eine bessere Methode? –

+0

Ich bin mir nicht sicher über den Setter, ich brauche es nicht. Meine dbcontexts werden in der Sprache der konkreten Klasse ausgedrückt, während Repositories um Schnittstellen herum gebaut werden. Repositories ermöglichen die Angabe von LINQ-Ausdrücken mit der Sprache der Benutzeroberfläche (und Getter reichen dafür aus), während die Einfügungen auf der Repository-Ebene implementiert werden (die auf konkreten Typen/Eigenschaften basiert). –

+0

Ich würde den Setter mit einer dynamischen Cast-Prüfung und einer Ausnahme schreiben, wenn die "Wert" -Wiedergabe fehlschlägt. –

0

Ich benutze eine generische Schnittstelle Adapter wie diese

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(); 
    } 
} 

Dann in der POCO Sie einfach dieses Ich bin neugierig

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

Ich habe eine sogar "smartere" Version, die automatisch die zugrundeliegenden EF-benötigten virtuals meldet, so dass Sie den neuen List () Aufruf in Ihren Konstruktoren nicht haben. Antwort aktualisiert Siehe den Bearbeitungsverlauf für die einfachere Version. – IDisposable

Verwandte Themen