views:

83

answers:

3

RIA Services is returning a list of Entities that won't allow me to add new items. Here are what I believe to be the pertinent details:

  • I'm using the released versions of Silverlight 4 and RIA Services 1.0 from mid-April of 2010.
  • I have a DomainService with a query method that returns List<ParentObject>.
  • ParentObject includes a property called "Children" that is defined as List<ChildObject>.
  • In the DomainService I have defined CRUD methods for ParentObject with appropriate attributes for the Query, Delete, Insert, and Update functions.
  • The ParentObject class has an Id property marked with the [Key] attribute. It also has the "Children" property marked with the attributes [Include], [Composition], and [Association("Parent_Child", "Id", "ParentId")].
  • The ChildObject class has an Id marked with the [Key] attribute as well as a foreign key, "ParentId", that contains the Id of the parent.

On the client side, data is successfully returned and I assign the results of the query to a PagedCollectionView like this:

_pagedCollectionView = new PagedCollectionView(loadOperation.Entities);  

When I try to add a new ParentObject to the PagedCollectionView like this:

ParentObject newParentObject = (ParentObject)_pagedCollectionView.AddNew();  

I get the following error:

" 'Add New' is not allowed for this view."

On further investigation, I found that _pagedCollectionView.CanAddNew is "false" and cannot be changed because the property is read-only.

I need to be able to add and edit ParentObjects (with their related children, of course) to the PagedCollectionView. What do I need to do?

+1  A: 

I am having the same problem as you. I would ideally have like to be able to use the AddNew() from the PCV but alas, no go...

I have a workaround I am using currently. If you make the AddNew() work please post the answer.

Instead of using the AddNew, on your DomainContext you can retrieve an EntitySet<T> by saying Context.EntityNamePlural (ie: Context.Users = EntitySet<User> )

You can add a new entity to that EntitySet by calling Add() and then Context.SubmitChanges() to send it to the DB. To reflect the changes on the client you will need to Reload (Context.Load())

I just made this work about 15mins ago after having no luck with the PCV so I am sure it could be made to work better, but hopefully this will get you moving forward.

Dallas Kinzel
Thanks for the workaround, Dallas. At this point I'm still hoping to find a way to get it working "correctly", but if all else fails it's good to have a backup plan. (If I cut RIA Services out of the mix and just mock data on the client side, throw it into an ObservableCollection and wrap that with the PCV, I'm able to add items with no problem.) There must be a way to get the PCV to allow adding new items.
MylesRip
I've been looking at it a bit more and I believe the reason is not that the PCV doesn't allow new items but that the collection coming back from the Context Load() operation is ReadOnly.It sounds like this is a problem for a number of people. Some have suggested and tried to publish an "EntityCollectionView" which would support the operations we are trying to use but it doesn't sound like that class exists anywhere in a final form.out of characters will put links on another post
Dallas Kinzel
The following link has some good information (read the two pages in this blog after the linked page as well for more info):http://www.riaservicesblog.com/Blog/post/View-Model-Collection-Properties-for-WCF-RIA-Services.aspx http://forums.silverlight.net/forums/p/168676/380096.aspx Quoting from the above link: "The safest way to do an add or delete is to do them directly to the EntitySet"
Dallas Kinzel
Colin's article on "View Model Collection Properties for WCF RIA Services" answers one question. As you said, LoadOp.Entities is read-only no matter what. He then suggests wrapping the Context.EntityNamePlural (which you mentioned above) with a PCV. In my case, that would be Context.ParentObjects. What I found when I tried it is that the Context.ParentObjects has CanAdd=true, but when I wrap it in the PCV, the PCV still reports CanAddNew=false. So close and yet so far away...
MylesRip
We were so close! In my situation, I think the best answer is to take the EntitySet and wrapping it in a PCV as suggested by Colin. _pagedCollectionView = new PagedCollectionView(_context.ParentObjects); Then do additions and deletions directly to the EntitySet. _context.ParentObjects.Add(); When you do this, the added item shows up in the PCV immediately, plus you get the added benefit of the capabilities that PCV brings to the table, such as sorting, filtering, grouping, paging, etc.
MylesRip
Dallas, The information and links you provided were crucial to finding the solution I ended up with in my previous comment, but it's spread out through your original answer and the comments which might make it harder than necessary for the next person to see the final solution. If you'd like to edit your answer above to make the final solution clear, I'll mark it as the accepted answer. Alternatively I can add my own answer in a succinct form. (In which case I'd up-vote your answer as well because of its usefulness.) Please let me know what you'd like to do. Is that okay? Thanks again!
MylesRip
+1  A: 

I was just playing around with a solution yesterday and feel pretty good about how it works. The reason you can't add is the source collection (op.Entities) is read-only. However, even if you could add to the collection, you'd still want to be adding to the EntitySet as well. I created a intermediate collection that takes care of both these things for me.

public class EntityList<T> : ObservableCollection<T> where T : Entity
{
    private EntitySet<T> _entitySet;

    public EntityList(IEnumerable<T> source, EntitySet<T> entitySet)
        : base(source)
    {
        if (entitySet == null)
        {
            throw new ArgumentNullException("entitySet");
        }
        this._entitySet = entitySet;
    }

    protected override void InsertItem(int index, T item)
    {
        base.InsertItem(index, item);
        if (!this._entitySet.Contains(item))
        {
            this._entitySet.Add(item);
        }
    }

    protected override void RemoveItem(int index)
    {
        T item = this[index];
        base.RemoveItem(index);
        if (this._entitySet.Contains(item))
        {
            this._entitySet.Remove(item);
        }
    }
}

Then, I use it in code like this.

dataGrid.ItemsSource = new EntityList<Entity1>(op.Entities, context.Entity1s);

The only caveat is this collection does not actively update off the EntitySet. If you were binding to op.Entities, though, I assume that's what you'd expect.

[Edit] A second caveat is this type is designed for binding. For full use of the available List operation (Clear, etc), you'd need to override a few of the other methods to write-though as well.

I'm planning to put together a post that explains this a little more in-depth, but for now, I hope this is enough.

Kyle

Kyle McClellan
Hi Kyle, I think this approach would be good for many situations. I remember reading somewhere that when you use ObservableCollection, you loose change tracking for Add/Delete. In my case, I want the capabilities that are included with the PagedCollectionView (grouping, sorting, filtering, paging) so I think I'm better off wrapping the "context.Entity1s" EntitySet in a PCV like this: _pagedCollectionView = new PagedCollectionView(_context.Entity1s); Then perform adds/deletes directly to the EntitySet. Also, the PCV automatically refreshes when the EntitySet changes so I don't have to sync.
MylesRip
The trick with this option is you do not lose change tracking for Add/Delete because the list writes through. Also, if you want PCV functionality, you pass the list to a PCV like this._pcv = new PCV(new EntityList<Entity1>(...))I won't ever argue with someone who recommends binding to the EntitySet, but this option offers the benefits of IEditableCollectionView (Add/Remove) support.The biggest difference you hit on is whether you want the automatics refreshing of added and removed entities (updates will refresh in both). If the updating is important, definitely use the EntitySet.
Kyle McClellan
PCV implements IEditableCollectionView, but for some reason it reports CanAddNew is "false" even if the underlying EntitySet has CanAdd equal to "true" so I ended up adding/removing directly to the EntitySet instead of using the IEditableCollectionView.AddNew() method even though I'm binding to the PCV. Your approach does the same thing for add/delete and encapsulates it nicely which is great. Instead of passing in op.Entities and wrapping it in ObservableCollection, though, why not just pass in (using your example above) _context.Entity1s and wrap it in a PCV directly?
MylesRip
The PCV's IECV implementation is fully dependent on the source collection implementing IList. Since EntitySet does not implement IList, PCV.CanAddNew is equal to false. ObservableCollection just turned out to be the right combination of interfaces and extensibility to work well with the PCV. Like I mention above, this is a solution for a certain scenario. I definitely recommend binding to the EntitySet if that makes the most sense for you.
Kyle McClellan
Ah... Colin Blair pointed out that "the IEditableCollectionView implementation used by PagedCollectionView is incompatible with EntitySet which is why the CanAddNew is false." You've stumbled on across the same thing. Thanks for sharing what you've learned, Kyle!
MylesRip
A: 

For my particular situation, I believe the best fit is this (Your Mileage May Vary):

Use a PagedCollectionView (PCV) as a wrapper around the context.EntityNamePlural (in my case, context.ParentObjects) which is an EntitySet. (Using loadOperation.Entities doesn't work for me because it is always read-only.)

_pagedCollectionView = new PagedCollectionView(context.ParentObjects);

Then bind to the PCV, but perform add/delete directly against the context.EntityNamePlural EntitySet. The PCV automatically syncs to the changes done to the underlying EntitySet so this approach means I don't need to worry about sync issues.

context.ParentObjects.Add();

(The reason for performing add/delete directly against the EntitySet instead of using the PCV is that PCV's implementation of IEditableCollectionView is incompatible with EntitySet causing IEditableCollectionView.CanAddNew to be "false" even though the underlying EntitySet supports this function.)

I think Kyle McClellan's approach (see his answer) may be preferred by some because it encapsulates the changes to the EntitySet, but I found that for my purposes it was unneccessary to add the ObservableCollection wrapper around loadOperation.Entities.

Many thanks to to Dallas Kinzel for his tips along the way!

MylesRip