IAsyncRepository or IObservableRepository for silverlight 4 + WCF Data Services

末鹿安然 提交于 2019-12-20 14:44:38

问题


Update 2 : @Enigmativity has a brilliant answer. I've implemented this into a IObservableRepository<T>. Details in my answer below.


Question: So I've changed most of the question (See edit history) I would just like it if someone commented/validated/puked on my design. =)

So typically my Repos look like this:

public interface IRepository<T> where T : class
{
    T GetById(int id);
    IQueryable<T> GetAll();
    void InsertOnSubmit(T entity);
    void DeleteOnSubmit(T entity);
    int SubmitChanges();
}

But when it comes to Silverlight and WCF Data Services, it gets seriously annoying query related data with all the asynchrony. I have to load the parent entity async first and then query its child entities async.

So I came up with an IAsyncRepository, I'd like to know if the design is ok, whether it could be improved, (and whether it makes any sense using Rx here?)

To Solve the child entities problem I plan to load all required child entities before calling the callback.

My Repo looks like:

public interface IAsyncRepository<T> where T : class
{
    void GetById(int id, Action<T> callback);
    void GetAllFromQuery(Func<MyEntities, IQueryable<Product>> funcquery,
                                             Action<IList<Calculator>> callback)
}

You could use the repo like this:

productRepo.GetAllFromQuery(
    x => x.Products.Where(p => p.ID > 5),
    y => Assert.IsTrue(y.Count > 0)); //y is a IList<Product>

What do you guys think?

Regards, Gideon


回答1:


Just a quick, off the cuff answer.

How about using the Reactive Extensions for .NET (Rx)?

You could then define your repository as:

public interface IObservableRepository<T> where T : class
{
    IObservable<T> GetById(int id);
    IObservable<T> GetAll(Func<IQueryable<T>, IQueryable<T>> query);
    IObservable<Unit> InsertOnSubmit(T entity);
    IObservable<Unit> DeleteOnSubmit(T entity);
    IObservable<int> SubmitChanges();
}

All of the returned observables would contain single values, except for GetAll which would have zero or more.

The Unit type is void in the Rx world. It's just a way of not needing to define a non-generic IObservable interface.

You would then query like so:

IObservableRepository<Foo> repo = ...;

var foos = repo.GetAll(ts => ts.Where(t => t.Bar == "Hello"));

foos.Subscribe(foo =>
{
    // Do something asynchronously with each `Foo`.
});

And submit could be done like this:

var submit =
    foos
        .Select(foo => repo.InsertOnSubmit(foo)).ToArray()
        .Select(s => repo.SubmitChanges());

submit.Subscribe(result =>
{
    // handle the asynchronous result of submit.
});

This is all based on trying to keep the repository methods as close as possible to the original, but it may be worth refactoring on the Silverlight side to something like this:

public interface IObservableRepository<T> where T : class
{
    IObservable<T> GetById(int id);
    IObservable<T[]> GetAll(Func<IQueryable<T>, IQueryable<T>> query);
    IObservable<int> Submit(T[] insertsOrUpdates);
    IObservable<int> Submit(T[] insertsOrUpdates, T[] deletes);
}

Submit would be a bit nicer now:

repo.Submit(foos).Subscribe(result =>
{
    // Handle asynchronous result of submit;
});

Like I said, off the cuff. :-)




回答2:


Too long to update the question, so I posted as an answer.

So I implemented it like this:

public interface IObservableRepository<T, TContext>
{
    IObservable<T> GetById(int id);
    IObservable<IList<T>> GetAll(Func<TContext, IQueryable<T>> funcquery);
    IObservable<int[]> SubmitInserts(IList<T> inserts);
    IObservable<int[]> SubmitDeletes(IList<T> deletes);
    IObservable<int[]> SubmitUpdates(IList<T> updates);
    //helpers
    IObservable<int> SubmitInsert(T entity);
    IObservable<int> SubmitDelete(T entity);
    IObservable<int> SubmitUpdate(T entity);
}

Some notes :

  • TContext is needed for GetAll(), the implementation will have the Entity Framework DataServiceContext which will allow you to do the following:

    var foos = repo.GetAll(ts => ts.Where(t => t.Bar == "Hello"));
    //ts is a DataContext passed here by GetAll();
    
  • The methods marked //helpers just call the other methods that take arrays.
  • The actual return type for CRUD functions for WCF Data Services+Entity Framework is a DataServiceResponse. What I do is loop through them and return the Http Status Codes. So the ints returned for the CRUD methods are Http Status Codes.
  • When loading child entities I just eager loaded them by doing this:

    context.Products.Expand("Child").Expand("Child2");
    

I can basically use it like this:

productRepo.GetById(3).Subscribe(x => /* Do something with product x*/ );
productRepo.SubmitUpdate(product)
         .Subscribe(r => /*return code should be 204 (http) 201 for insert */);
//same for insert and delete

Do tell me if I should put up the actual implementation here.

Any comments on this would be well taken =)

Thanks



来源:https://stackoverflow.com/questions/5349140/iasyncrepository-or-iobservablerepository-for-silverlight-4-wcf-data-services

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!