Advice needed: Generic IRepository and XAML

2 views
Skip to first unread message

Erick Thompson

unread,
Mar 3, 2010, 7:10:14 PM3/3/10
to altnet...@googlegroups.com
I have run into a situation where I can't see any good solutions, and I'm hoping someone has run into a similar situation.
 
I have a generic IRepository<T> interface which is used for loading data from a store. I have the generic parameter as I want to allow typed queries (the queries will end up hitting a ANDS service). I want to use classes implementing interface this in custom controls and ModelViews in a Silverlight 4 application.
 
The problem is that you can't have a generic interface in a class without having a generic type on the class.
 
public class ControlBase {
  protected IRepository<T> _repository;
}
 
will not compile. As this is a base class, I can't specify the T unless I use a inherited base DataClass. I would like to use POCOs if possible.
 
In the fantasy world in my head, I would simply make the base class generic, and Bob's your uncle. However, I want these classes to be easily consumed in XAML, which doesn't have a good way to instantiate a generic type.
 
What I ultimately want is for the user to be able to create the control/form (bound to ModelView) without a generic type, but that the data access occurs in a typed way. For example:
 
public class CoolControl : ControlBase {
  public CoolControl() {
    _repository = IoC.Resolve<IRepository<SomeConcreteDataType>>();
  }
 
  public LoadData () {
    // do some typed IQueryable stuff on repository
  }
}
 
With this, the user can specify the control/ViewModel in XAML, and the control/ViewModel author gets the benefit of typing.
 
Is there any good way to do this or I am stuck?
 
Thanks,
Erick
 

Kelly Leahy

unread,
Mar 3, 2010, 11:24:53 PM3/3/10
to altnet...@googlegroups.com
If we take a step back, and think about what you're saying, I think
you'll realize it doesn't make sense.

It sounds like you're saying you want to have some generic object that
you hold onto in your class but you don't want to know what the type
of that object is. However, you want to be able to write code that
uses that object. There's not any sensible way that the compiler
could type-check your code without knowing what T is, which is why you
either need to specify a value of T in your field (i.e. bind the
parameter to a type), or you need to make T a generic parameter of
your type (i.e. leave it as an 'open' generic and 'provide' the T from
the use site of your class).

The thing I'm not really getting is why the control author cares what
T is. It seems to me that if you're doing VM / PM pattern well, you
should be striving to have very little (if any) code in your controls,
and all of the interaction should be through bindings to the VM (which
are - by definition - untyped, though if you use something like the
binding frameworks that Glenn has been trying to build, you'll see
that you can use stronger typing in your bindings).

What you can do, and I've done this in the past, is not make the
repository generic, but rather make its methods generic (i.e.
Query<T>(...) instead of IRepository<T>::Query(...)) but this won't
allow you to resolve different repositories by type from the IoC
container.

The other thing I'd ask yourself is whether inheritance is the right
solution to your problem. In nearly every case in my past that I
thought it was, I turned out to be wrong. You might also think about
whether the type of the repository is important to be in a base class,
or whether you can get the same effect by having the base class be
abstract, providing a few methods to 'hook' into the derived class
implementations, and then you can have the type parameter on the base
class and have the deriving class provide the "T" when it derives. In
other words, you could make:

public class FooContainerControl: ContainerControlBase<Foo> { ... }

We do this a lot in our UI code (though we do it with the PMs, not the
controls, I personally hate base classes for controls - it never works
out well, we use interfaces exclusively for them). For instance, we
have a:

public class EditorGridPresentationModelBase<T> where T: IItemCore, class
{ ... }

and then for Table and Variable (both IItemCore implementers), we have

public class EditorGridPresentationModel: EditorGridPresentationModelBase<Table>
{ ... }
(in the Tables namespace), and

public class EditorGridPresentationModel:
EditorGridPresentationModelBase<Variable>
{ ... }
(in the Input namespace).

Kelly

> --
> You received this message because you are subscribed to the Google Groups
> "Seattle area Alt.Net" group.
> To post to this group, send email to altnet...@googlegroups.com.
> To unsubscribe from this group, send email to
> altnetseattl...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/altnetseattle?hl=en.
>

Aeden Jameson

unread,
Mar 3, 2010, 11:44:27 PM3/3/10
to altnet...@googlegroups.com
I'm not familiar with all the details of your situation, but a few
thoughts come to mind.

* Don't use a generic repository. e.g interface InvoiceRepository

- The interface will also help express intent of your code better
where ever the interface is used

- The implementation of InvoiceRepository could be backed by
a generic repository to achieve technical quality factors like
removing duplication, better reuse etc...

If you would like to use a generic repository and are already on c#
4.0 I believe you could use contravariance like this,

//Marker Interface
public interface AggregateRoot

public OrderBook : AggregateRoot


public interface IRepository<T>


public interface IOrderBookRepository :
IRepository<OrderBook>


public class ControlBase {
protected IRepository<AggregateRoot> _repository;
}


public class CoolControl : ControlBase {
public CoolControl() {

_repository = IoC.Resolve<IOrderBookRepository>();
}

public LoadData () {
// do some typed IQueryable stuff on repository
}
}


HTH


Cheers,
Aeden


On Wed, Mar 3, 2010 at 4:10 PM, Erick Thompson <erick.t...@gmail.com> wrote:

> --
> You received this message because you are subscribed to the Google Groups
> "Seattle area Alt.Net" group.
> To post to this group, send email to altnet...@googlegroups.com.
> To unsubscribe from this group, send email to
> altnetseattl...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/altnetseattle?hl=en.
>

--
Cheers,
Aeden

Software Musings @
http://aedenjameson.blogspot.com/

Justin Bozonier

unread,
Mar 4, 2010, 1:27:26 AM3/4/10
to altnet...@googlegroups.com
I agree with Kelly here. I'd be interested to see the code you have
now and see the code you'd like to change it to (whether or not it
compiles) just to get a complete set of thoughts from you.

Also, no cool control I know would take a dependency on an IOC container. :)

On Wed, Mar 3, 2010 at 4:10 PM, Erick Thompson <erick.t...@gmail.com> wrote:

Erick Thompson

unread,
Mar 4, 2010, 1:31:03 AM3/4/10
to altnet...@googlegroups.com
Kelly,
 
Thanks for the great response. I agree with your comments about inheritance (it is usually ugly). I love the idea about making the interface non-generic and moving the generic parameter to the method. One key detail that I forgot to mention is that this is a platform, and not an application, hence my inability to know what T is.
 
I completely agree about keeping very little code in the ViewModel (except for UI specific code). My ultimate goal is to have a 100 level programmer be able to create a new ViewModel with very little knowledge of how it works, and do most of the presentation customization via XAML. The base VM contains all the plumbing to actually make things happen, and the derived VM is the one used by the dev. They would be expected to write something like the following.
 
class MyDataObjectViewModel : FooViewModel {
  public override IQueryable<DataObject> Query(IQueryable<DataObject> basequery) {
    var query = from d in basequery
                        select d; // or some other LINQ query)
 
    return base.Query(query);
  }
}
 
The base ViewModel actually handles the population and management of the items in the resulting collection (which the UI/Control binds to in a non-generic way). If I'm not mistaken, having the generic parameter on the method should work for this. I'll have to try it out in the morning.
 
Aside from that, do you see a better way to do this? I generally don't like inheritance, but it seems like a good way to keep the messy details away from the end user/dev.
 
Also, as an aside, which project of Glenn's are you referring to? It sounds interesting...
 
Thanks,
Erick

Erick Thompson

unread,
Mar 4, 2010, 1:34:55 AM3/4/10
to altnet...@googlegroups.com
Aeden,
 
Ah, to be on 4.0, and have the contravariance love. :) Alas, this is an Azure app, which is 3.5sp1 for the foreseeable future (at least through this project).
 
I should have mentioned that this is a platform, so I don't know the actual type in the ViewModel or repository (I don't know if it's Invoice or Sale or ShoppingCart). With 4.0, I could use your approach. As it stands, Kelly suggested a generic method, which is a good solution.
 
Thanks,
Erick

Kelly Leahy

unread,
Mar 4, 2010, 1:39:51 AM3/4/10
to altnet...@googlegroups.com
Actually, I think in that case your best bet is to have the generic
parameter on the base class. Why is it that you don't like that?

Glenn's project that I was referring to is a side project, AFAIK, that
he was working on for binding using a fluent interface in code, rather
than specifying your bindings in XAML. AFAIK, it's still in the
"exploratory" stages, but I'm sure he'd love to talk about it with you
if he has time.

I think the control shouldn't have any of this stuff in it, though.
It seems to me that the control code should be empty if at all
possible. All of the "view" logic should be in the VM. I don't take
as strong a view of the VM/M separation as some people do. In my
opinion it's ok to have a lot of code in the VM, so long as that
aligns with your testing needs and goals. Our issue right now is that
it doesn't ;)

Kelly

On Wed, Mar 3, 2010 at 10:31 PM, Erick Thompson

Erick Thompson

unread,
Mar 4, 2010, 1:49:20 AM3/4/10
to altnet...@googlegroups.com
I'll answer backwards. Why am I using an IoC container?
 
Well, right now, the repository is very simple - it takes a query and runs it against an ANDS service and returns the results. However, a great deal of the data in these queries is almost read-only (changes maybe once a year), and the target audience consists of users with very limited bandwidth. So in the future (v1.x), we want to be able to have a different repository, one that teases apart the query and uses local data where possible. I could use inheritance to do this, but it seems like a better fit for IoC, as I don't need to mess with the original repository.  Also, it makes testing much easier. :-)
 
As for what I am trying to do, I outlined that it my response to Kelly. It's a platform that consists of a VM and a specialized control that leverages the VM. The control really isn't useful without the VM. Instances of the control + VM will be created by junior devs, and so the intent is to create a derived VM that is dead easy to use, where all you need to do is plug in a LINQ query. Having Type information makes writing LINQ queries _much_ easier and less error prone. The interface currently consists of two properties (more will be added later, but they aren't generic).
 
IQueryable<BaseDataObject> QueryBase;
IEnumerator<BaseDataObject> Query(IQueryable<BaseDataObject> query);

The base VM exposes QueryBase for modification to the 100 level VM, and then passes that query to the Query method on the repository. The base VM then handles the population of the appropriate collections that the control is bound to.
 
Does this make more sense?
 
Thanks,
Erick

Erick Thompson

unread,
Mar 4, 2010, 1:56:36 AM3/4/10
to altnet...@googlegroups.com
Amen to the VM/Control separation. I'm working with some existing code, so I don't have as much latitude as I'd like, but in my mind, the control should contain rendering code only (when the available UI widgets are lacking, for animation, etc). The ViewModel should contain all the logic/collections/etc for the control. With SL4 and commands, we are able to get a lot closer to that goal. I hope we're all the way there soon.

If I have the generic parameter on the base class (I assume you mean base ViewModel), doesn't that imply that I then have to pass an untyped query to the repository? Then do a Cast<DataObject> operator on the way back (if needed)? I can't see any technical reason that it wouldn't work, but it seems dangerous somehow. Have you used a purely non-generic repository style system before? Any problems?
 
Thanks, this has been extremely valuable!
 
Erick

Kelly Leahy

unread,
Mar 4, 2010, 3:10:02 AM3/4/10
to altnet...@googlegroups.com
No... I think you can still have the strongly typed repository in the VM base.

public class VMBase<DataType, RepositoryType>: INotifyPropertyChanged
where DataType: DataObjectBase,
RepositoryType: IRepository<DataType>
{
private readonly RepositoryType _Repository;

public VMBase(RepositoryType repository)
{
_Repository = repository;
}

// some accessors for commonly used repository operations...
protected IQueryable<DataType> GetAllObjects()
{
return _Repository.GetAll();
}

// allow access to 'private' repository, without the caller having
direct access to the field...
protected void AccessRepository(Action<RepositoryType> repositoryAction)
{
repositoryAction(_Repository);
}
...

protected void OnPropertyChanged<T>(Expression<Func<T>> propertyAccessor)
{
// crack the property name from the expression and use it to fire
the PropertyChanged event.
}
}

public class BooksRepository: BaseRepository<Book>
{
public IQueryable<Book> GetBooksBySubjectFilter(string subjectFilter)
{
...
}
}

public class BooksVM: VMBase<Book, BooksRepository>
{
public BooksVM(BooksRepository repository)
: base(repository)
{
}

public string SubjectFilter
{
get { return _SubjectFilter; }
set
{
if(_SubjectFilter == value)
return;
_SubjectFilter = value;
_RefreshBooksList();
OnPropertyChanged(() => SubjectFilter);
}
}

private void _RefreshBooksList()
{
IQueryable<Book> booksList;
AccessRepository(r => booksList =
r.GetBooksBySubjectFilter(_SubjectFilter));
// ... do something to put booksList in a UI accessable
collection (ObservableCollection, etc.)
}
}

... yada yada yada...

get the idea?

On Wed, Mar 3, 2010 at 10:56 PM, Erick Thompson

Kelly Leahy

unread,
Mar 4, 2010, 3:12:49 AM3/4/10
to altnet...@googlegroups.com
note... missing a 'where' before RepositoryType on the third line of
code below...

otherwise, I think the skeleton code will compile if you replace the
comments with some 'meat'. Obviously gmail isn't the best C#
compiler, so you may have to do some tweaking to get a working sample.

Erick Thompson

unread,
Mar 4, 2010, 1:42:51 PM3/4/10
to altnet...@googlegroups.com
Doh! Of course, I can declare the generic type, but I don't need to specify it. Sometimes you can't see the forest for the trees.
 
The author of the derived VM will need to declare the type of the repository, but that shouldn't be too difficult. In your code below, both generic parameters can be collapsed, as they should always be the same (repository will always return data objects).
 
Thanks for your help Kelly!
 
Erick

Kelly Leahy

unread,
Mar 4, 2010, 2:35:47 PM3/4/10
to altnet...@googlegroups.com
I don't think you want them collapsed, since you want the user to be
able to make use of methods that are on their "special" derived
repository (that might not be in IRepository<T>). This is how you do
OCP for this type of design.

kelly

Glenn Block

unread,
Mar 5, 2010, 4:55:19 AM3/5/10
to altnet...@googlegroups.com
I would not depend on an IoC directly within your user control. It's making the code more brittle and less resable. If the component needs dependencies, inject them. As far as testabilty goes, why are you testing user controls in the first place? The place to test is the VM and it's related services. If you need to pipe dependencies into a user control, a safer way is to use attached behaviors, or even to have a locator in your app resources.
 
As far as trying to make things easier, be very careful about leaky abstrations as they easily sneak in when you attempt to make things easy. I worry about the archiecture you are describing in the same way i worry about web forms.  It's introducing tight coupling around components that are supposed to be separated via a seperated presentation pattern, it's a contradiction in terms! A view also has no business knowing about a repository, it's a completely non-UI concern.
 
I would keep the UI as agnostic as possible as to the model behind it. Move all dependencies into the VM. If you want to have a base VM that provides some standard capabilities, then great do that. Don't push any knowledge of the VM or it's concerns into the UI. Let your VM get your repo, let your VM do the data access. The view should simply bind to the VM, and invoke it's members optimally through databinding, but possibly directly depending on the complexities.
 
My $.02.

Erick Thompson

unread,
Mar 5, 2010, 6:12:31 AM3/5/10
to altnet...@googlegroups.com
Thanks Glenn. I wish I had a whiteboard, as that is where I seem to be able to explain things the best. Especially at 2:30 am. :)
 
Testing
The vast majority of the testing will be on the ViewModel - the View will have no concept of repositories. Everything in the View will driven via Bindings in the ViewModel (which Commands in SL4 make much easier). However, I will have some sanity/integration tests running on the View, just to make sure that any changes didn't breaking something unexpected.
 
Let me describe the situation, and perhaps that will be more clear (once I get clearance to release under MS-PL it will be much easier). The component that I am writing has two coupled parts - the VM and the Control.
 
ViewModel
The ViewModel handles the heavy lifting in terms of loading from the repository, adding to its ObservableCollection, exposing Commands for saving, editing, etc. All controls bind to an instance of the ViewModel and the binding is performed in XAML via a static resource on the Control. The ViewModel is required to be derived from in order to add query logic.
 
AppViewModel (abstract & generic)
   ^
   |
DataObjectViewModel (concrete and non-generic)
 
However, there is also a chain of inheritance due to the need to query and display aggregate data. While it could be done via the query, that results in some horrid queries. My thinking at this point is to have an additional class that is derived from AppViewModel that handles the one-to-many nature of aggregated data.
 
AppViewModel (abstract & generic)
   ^
   |
AggAppViewModel (abstract & generic<AggEntity> and <Entity>)
   ^
   |
AggDataObjectViewModel (concrete and non-generic)
 
Control
Along with ViewModels is a hierarchy of Controls. The Controls all expose a set of similar functionality. For example, a Grid control might have a basic grid, a filterable grid, a savable grid, etc. They all ultimately get data from the same source (i.e., ViewModel), but expose difference functionality of the data. For those controls that require extra data, the type of the ViewModel would need to be an instance of the more specific VM (e.g., derived from AggDataObjectViewModel)
 
IoC vs. DI
As I mentioned, at some point in the not too distant future, the abilities of the repository will be expanded, without impacting its core functionality. A perfect example of this is caching. Due to the intended use of the ViewModels, there are going to potentially be hundreds of derived repository classes (one for each Data Object type). When caching gets added, it won't be added to every Data Object (some DOs are better suited to caching than others), so if I used DI, I would need to now add type dependent logic in each repository. Using IoC, I can update the configuration to say "if you're a repository for type X, use this cache enabled class". Make sense?
 
Sorry for the long winded response, and apologizes for any typos. My biggest fear right now is that no matter how simple I keep things, there are going to be a _lot_ of derived classes floating around. I don't see a way around this, unless I go totally non-typed. Ah, the joys of software complexity. To be honest, I love this stuff, which I am up at 3am working on it.
 
Thanks for the help Glenn!
 
Erick
Reply all
Reply to author
Forward
0 new messages