Models not deriving from DataObjectBase<T>

Oct 13, 2011 at 11:04 AM

Geert,

In part 3 of the CodeProject series, you say that the Model should implement INotifyPropertyChanged, IDataErrorInfo and IEditableObject.

In your samples, I see that you accomplish this my making the Model derive from DataObjectBase<T>.

Lets say that the SL application gets its data from a WCF service. The interface has data contracts that are POCO's. If you add a reference to the WCF service from SL, it generates client side code for these data contracts, and INotifyPropertyChanged is automatically implemented in that generated code (but not IDataErrorInfo nor IEditableObject).

If I want to use the full power of Catel, this means that I have to define a Model deriving from DataObjectBase<T> that is the bridge between the generated stub code and the my ViewModel. In the Model code, I need to manually code the copying of fields to and from the Model (which I want to prevent).

Is there a better way of doing this, and if so can you give some guidance on this?

Geert.

Oct 13, 2011 at 11:17 AM

The cool thing about the DataObjectBase is that it is fully serializable and can be used with WCF (but then you'll need the latest source, it's introduced in 2.3). If you want other objects to support this, the models need to implement these interface. If you use RIA services, all the interfaces are supported by the entities generated by RIA services.

Catel also supports validation attributes, so if the generated POCO objects have attribute validation (like [Required]), they are picked up by Catel as well.

So basically, you are not forced to use the DataObjectBase as model base. You can use any object you want, and Catel will try to use as much as possible on that object.

Nov 28, 2011 at 11:16 PM

I want to come back to this question...

Can you elaborate on using DataObjectBase in WCF?

In my case, I have a Domain layer that has a plain C# interface (no wcf/data contract stuff). On top of that I am putting a Service layer (WCF) for exposing functionality to the UI.

If my Domain data classes are hierarchical (e.g. a Company (with a Name property) has Divisions (List<Division>, each with a Name property) and each Division has Employees (List<Employees>, each with a Name property). How would I define this using DataObjectBase?

I presume I would have in my Service code the following classes:

- Employee: DataObjectBase<Employee> with a "dependancy property" like Name property

- Division: DataObjectBase<Division> with a "dp" Name property and an ObservableCollection<Employee> "dp" property

- Company: DataObjectBase<Company> with a "dp" Name property and an ObservableCollection<Division> "dp" property

I would have to add [DataContract] on each class and [DataMember] on each "dp" property and use the types in my WCF service methods.

On the client side I would need the same classes to use as my Model classes. Simply adding a service reference for sure would not generate these classes for me? Can you explain this step to me?

 

Nov 29, 2011 at 9:35 AM
Edited Nov 29, 2011 at 9:36 AM

The DataObjectBase is really easy to use. You don't have to add any [DataContract] or [DataMember] stuff to your classes. Just register the properties like you are normally doing for a model, and the class is ready to be used with WCF (or any other serialization methods). There is one exception, and that is when you define a base class as a property type. In that case, you will have to add all known types that might implement it on the class:

 

[KnownType(Manager), KnownType(Employee)]
public class Job : DataObjectBase<Job>
{
    // this is a "dp" alike property, but to keep it simple I defined it as simple property. The Person is a base class, but it will contain either a Manager or Employee. As you can see, I have added these as known types
    public Person Person { get; set; }
} 

To get the same classes on the client, simply add them as a linked file to the client and you have exactly the same logic on the client.

Nov 29, 2011 at 10:32 AM

Trying this out, but had problems with adding the service reference.

I have a Company : DataObjectBase<Company> which has an Address "dp" property. Address is DataObjectBase<Address>. I needed to decorate the Address class with [Serializable].

 

 

Nov 29, 2011 at 10:47 AM

Sorry, you must indeed decorate any object that is serializable with the Serializable attribute, thus:

[Serializable, KnownType(Manager), KnownType(Employee)]
public class Job : DataObjectBase<Job>
{
    // this is a "dp" alike property, but to keep it simple I defined it as simple property. The Person is a base class, but it will contain either a Manager or Employee. As you can see, I have added these as known types
    public Person Person { get; set; }
}

It must also have a public empty constructor (but this is always required for deserializing xml).

Nov 29, 2011 at 10:47 AM

Ok, now I have a service method GetCompany that returns a Company. In the e.Result of the method on the client I only see an ArrayOfXElement? I would expect an e.Result that is a Company ? What am I doing wrong?

Nov 29, 2011 at 10:56 AM

When adding the service reference, did you check the "Reuse types in referenced assemblies"? Also, make sure to include links to the same models on the client (so the types are actually known).

Nov 29, 2011 at 11:05 AM

Both Company and Address are added as linked files under Models, and I have "reuse types in all referenced assemblies" checked.

Nov 29, 2011 at 11:24 AM

I do see 2 warnings in my SL project: "Custom tool warning; unable to load one or more requested types. Retrieve the LoadExceptions property for more info" and

"Custom tool warning. The Assembly System.Xml.Linq could not be found" (I do have it referenced in my SL project).

Nov 29, 2011 at 1:12 PM

I will try to reproduce this tonight and write some documentation around this with a guide.

Nov 29, 2011 at 6:12 PM

I have been digging into this issue, and it seems that Silverlight always returns ArrayOfXElements for a WCF call (if you can find information that claims something else, please share the link). I have contact another Catel user of which I know that was using the DataObjectBase for serialization. He told me that they generated the client once, and then replaced the ArrayOfXElements by the actual type and put that into another class which has less overhead.

It is a bit cumbersome, but somehow this seems to be the only way.

Nov 29, 2011 at 6:21 PM

No, I have been testing with simple objects, and they seem to create strong types elements. Trying to figure out how I can fix this.

Nov 29, 2011 at 7:18 PM

Indeed, simple objects (not deriving from DataObjectBase) get generated on the client side as strong typed classes (with INotifyPropertyChanged implemented). However this only happens if the class is decorated with [DataContract] and only the members decorated with [DataMember] or [EnumMember] are generated (if you want the DataContractSerializer (see http://msdn.microsoft.com/en-us/library/ms731073.aspx).

 

Nov 29, 2011 at 7:27 PM

As soon as you implement the IXmlSerializable interface, the client proxy generator cannot create any strong types. It is possible to use the XmlSchemProvider to specify the type description (should be very easy with the registered properties), but unfortunately, it is static :(. Have to find a way to fix this, but this will not be available in the 2.4 version.

Nov 29, 2011 at 8:09 PM

Maybe I should have explained first why I was trying to use DataObjectBase in my WCF services.

Here is what I am trying to achieve:

If my Model class and the ViewModel class derive from DataObjectBase and ViewModelBase respectively (and so implement IEditableObject, the DataWindow<TViewModel> work as expected if
I press the Cancel button (Model instance is rolled back to orignal state), and I can use the full features of Catel (automatically having the Model updated through the ViewModel properties by using
ViewModelToModel)

-> If I use a RIA services, the generated classes also implement IEditableObject, so I can use these as my Model classes and achieve the same thing.

-> If I could use DataObjectBase in plain WCF services, I could achieve the same thing.

-> However if I use plain WCF services, the classes that are generated by add service reference don't have IEditableObjectt. If I use these as my Model, the pressing the Cancel button on a DataWindow<Tvm>
does not roll back the Model, so I can't use the auto updating of the Model from ViewModel properties using ViewModelToModel). Unless there is a way to manually trigger the pushing of data from the
ViewModel to the Model through the ViewModelToModel attributes (e.g. if the result of the DataWindow is true, so if the OK button was pressed). Is there?
==> If this would be possible this would solve my problem (It would give me the freedom of using plain WCF services, with [DataContract] decorated plain classes, making it non Catel dependant and also
accessible from other technologies like WinForms).

Nov 29, 2011 at 10:29 PM

I have found a workaround:

- I have a Person class that implements INotifyPropertyChanged (e.g. generated by Add service reference)
- My MainPageViewModel has an ObservableCollection<Person>
- I have a generic implementation of IEditableObject (using a Memento and CareTaker class as described at http://www.silverlightshow.net/items/ModalDialogs-IEditableObject-and-MVVM-in-Silverlight-4.aspx)
- I pass a Person to the PersonViewModel constructor.
- The PersonViewModel as a 2 level Model:
    - a [Model] of type CareTaker<Person> called EditablePerson
    - a [Model] of type Person called Person that refers to [ViewModelToModel("EditablePerson","Target")]  // Target being the property of the CareTaker where the Person is stored
- The other ViewModel properties refer to the Person via [ViewModelToModel("Person")

 
Nov 30, 2011 at 10:08 AM

What I was looking for in my previous post is probably an additional option:

In your "part 3" document on CodeProject in 3.8 Saving, you describe 2 options that are available:
3.8.1 = using ModelAttribute decorations
3.8.2 = manual mapping and saving

I am basically asking for:
3.8.3 = on demand mapping and saving (using ModelAttribute decorations, but only pushing data from ViewModel to Model on demand through a method call on the ViewModel).
That way Catel can do all of it's wonderful stuff in the View and ViewModel and can work on top of (plain or INPC only) Model classes.

Nov 30, 2011 at 1:19 PM

I am going to respond to all 3 posts, so here they come:

1) No, if there is no support for IEditableObject, you need to either clone the object or store the old values somewhere manually (but care, deep graphs can become complex)

2) ok, nice info.

3) Hmmm, good idea. I will create a task for this (see http://catel.codeplex.com/workitem/7041, will be implemented in 2.5)