所以,这是一个迟钝的问题,但让我看看我是否可以相对简单地说出来。假设我有以下界面:
public interface IFoo
{
ICollection<IBar> Bars { get; set; }
}
然后我实施:
public class Foo : IFoo
{
public virtual ICollection<IBar> Bars { get; set; }
}
只有Entity Framework无法使用接口,所以它几乎完全忽略了这个导航属性。为了让EF识别它,我需要将其更改为:
public virtual ICollection<Bar> Bars { get; set; }
Bar
将成为我IBar
的实施位置。只是,无法实现希望IBar
而不是Bar
的接口。
现在,考虑一个稍微不同的场景,我刚刚获得了一个基本的外键:
public interface IFoo
{
IBar Bar { get; set; }
}
public class Foo : IFoo
{
public virtual IBar Bar { get; set; }
}
同样的问题,但在这里,我可以通过添加:
来解决它public class Foo : IFoo
{
public virtual Bar Bar { get; set; }
IBar IFoo.Bar
{
get { return Bar; }
set { Bar = (Bar)value; }
}
}
EF很高兴,因为它有一个具体类型,界面很开心,因为它有一个IBar
的实现。问题是我无法弄清楚如何将ICollection<IBar>
应用于相同的逻辑,因为(ICollection<Bar>)value
引发了一个异常,说“无法将类型ICollection<Bar>
隐式转换为ICollection<IBar>
”
我应该如何正确地进行演员表?
更新
所以,我没有足够关注错误产生的位置。它实际上抱怨get { return Bars; }
位。我能够通过将其更改为:
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; }
}
}
这对我来说似乎有些小事,就像我只是掩饰错误并为自己制造一个小定时炸弹。我很感激任何想法或其他解决方案。
答案 0 :(得分:2)
为了让协方差/逆变工作,我在我的界面中将导航属性定义为可枚举:
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;
}
}
对于基于接口类型构建的EF LINQ查询,这仍然足够了。
答案 1 :(得分:0)
我使用像这样的通用接口适配器
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();
}
}
然后在POCO中你只需要这样做
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;
}
}