none
GetById<T>

    Question

  • I am wondering if it is possible to have generic GetById<T>() method? I have read a couple of posts on this and I can't seem to figure out how to get this working with RIA?


    Thanks in advance...

    Saturday, March 19, 2011 10:52 AM

Answers

  • OK, I found an email of mine that has the code. What you need is a custom DomainServiceDescriptionProvider, there is a sample at http://archive.msdn.microsoft.com/RiaServices/Release/ProjectReleases.aspx?ReleaseId=2659 which shows how to create one.

    First, this is the class I am using for the generic Query command, this was written for EF:

    public class GenericQuery<TDomainService, TEntity> : DomainOperationEntry 
            where TDomainService : DomainService, IGenericDomainService
            where TEntity : EntityObject
    {
            public  GenericQuery(string operationName)
                : base(typeof(TDomainService), operationName, DomainOperation.Query, typeof(IQueryable<TEntity>), new List<DomainOperationParameter>(), new AttributeCollection())
            {
            }
            public override object Invoke(DomainService domainService, object[] parameters)
            {
                return ((IGenericDomainService)domainService).GetEntities<TEntity>();
            }
            
            
        }


    I also have an interface named IGenericDomainService which I apply to the DomainService. Then I implement the interface by adding this to the DomainService:

    IQueryable<T> IGenericDomainService.GetEntities<T>()
    {
        return this.ObjectContext.CreateObjectSet<T>();
    }


     

    OK, now for the DomainServiceDescriptionProvider. I override the GetDescription method like this:

    public override DomainServiceDescription GetDescription()
    {
        DomainServiceDescription returnDescription =  base.GetDescription();
        returnDescription.AddOperation(new GenericQuery<TestDomainService, Book>("GetBooks"));
        return returnDescription;
    }


    So, a little explanation. During the GetDescription you can add new DomainServiceOperation objects to the DomainServiceDescription by calling AddOperation. A DomainServiceOperation has an Invoke method which is the actual payload. During compilation and runtime, all of the CRUD methods you create in your DomainService are found using reflection, wrapped in DomainServiceOperations as the Invoke, and are then stored in the DomainServiceDescription. What I am showing here is how you can add your own DomainServiceOperations to the DomainServiceDescription dynamically. To take it a step further, it might be possible to read your NHibernate model and automatically loop through your objects creating methods, I will leave that up to you.

    My only caution here is that I am not sure this is actually a good idea. Part of the reason that the DomainService exists is that you are supposed to lock down all of the queries to make sure that only the data that is supposed to be queried is actually queried. Moving the queries into a generic load method seems a littler counterproductive to me.

    Sunday, March 20, 2011 4:50 PM
  • You could simplify that code down a little if you refactored your DomainService the same way that the LinqToEntitiesDomainService is written. I think that would include:

    1. Move your session object to be a private member of the class
    2. Perform the session = OpenSession() in the initialize method of the DomainService
    3. Move the CloseSession into the Dispose method

    I am not sure what you are doing in ExceptionHandler.Handle(ex). Your query method is already wrapped inside the DomainService's exception handling and you can check the error and re-throw a different one from the OnError method. Generally, you don't need your own try-catch in a DomainService unless you know there are recoverable errors.

    All those changes would change your query to:

    public IList<T> GetList<T>()
    {
        ICriteria query = Session.CreateCriteria(typeof (T));
        return query.List<T>();
    }

    ....but, I can't help wondering if a strongly typed version of that query wouldn't be a single line of code like EF has.

    Monday, March 21, 2011 8:08 AM

All replies

  • may be you can have a look at the below link. Seems irrelevant to you but worth reading it. Its the way to handle generic types

    http://msmvps.com/blogs/deborahk/archive/2010/07/19/the-world-s-simplest-silverlight-ria-poco-example.aspx

    Sunday, March 20, 2011 9:18 AM
  • Hi, thanks for the reply. I am missing something I think... How does this article deal with generics? I don't want to have to code every entity's getById() and getAll() methods on the service layer. Is there not a way to do it with generics?


    Thanks again...

    Sunday, March 20, 2011 10:25 AM
  • There is a way, I just haven't publicized it. I don't have the code handy but I will post something tomorrow.

    Sunday, March 20, 2011 11:35 AM
  • WCF RIA Services does not expose generic domain service methods for the client to call. RIA is strongly-typed to make it easier to reason about the behavior.

    What do you want to use that feature for?

    (Edit: Just saw Colin's post above. I'm looking forward to hear what he has to say!)

    Sunday, March 20, 2011 11:36 AM
  • @ColinBlair: Thanks I look forward to seeing the code!

    @jcooke: Well I guess coming from NHibernate, I just find that it that it is very tedious having to code a method for every entity's getById() and getAll() methods. They are so commonly used that it seems like having a generics method would cut down on a ton of code... Am I missing something?

    Sunday, March 20, 2011 11:42 AM
  • They are so commonly used that it seems like having a generics method would cut down on a ton of code... Am I missing something?

    I am not sure if the amount of code saved is really worth it. Using methods in the DomainService, you have lots of methods that look like:

    IQueryable<TEntity> GetAllTEntities()
    {
        return context.TEntities;
    }
    IQueryable<TEntity2> GetAllTEntities2()
    {
        return context.TEntities2;

    Where using generics, you have lines of code that look like:
    AddGetMethod<TEntity>(GenericGetAll<TEntity>);
    AddGetMethod<TEntity2>(GenericGetAll<TEntity2>); 

    Maybe with NHibernate there is more code needed there than with EF, I don't know)

    Sunday, March 20, 2011 12:02 PM
  • OK, I found an email of mine that has the code. What you need is a custom DomainServiceDescriptionProvider, there is a sample at http://archive.msdn.microsoft.com/RiaServices/Release/ProjectReleases.aspx?ReleaseId=2659 which shows how to create one.

    First, this is the class I am using for the generic Query command, this was written for EF:

    public class GenericQuery<TDomainService, TEntity> : DomainOperationEntry 
            where TDomainService : DomainService, IGenericDomainService
            where TEntity : EntityObject
    {
            public  GenericQuery(string operationName)
                : base(typeof(TDomainService), operationName, DomainOperation.Query, typeof(IQueryable<TEntity>), new List<DomainOperationParameter>(), new AttributeCollection())
            {
            }
            public override object Invoke(DomainService domainService, object[] parameters)
            {
                return ((IGenericDomainService)domainService).GetEntities<TEntity>();
            }
            
            
        }


    I also have an interface named IGenericDomainService which I apply to the DomainService. Then I implement the interface by adding this to the DomainService:

    IQueryable<T> IGenericDomainService.GetEntities<T>()
    {
        return this.ObjectContext.CreateObjectSet<T>();
    }


     

    OK, now for the DomainServiceDescriptionProvider. I override the GetDescription method like this:

    public override DomainServiceDescription GetDescription()
    {
        DomainServiceDescription returnDescription =  base.GetDescription();
        returnDescription.AddOperation(new GenericQuery<TestDomainService, Book>("GetBooks"));
        return returnDescription;
    }


    So, a little explanation. During the GetDescription you can add new DomainServiceOperation objects to the DomainServiceDescription by calling AddOperation. A DomainServiceOperation has an Invoke method which is the actual payload. During compilation and runtime, all of the CRUD methods you create in your DomainService are found using reflection, wrapped in DomainServiceOperations as the Invoke, and are then stored in the DomainServiceDescription. What I am showing here is how you can add your own DomainServiceOperations to the DomainServiceDescription dynamically. To take it a step further, it might be possible to read your NHibernate model and automatically loop through your objects creating methods, I will leave that up to you.

    My only caution here is that I am not sure this is actually a good idea. Part of the reason that the DomainService exists is that you are supposed to lock down all of the queries to make sure that only the data that is supposed to be queried is actually queried. Moving the queries into a generic load method seems a littler counterproductive to me.

    Sunday, March 20, 2011 4:50 PM
  • Hi ColinBlair,

    Thanks so much for taking the time to post this. It seems that I am dealing with two different ORMs here... :)

    Here is sample code for a GetAllList<T> for NHibernate

    public IList<T> GetList<T>()
    {
        IList<T> returnList = new List<T>();
    
        try
        {
            ISession session = OpenSession();
            ICriteria query = session.CreateCriteria(typeof (T));
            returnList = query.List<T>();
            CloseSession();
        }
        catch (Exception ex)
        {
            ExceptionHandler.Handle(ex);
        }
        return returnList;
    }

    I thought that it would be easier to do this with Entity framework, but I guess not... Thanks again for all of the help!

    Monday, March 21, 2011 7:52 AM
  • You could simplify that code down a little if you refactored your DomainService the same way that the LinqToEntitiesDomainService is written. I think that would include:

    1. Move your session object to be a private member of the class
    2. Perform the session = OpenSession() in the initialize method of the DomainService
    3. Move the CloseSession into the Dispose method

    I am not sure what you are doing in ExceptionHandler.Handle(ex). Your query method is already wrapped inside the DomainService's exception handling and you can check the error and re-throw a different one from the OnError method. Generally, you don't need your own try-catch in a DomainService unless you know there are recoverable errors.

    All those changes would change your query to:

    public IList<T> GetList<T>()
    {
        ICriteria query = Session.CreateCriteria(typeof (T));
        return query.List<T>();
    }

    ....but, I can't help wondering if a strongly typed version of that query wouldn't be a single line of code like EF has.

    Monday, March 21, 2011 8:08 AM
  • I won't go into the exception handling portion of the code because it doesn't apply at this point (I shouldn't have included it here), but I still think that the strongly typed code will result in hundreds of single line methods on the service class assuming we are dealing with 50 entities.

    A generics method like this could get rid of a lot of the methods and allow for code reuse. The generics methods could also throw exceptions if not accessed properly etc...

    I guess it's just two different ways of doing the same thing. I appreciate the replies!

    Monday, March 21, 2011 11:42 AM
  • @ColinBlair - I couldn't get the code the work and the link you've provided goes to generic projects page. I looked through all the ones marked with DomainService, but couldn't see this kind of code. 

    Do you remember which project it was? 

    Thursday, April 05, 2012 5:12 PM