autonomatt
autonomatt

Reputation: 4433

NHibernate: Calling .RemoveAll on a collection

I have a problem with not being able to use .RemoveAll when retrieving a collection via NHibernate.

I have an entity called Order that I persist via NHibernate.

Order has many OrderItems. Here is my mapping for this relationship:

mapping.HasMany(o => o.Items)
                   .Cascade.AllDeleteOrphan()
                   .AsList()
                   .Inverse();

In my domain that look like this:

public virtual IList<OrderItem> Items { get; set; }

As I understand it, I have to use IList because NHibernate has it's own implementation of List.

Now I want to remove an item from my Order using this method on in my Order class:

public virtual void RemoveItem(string variantSku)
{
    items.RemoveAll(x => x.Variant.VariantSku == variantSku);
}

It doesn't work, because IList does not have this method.

I tried:

items.ToList().RemoveAll(x => x.Variant.VariantSku == variantSku);

But that doesn't seem to work. I realise the items.ToList() actually creates a copy of the original list, so I guess I could try:

var itemsList = items.ToList();
itemsList.RemoveAll(...)

But then does it still persist via NHibernate?

My question: can I actually use .RemoveAll in this context or should I be thinking about a different way to remove an item?

Upvotes: 3

Views: 1695

Answers (2)

Jamie Ide
Jamie Ide

Reputation: 49251

You have the collection mapped as the inverse side of the relationship, so in addition to removing the item from the collection you have to nullify the reference to the Order on the OrderItem side. Therefore the RemoveAll method, or the extension method, won't do the job.

I would handle it like this:

public virtual void RemoveItem(string variantSku)
{
    var itemsToRemove = items.Where(x => x.Variant.VariantSku == variantSku).ToArray();
    foreach(var item in itemsToRemove)
    {
       item.Order = null;
       items.Remove(item);
    }
}

I also suggest using set instead of bag mapping.

Upvotes: 4

Jon Skeet
Jon Skeet

Reputation: 1500015

You could always create your own RemoveAll extension method:

public static void RemoveAll<T>(this IList<T> source, Predicate<T> predicate)
{
    // TODO: Argument non-nullity validation

    // Optimization
    List<T> list = source as List<T>
    if (list != null)
    {
        list.RemoveAll(predicate);
        return;
    }

    // Slow way
    for (int i = source.Count - 1; i >= 0; i--)
    {
        if (predicate(source[i]))
        {
            source.RemoveAt(i);
        }
    }
}

I would expect NHibernate to care about the collection itself - just removing an item from a List<T> isn't something that NHibernate can "notice".

Of course, if you didn't want NHibernate to care about what you do - if your changes were only meant to be local - then your final piece of code would be fine.

Upvotes: 3

Related Questions