问题
So, this is kind of an obtuse question, but let me see if I can lay it out relatively simply. Lets say I have the following interface:
public interface IFoo
{
ICollection<IBar> Bars { get; set; }
}
Which I then implement with:
public class Foo : IFoo
{
public virtual ICollection<IBar> Bars { get; set; }
}
Only Entity Framework can't work with interfaces, so it pretty much completely ignores this navigation property. In order to get EF to recognize it, I need to change it to:
public virtual ICollection<Bar> Bars { get; set; }
Where Bar would be my implementation of IBar. Only, that fails to implement the interface, which wants IBar not Bar.
Now, consider a slightly different scenario, where I've just got a basic foreign key:
public interface IFoo
{
IBar Bar { get; set; }
}
public class Foo : IFoo
{
public virtual IBar Bar { get; set; }
}
Same issue, but here, I can solve it by adding:
public class Foo : IFoo
{
public virtual Bar Bar { get; set; }
IBar IFoo.Bar
{
get { return Bar; }
set { Bar = (Bar)value; }
}
}
EF is happy because it has a concrete type and the interface is happy because it has an implementation with IBar. The problem is that I can't figure out how to apply the same logic with an ICollection<IBar> because (ICollection<Bar>)value raises an exception saying "Cannot implicitly convert type ICollection<Bar> to ICollection<IBar>".
How should I properly make the cast?
UPDATE
So, I wasn't paying close enough attention to where the error was being generated. It was actually complaining about the get { return Bars; } bit. I was able to get rid of the error by changing it to:
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; }
}
}
That seems a little hokey to me though, like I'm only masking the error and creating a little time bomb for myself. I'd appreciate any thoughts or alternate solutions.
回答1:
To let the covariance/contravariance work I define navigation properties as enumerables in my interfaces:
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;
}
}
This is still enough for EF LINQ queries built on interface types.
回答2:
I use a generic interface adapter like this
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();
}
}
Then in the POCO you simply do this
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;
}
}
来源:https://stackoverflow.com/questions/20555028/implementing-an-icollectionisomething-with-a-concrete-type-to-satisfy-entity-f