问题
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 forGetAll()
, the implementation will have the Entity FrameworkDataServiceContext
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