使用具体类型实现ICollection <isomething>以满足Entity Framework </isomething>

时间:2013-12-12 21:51:47

标签: c# entity-framework interface entity-framework-6

所以,这是一个迟钝的问题,但让我看看我是否可以相对简单地说出来。假设我有以下界面:

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

这对我来说似乎有些小事,就像我只是掩饰错误并为自己制造一个小定时炸弹。我很感激任何想法或其他解决方案。

2 个答案:

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