New Posts New Posts RSS Feed: Sample Repository Implementation
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Sample Repository Implementation

 Post Reply Post Reply
Author
chuckc View Drop Down
Groupie
Groupie


Joined: 27-Feb-2010
Posts: 54
Post Options Post Options   Quote chuckc Quote  Post ReplyReply Direct Link To This Post Topic: Sample Repository Implementation
    Posted: 12-May-2010 at 9:09pm
 
There have been a number of postings advocating using a Repository to wrap EntityManager access, so I thought I'd toss out a simple sample implementation to generate discussion.  This is by no means production code, but should be basically functional. 
 
Some obvious opportunities for refinement would be introduction of an IRepository interface definition, and most likely replacing the Entity type constraint with your own entity base class. 
 
What else?  Any glaring problems?  Pros/cons?  Thoughts on generic class versus generic methods?
 
 

    public class Repository // base this on an IRepository interface definition

    {

        private MyEntityManager _defaultManager;

 

        public Repository()

        {

_defaultManager = MyEntityManager.DefaultManager;

        }

 

        public IEnumerable<T> FindAll<T>(Expression<Func<T, bool>> expression)

where T : Entity

        {

            IEntityQuery<T> q = new EntityQuery<T>().Where(expression);

            return _defaultManager.ExecuteQuery(q);

        }

 

        public T Single<T>(Expression<Func<T, bool>> expression) where T : Entity

        {

            IEntityQuery<T> q = new EntityQuery<T>().Where(expression);

            return _defaultManager.ExecuteQuery(q).FirstOrDefault();

        }

 

        public T First<T>(Expression<Func<T, bool>> expression) where T : Entity

        {

            IEntityQuery<T> q = new EntityQuery<T>().Where(expression);

            return _defaultManager.ExecuteQuery(q).First();

        }

 

        public T Get<T>(Guid id) where T : Entity

        {

            return _defaultManager.FindEntity(new EntityKey(typeof(T), id)) as T;

        }

 

        public void MarkForDeletion<T>(T entity) where T : Entity

        {

            entity.EntityAspect.Delete();

        }

 

        public T Create<T>() where T : Entity

        {

            return _defaultManager.CreateEntity<T>();

        }

 

        public void SaveAll()

        {

            _defaultManager.SaveChanges();

        }

    }

 

 

Example usage:

 

Repository repository = new Repository();

IList foo = repository.FindAll<Contact>(c => c.Person.LastName == "Doe").ToList();

 

 

Looking forward to other's feedback.

 



Edited by chuckc - 16-May-2010 at 6:45pm
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 01-Jun-2010 at 11:09am
Thanks a ton, Chuck!
 
This begins the work of providing some insulation and elasticity between the DevForce persistence machinery and consumers such as the UI. That is an essential step and I'm pleased to see you take up that cause.
 
Some important next steps:
  • Base on an interface (essential for testability)
  • Virtual members
  • Use dependency inversion (consider IoC); should not "new" an EntityManager ; 
  • I would never use EntityManager.DefaultManager in production code; this is a demo feature. Avoid static classes and methods when possible ... and with EM, it's always possible. See also remarks at the bottom of this post.
  • I typically configure an EM outside the repository and inject it; sometimes I further configure it inside the repository (e.g., for Saving/Saved event handling).
  • I distinguish by verb choice whether the repository will look only in cache (Find) or potentially go to the server (Get); in your example, you defined a Get that DevForce would have called a Find.

    I don't mind if you develop a different vocabulary; I only ask that you be clear on the difference and be consistent.

    [Hmm. Now that I re-read your code, I see that you reverse the standard DevForce meanings of Find and Get. I'd advise against that because people who become familiar with DevForce terminology may be confused as I was.]
  • GetById<T> method would be a typical method in a generic repository.
  • I don't think a Repository should have a Create method. Repositories don't know how entities are created. In your example, Create insulates the caller from direct contact with the EM (good!) but that's it. Better to have Add (for new) and Attach (for existing) methods for associating entities with EMs.
  • A Repository for Silverlight clients would use async calls and requires a different API. I haven't settled on a good set of practices for this myself. Still feeling my way.

I know that many repository authors favor these general purpose repositories. They often pin them to particular "Aggregates" by defining a Repository<T> where T is an aggregate.

I've found that while this may work well for the short-lived repositories of web applications, it isn't so great for RIA and smart client apps.
 
I prefer Repositories built specifically for application "modules" ... for the larger, coherent workflows that we tend to put in separate modules such as "Order Entry", "Inventory", "Logistics", "Accounting".
 
When I build these out, I give the repositories fully semantic "query" methods that are specific in purpose and clear in intent:  GetCustomers(String), GetCustomer(Guid), GetOrdersForCustomer(Customer).
 
Yes, these are not composable (they return entities). Sometimes you need to return a query object that the caller can elaborate. Not afraid of those at all. But if the use cases call for static query, I'd tend to encapsulate that static query in a method to reduce the need for (risk of) the caller getting the query right.
 
Your FindAll, which takes an expression, is a useful hybrid; it enables flexibility in the predicate while taking over the other aspects of the query.
 
I do not fear that the repository API will become to large. If it starts heading in that direction, I treat that as a code smell. Maybe the module is focused enough. Maybe it hosts multiple workflows that should be divided among multiple repositories.
 
Btw, two repositories may share the same EntityManager instance ... but you should know why they do and be aware of the consequences. Calling "Save" will affect all of the repositories that use that EM. Make sure you appreciate the implications.
 
Please revise your definition of Single. "Single" is a real LINQ verb that means "must have exactly one" (btw, DF 2010 supports both Single and SingleOrNullEntity). You defined it as "the first of many ... or the null entity if there are none". These are not the same at all.
 
So much for my first thoughts. Remember, I'm offering my opinion ... not law. I called out a few absolute "Do Nots" ... but only a few and even here all I'm saying is that you should prepare very good arguments for rejecting my advice. For the most part, just having a repository is more important then doing it one particular way.
 
Thanks again, Chuck, for pioneering. Pioneers take arrows ... hope you don't mind mine :-)

Ward 
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 07-Jun-2010 at 3:18am
For a useful alternative approach, see Scott Allen's recent "Testability and Entity Framework 4". It's an excellent short paper.  Read it first ... I'll wait.
 
Back? You probably noticed that his paper did not account for the async nature of Silverlight data acquisition nor the DevForce distinction between looking locally for data (Find) and looking remotely (Get). He was talking about EF which lacks these capabilities and MVC which lacks the stateful client of a Silverlight application. We should be able to look past this to the essential ideas and the flow of the argument.
 
He adopts the standard Single-Aggregate-Repository (IRespository<T>) ... and it's difficult to argue with a standard.
 
Notice that his UnitOfWork (UoW) brings the repositories together ... and you call "Save" on the UoW, not on any repository. It is a facade over the module's member repositories
 
public interface IUnitOfWork {
    IRepository<Employee> Employees { get; }
    IRepository<Order> Orders { get; }
    IRepository<Customer> Customers { get; }
    void Save();
}
 
 
One important benefit of this facade is that the UoW grows by adding repositories; it is unaffected by the evolution of any of the individual repositories it exposes. If you follow my "recommendation" - pooling all module queries into a single repository, regardless of the types involved - you will modify your master repository when any query method is changed or added.
 
For example, if you add a custom GetEmployee method, you modify the same repository that gets Orders and Customers. That violates the "Single Responsibility Principle" (SRP). That makes the repository bigger and less coherent. It's harder to test and to fake.
 
Why did I recommend such a repository. Clearly it is inferior.  Well, I found that some shops were overwhelmed by the number of repositories they had to maintain when each aggregate had its own repository. They wanted one place to go for all queries.  One of our clients was really pissed off. Maybe I should have fought city hall but I eventually stopped fighting it. I was just happy they used a repository and didn't throw us out of the account!
 
If you are comfortable with multiple, single-aggregate repositories managed under the umbrella of a module UoW ... go for it! I approve ... it's better design. But I can report that the world does not end when the repositories are de-factored into one.
 
If you follow Scott's lead, I'm almost certain you'll end up with IEmployeeRepository rather than IRepository<Employee>. Each entity aggregate is different ... the way you use it is different .. and custom methods are bound to arise. IEmployeeRepository inherits from IRepository<T> and you may get some reuse from the core implementation. But the same impulse that drove you to repositories in the first place is going to drive you to customize each repository to the needs of the module.
 
This means your UoW will look more like:
 
public interface IModuleUnitOfWork {
    IEmployeeRepository Employees { get; }
    IOrderRepository Orders { get; }
    ICustomerRepository Customers { get; }
    void Save();
}
 
Nothing wrong with that in my book.
 
I see Scott offering methods that return IQueryable<T> and letting the consumer party on that. I think that's going to be trouble for most developers, especially when you have to deal with async and possible exceptions. Those Includes and OrderBys and who knows-what-else are persistence concerns leaking into the consumer classes (e.g., ViewModels).  I'd rather pull all of that into the repository even at the cost of a repository API with more methods and fewer (or no) composable queries.  That's my customer experience talking; you are welcome to do otherwise.
 
My other remarks (e.g., don't have Create methods) remain standing either way.


Edited by WardBell - 07-Jun-2010 at 3:26am
Back to Top
chuckc View Drop Down
Groupie
Groupie


Joined: 27-Feb-2010
Posts: 54
Post Options Post Options   Quote chuckc Quote  Post ReplyReply Direct Link To This Post Posted: 26-Jun-2010 at 4:55pm

Hi Ward,

I've incorporated many of your suggestions, and things are coming together pretty well, but I’ve got a follow-up question.

Regarding Find/Get nomenclature:  I am switching over to make sure my Repository uses Find/Get in the same sense that Devforce does.  What is a good technique for getting an entity that corresponds with the EntityManager.FindEntity method?  After no small amount of searching, I haven’t found any immediately obvious Get-style methods.    

        public T Find<T>(Guid id) where T : Entity

        {

            // retrieves from cache only

            return _defaultManager.FindEntity(new EntityKey(typeof(T), id)) as T;

        }

              public T Get<T>(Guid id) where T : Entity

        {

            // there is no GetEntity – what’s a good technique?

            return _defaultManager.GetEntity(new EntityKey(typeof(T), id)) as T;

        }


I’d appreciate any recommendations.

 

Back to Top
DavidKozikowski View Drop Down
Groupie
Groupie
Avatar

Joined: 02-Feb-2010
Location: Aurora, IN
Posts: 59
Post Options Post Options   Quote DavidKozikowski Quote  Post ReplyReply Direct Link To This Post Posted: 29-Jun-2010 at 9:08am
"If you follow Scott's lead, I'm almost certain you'll end up with IEmployeeRepository rather than IRepository<Employee>. Each entity aggregate is different"

 
Yes I believe that is the way to go. I’m now at the point of injecting the entity manager into a specific Repository. I have been using MEFedMVVM, I like it a lot very simple and clean.

I’m looking for a little input on what EM I should inject. I’m also trying to keep the entire application Blendable. I’m thinking about ways to do this.  So has anyone gone down this path before? How are you making your Repositories work in Design Time and runtime?

 

It would be nice to see/ have a sample sample of a repository that is using the NorthwindIB , better yet Templates for creating a IB Silverlight 4 MVVM app with a Repository and templates to add a new Repository based on a IB Entity. (I love templates and hate to type)

 

Thanks!

Back to Top
browniepoints View Drop Down
Newbie
Newbie
Avatar

Joined: 21-Aug-2010
Posts: 1
Post Options Post Options   Quote browniepoints Quote  Post ReplyReply Direct Link To This Post Posted: 21-Aug-2010 at 7:00pm

Ward,

   I read the article and the UoW bothered me. I agree with what you said about separating the UoW from the Repository (and injecting it into the Repository for Attach/Add operations). I haven't played much with DevForce yet but it seems to meet my needs better than the CURRENT .NET stack. I prefer POCO but it's nearly impossible to get EF Code First and RIA Services to cooperate.
   As a quick question, do you plan to support Windows Phone 7 with DevForce?
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 21-Aug-2010 at 7:41pm
@chuckc - sorry for long delay in response. Don't know how I missed it.
 
Nothing wrong with the GetById method you wrote but it isn't the prettiest is it. You're kind of stuck with it when trying to write a generic method of this kind because you don't know the key property for every entity, T. For example, it might be called "Id" or "ID" or "CustomerId" or ...   See what I mean?  The method you wrote relies on the fact that DevForce knows the property ... because every entity must have a key, that key would have been discovered when we built the metadata for the entity, and so we know what to do.
 
You have assumed that the id will be a GUID. That's great if all entity ids are of a single type. Otherwise, you'd have to abstract it further and use object ... thus giving you no static strong typing. Oh well. 
 
@brownie - we have POCO support if that's what you prefer. The downside ... and it's intrinsic to POCO ... is that you write all the CRUD methods and other stuff we pack into our entities ... which takes time and testing. It's your option.
 
WP7? Man ... do I WANT to be there. But WP7 doesn't support IQueryable<T> nor does it support Reflection.Emit.  The first version of WP7 targets consumer applications, not business applications so these features have been back-burnered. I understand that business decision. Makes it tough for us (and for RIA Services too).
 
We'll try to come up with a reasonable answer by year end.
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 21-Aug-2010 at 7:44pm
@David K - I feel you man. I'd like to get some of those up as well. PrismExplorer shows one way to do repositories.  See this forum post for discussion of design time data.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down