我可以隐藏我的ICollection< T>当我在EF4代码中有一对多映射时,字段? [英] Can I hide my ICollection<T> fields when I have a one-to-many mapping in EF4 code-only?

查看:124
本文介绍了我可以隐藏我的ICollection< T>当我在EF4代码中有一对多映射时,字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我拥有一对多映射的域类通常采用以下形式(未经测试的代码):

  public Customer客户
{
//公共方法。

public Order AddOrder(Order order)
{
_orders.Add(order);
}

public Order GetOrder(long id)
{
return _orders.Where(x => x.Id).Single();
}

//等

//私有字段。

私人ICollection< Order> _orders = new List< Order>();
}



有没有办法通过展示他们来继续和恢复我的集合?如果没有,我的域名对象将被设计为满足ORM的要求,这似乎违背了这一努力的精神。暴露一个ICollection(使用它的Add等)方法看起来并不是特别干净,而不是我的默认方式。



更新



找到这篇文章,这表明在5月份是不可能的。当然,微软的海报确实说,他们强烈地考虑实施(我希望如此),而且我们已经半年了,所以也许有进展吗?

解决方案

我发现,无论做什么,EF要求公开 ICollection< T> 。我认为这是因为当从数据库加载对象时,映射将寻找一个collection属性,获取该集合,然后调用该集合的 Add 方法来添加每个的子对象。



我想确保添加是通过父对象上的方法完成的,因此创建了一个包装集合的解决方案,捕捉添加和引导



扩展列表和其他集合类型是不可能的,因为添加方法不是虚拟的。一个选项是扩展 Collection class并覆盖 InsertItem 方法。



我只专注于添加删除 ,和清除功能的 ICollection< T> 接口,因为那些是可以修改集合的。 p>

首先,我的基础收集包装器实现 ICollection< T> 接口
默认行为是的正常收藏。但是,调用者可以指定要调用的另一个添加方法。另外,调用者可以执行添加删除清除操作不允许通过设置替代方案 null 。如果有人尝试使用该方法,则会导致 NotSupportedException



抛出异常不是首先防止访问是好的。然而,代码应该被测试(单元测试),并且将非常快速地发现异常并进行适当的代码更改。

  public抽象类WrappedCollectionBase< T> :ICollection< T> 
{

私人ICollection< T> InnerCollection {get {return GetWrappedCollection(); }}

private Action< T> addItemFunction;
private Func< T,bool> removeItemFunction;
private Action clearFunction;


///< summary>
///默认行为就像一个正常集合
///< / summary>
public WrappedCollectionBase()
{
this.addItemFunction = this.AddToInnerCollection;
this.removeItemFunction = this.RemoveFromInnerCollection;
this.clearFunction = this.ClearInnerCollection;


public WrappedCollectionBase(Action< T> addItemFunction,Func< T,bool> removeItemFunction,Action clearFunction):this()
{
this.addItemFunction = addItemFunction;
this.removeItemFunction = removeItemFunction;
this.clearFunction = clearFunction;
}

protected abstract ICollection< T> GetWrappedCollection();

public void Add(T item)
{
if(this.addItemFunction!= null)
{
this.addItemFunction(item);
}
else
{
抛出新的NotSupportedException(不允许直接添加到此集合);
}
}

public void AddToInnerCollection(T item)
{
this.InnerCollection.Add(item);


public bool删除(T项)
{
if(removeItemFunction!= null)
{
return removeItemFunction(item) ;
}
else
{
抛出新的NotSupportedException(不允许从此集合直接删除);
}
}

public bool RemoveFromInnerCollection(T item)
{
return this.InnerCollection.Remove(item);
}

public void Clear()
{
if(this.clearFunction!= null)
{
this.clearFunction() ;
}
else
{
抛出新的NotSupportedException(不允许清除此集合);
}
}

public void ClearInnerCollection()
{
this.InnerCollection.Clear();
}

public bool包含(T项)
{
返回InnerCollection.Contains(item);
}

public void CopyTo(T [] array,int arrayIndex)
{
InnerCollection.CopyTo(array,arrayIndex);
}

public int Count
{
get {return InnerCollection.Count; }
}

public bool IsReadOnly
{
get {return((ICollection< T>)this.InnerCollection).IsReadOnly; }
}

public IEnumerator< T> GetEnumerator()
{
return InnerCollection.GetEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return InnerCollection.GetEnumerator();
}

}

鉴于基类,我们可以使用它有两种方式。例如使用原始的post对象。



1)创建一个特定类型的包装集合(例如, List
public class WrappedListCollection:WrappedCollectionBase,IList
{
private List innerList;

  public WrappedListCollection(Action< T> addItemFunction,Func< T,bool> removeItemFunction,Action clearFunction)
:base(addItemFunction,removeItemFunction,clearFunction)
{
this.innerList = new List< T& ();
}

protected override ICollection< T> GetWrappedCollection()
{
return this.innerList;
}
< ... snip ....> //填写IList的实现,如果重要或不实施IList
}

然后可以使用:

  public Customer Customer 
{
public ICollection< Order>订单{get {return _orders; }}
//公共方法。

public void AddOrder(Order order)
{
_orders.AddToInnerCollection(order);
}

//私有字段。

private WrappedListCollection< Order> _orders = new WrappedListCollection< Order>(this.AddOrder,null,null);
}

2)使用

  public class WrappedCollection< T> :WrappedCollectionBase< T> 
{
private ICollection< T> wrappedCollection;

public WrappedCollection(ICollection< T> collectionToWrap,Action< T> addItemFunction,Func< T,bool> removeItemFunction,Action clearFunction)
:base(addItemFunction,removeItemFunction,clearFunction)
{
this.wrappedCollection = collectionToWrap;
}

protected override ICollection< T> GetWrappedCollection()
{
return this.wrappedCollection;
}
}

可以使用如下:



{
public ICollection Orders {get {return _wrappedOrders;



  public void AddOrder(Order order)
{
_orders.Add(order);
}

//私有字段。
私人ICollection< Order> _orders = new List< Order>();
private WrappedCollection< Order> _wrappedOrders = new WrappedCollection< Order>(_ orders,this.AddOrder,null,null);
}

还有一些其他方法可以调用 WrappedCollection 构造函数
例如,要覆盖添加,但保持删除并正常清除

  private WrappedListCollection< ;排序> _orders = new WrappedListCollection(this.AddOrder,(Order o)=> _orders.RemoveFromInnerCollection(o),()=> _orders.ClearInnerCollection()); 

我同意,如果EF不要求收集是公开的,但这个解决方案允许我控制我的收藏的修改。



对于阻止访问集合进行查询的问题,您可以使用上述方法2)并设置WrappedCollection GetEnumerator 方法抛出一个 NotSupportedException 。那么你的 GetOrder 方法可以保持原样。然而,较为简单的方法可能是暴露包裹的集合。例如:

  public class WrappedCollection< T> :WrappedCollectionBase< T> 
{
public ICollection< T> InnerCollection {get;私人集合

public WrappedCollection(ICollection< T> collectionToWrap,Action< T> addItemFunction,Func< T,bool> removeItemFunction,Action clearFunction)
:base(addItemFunction,removeItemFunction,clearFunction)
{
this.InnerCollection = collectionToWrap;
}


protected override ICollection< T> GetWrappedCollection()
{
return this.InnerCollection;
}
}

然后在 GetOrder 方法将成为

  _orders.InnerCollection.Where(x => x.Id == ID)。单(); 


My domain classes that have one-to-many mappings generally take the following form (untested code):

public Customer Customer
{
    // Public methods.

    public Order AddOrder(Order order)
    {
        _orders.Add(order);
    }

    public Order GetOrder(long id)
    {
        return _orders.Where(x => x.Id).Single();
    }

    // etc.

    // Private fields.

    private ICollection<Order> _orders = new List<Order>();
}

The EF4 code-only samples I've seen expose a public ICollection when dealing with one-to-many relationships.

Is there a way to persist and restore my collections with exposing them? If not, it would appear that my domain objects will be designed to meet the requirements of the ORM, which seems to go against the spirit of the endeavour. Exposing an ICollection (with it's Add, etc. methods) doesn't seem particularly clean, and wouldn't be my default approach.

Update

Found this post that suggests it wasn't possible in May. Of course, the Microsoft poster did say that they were "strongly considering implementing" it (I'd hope so) and we're half a year on, so maybe there's been some progress?

解决方案

I found that whatever was done, EF requires the ICollection<T> to be public. I think this is because when the objects are loaded from the database, the mapping looks for a collection property, gets the collection and then calls the Add method of the collection to add each of the child objects.

I wanted to ensure that the addition was done through a method on the parent object so created a solution of wrapping the collection, catching the add and directing it to my preferred method of addition.

Extending a List and other collection types was not possible because the Add method is not virtual. One option is to extend Collection class and override the InsertItem method.

I have only focussed on the Add, Remove, and Clear functions of the ICollection<T> interface as those are the ones that can modify the collection.

First, is my base collection wrapper which implements the ICollection<T> interface The default behaviour is that of a normal collection. However, the caller can specify an alternative Add method to be called. In addition, the caller can enforce that the Add, Remove, Clear operations are not permitted by setting the alternatives to null. This results in NotSupportedException being thrown if anyone tries to use the method.

The throwing of an exception is not as good as preventing access in the first place. However, code should be tested (unit tested) and an exception will be found very quickly and a suitable code change made.

public abstract class WrappedCollectionBase<T> : ICollection<T>
{

    private ICollection<T> InnerCollection { get { return GetWrappedCollection(); } }

    private Action<T> addItemFunction;
    private Func<T, bool> removeItemFunction;
    private Action clearFunction;


    /// <summary>
    /// Default behaviour is to be like a normal collection
    /// </summary>
    public WrappedCollectionBase()
    {
        this.addItemFunction = this.AddToInnerCollection;
        this.removeItemFunction = this.RemoveFromInnerCollection;
        this.clearFunction = this.ClearInnerCollection;
    }

    public WrappedCollectionBase(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) : this()
    {
        this.addItemFunction = addItemFunction;
        this.removeItemFunction = removeItemFunction;
        this.clearFunction = clearFunction;
    }

    protected abstract ICollection<T> GetWrappedCollection();

    public void Add(T item)
    {
        if (this.addItemFunction != null)
        {
            this.addItemFunction(item);
        }
        else
        {
            throw new NotSupportedException("Direct addition to this collection is not permitted");
        }
    }

    public void AddToInnerCollection(T item)
    {
        this.InnerCollection.Add(item);
    }

    public bool Remove(T item)
    {
        if (removeItemFunction != null)
        {
            return removeItemFunction(item);
        }
        else
        {
            throw new NotSupportedException("Direct removal from this collection is not permitted");
        }
    }

    public bool RemoveFromInnerCollection(T item)
    {
        return this.InnerCollection.Remove(item);
    }

    public void Clear()
    {
        if (this.clearFunction != null)
        {
            this.clearFunction();
        }
        else
        {
            throw new NotSupportedException("Clearing of this collection is not permitted");
        }
    }

    public void ClearInnerCollection()
    {
        this.InnerCollection.Clear();
    }

    public bool Contains(T item)
    {
        return InnerCollection.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        InnerCollection.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return InnerCollection.Count; }
    }

    public bool IsReadOnly
    {
        get { return ((ICollection<T>)this.InnerCollection).IsReadOnly; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return InnerCollection.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return InnerCollection.GetEnumerator();
    }

}

Given that base class we can use it in two ways. Examples are using the original post objects.

1) Create a specific type of wrapped collection (For example, List) public class WrappedListCollection : WrappedCollectionBase, IList { private List innerList;

    public WrappedListCollection(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
        : base(addItemFunction, removeItemFunction, clearFunction)
    { 
    this.innerList = new List<T>();
    }

    protected override ICollection<T> GetWrappedCollection()
    {
        return this.innerList;
    }
 <...snip....> // fill in implementation of IList if important or don't implement IList
    }

This can then be used:

 public Customer Customer
 {
 public ICollection<Order> Orders {get { return _orders; } }
 // Public methods.

 public void AddOrder(Order order)
 {
    _orders.AddToInnerCollection(order);
 }

// Private fields.

private WrappedListCollection<Order> _orders = new WrappedListCollection<Order>(this.AddOrder, null, null);
}

2) Give a collection to be wrapped using

 public class WrappedCollection<T> : WrappedCollectionBase<T>
{
    private ICollection<T> wrappedCollection;

    public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
        : base(addItemFunction, removeItemFunction, clearFunction)
    {
        this.wrappedCollection = collectionToWrap;
    }

    protected override ICollection<T> GetWrappedCollection()
    {
        return this.wrappedCollection;
    }
}

which can be used as follows:

{ public ICollection Orders {get { return _wrappedOrders; } } // Public methods.

 public void AddOrder(Order order)
 {
    _orders.Add(order);
 }

// Private fields.
private ICollection<Order> _orders = new List<Order>();
private WrappedCollection<Order> _wrappedOrders = new WrappedCollection<Order>(_orders, this.AddOrder, null, null);
}

There are some other ways to call the WrappedCollection constructors For example, to override add but keep remove and clear as normal

private WrappedListCollection<Order> _orders = new WrappedListCollection(this.AddOrder,  (Order o) => _orders.RemoveFromInnerCollection(o), () => _orders.ClearInnerCollection());

I agree that it would be best if EF would not require the collection to be public but this solution allows me to control the modification of my collection.

For the problem of preventing access to the collection for querying you can use approach 2) above and set the WrappedCollection GetEnumerator method to throw a NotSupportedException. Then your GetOrder method can stay as it is. A neater method however may be to expose the wrapped collection. For example:

 public class WrappedCollection<T> : WrappedCollectionBase<T>
 {
    public ICollection<T> InnerCollection { get; private set; }

    public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
        : base(addItemFunction, removeItemFunction, clearFunction)
    {
        this.InnerCollection = collectionToWrap;
    }


    protected  override ICollection<T> GetWrappedCollection()
    {
        return this.InnerCollection;
    }
}

Then the call in the GetOrder method would become

_orders.InnerCollection.Where(x => x.Id == id).Single();

这篇关于我可以隐藏我的ICollection&lt; T&gt;当我在EF4代码中有一对多映射时,字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆