Implementing an ICollection<ISomething> with a concrete type to satisfy Entity Framework

二次信任 提交于 2019-12-05 10:26:18

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.

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;
        }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!