How to make Entity methods in RIA DomainContext work asynchronously - silverlight

Intro:
I have a RIA service on Silverlight application that generates the code from the .Web app.
On a server side, I am using EF4 and a DomainService based on a EF4 model.
First example:
If I extend the DomainService with my own methods implementing IEnumerable or IQueryable the RIA generates the appropriate methods on its DomainContext class. Something like this:
public partial class SymbolicDataService
{
public IQueryable<Chemical> GetWeightedChemicals(int min, int max)
{
// ... some EF query here
}
}
RIA generates the method, so I can do something like this on Silverlight side:
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
SymbolicDataContext db = new SymbolicDataContext();
var chemicals = db.Load(db.GetWeightedChemicalsQuery(10,24), onChemicalsLoaded, false);
}
and then I respond to the loading in a onChemicalsLoaded callback function.
Second example:
If I want a method that does not return IEnumerable or IQueryable, but is a void method, I mark the DomainService's method with [Invoke] attribute:
[Invoke]
public void FlushChemical(Chemical chemical)
{
// some code that does what it does (with EF)
}
Now I can do something like:
private void btnLoad_Click(object sender, RoutedEventArgs e)
{
SymbolicDataContext db = new SymbolicDataContext();
var chemical = db.GetWeightedChemicals(10,24).FirstOrDefault();
db.FlushChemical(chemical);
}
Third Example:
If I do:
public void ShakeChemical(Chemical chem, int timeShaking)
{
// Shake the chemical until it drops
}
RIA will create an Entity method on client side that enables me to do this:
private void btnShake_Click(object sender, RoutedEventArgs e)
{
Chemical chem = (ListBox)sender.SelectedItem as Chemical;
chem.ShakeChemical(22);
db.SaveChanges();
}
Question:
My question here is how to make the last two examples work asynchronously like the LoadOperation? In the first example, I can use callback on Load method to respond to the operation completion, but I have no idea how to make the other two functions asynchronous and I don't want my UI to block during the calls.
EDIT:
I see now that the second example's method has an overload with Action argument so I do have a callback for the second example. However, the question remains for the third example.

I'm assuming that the third scenario is updating a Chemical object in some way?? If this is the case then just look into "Named Update" methods for RIA Services. Hope this helps

Related

How can I initialize a Prism module without View and ViewModel for working with EventAggregator?

I am writing an application using Prism that contains three modules. First one has a view to configure a "Person", second one is a service that generates that "Person" and third one is the visualization of all people. These three modules communicate with EventAggregator system. But I have problems with the messages on the service one.
In this service module I only have the service implementation and the module definition.
This service is a people manager that receives a message from EventAggregator, creates a "Person" with a task and send a message to the third module with this "Person".
Service:
private List<Person> people = new();
public PeopleControllerService(IEventAggregator eventAggregator, ICommonParametersService commonParameters)
{
this._eventAggregator = eventAggregator;
eventAggregator.GetEvent<GeneratePersonEvent>().Subscribe(GeneratePerson);
this._commonParameters = commonParameters;
}
private void GeneratePerson()
{
Person newPerson = new(this._commonParameters.DefaultPersonTask);
this.People.Add(newPerson);
this._eventAggregator.GetEvent<AssignedPersonEvent>().Publish(newPerson);
}
Module definition:
private PeopleControllerService moduleController;
public void OnInitialized(IContainerProvider containerProvider)
{
IEventAggregator eventAggregator = containerProvider.Resolve<IEventAggregator>();
ICommonParametersService commonParametersService = containerProvider.Resolve<ICommonParametersService>();
this.moduleController = new(eventAggregator, commonParametersService);
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
The problem is that when I send the "GeneratePersonEvent" message it never reaches the PeopleControllerService and the "GeneratePerson" method is never executed.
I've tried using a view and a viewModel, programming the service in the viewModel and assigning the view to a dummy and hidden region in the app and I've verified that it works that way.
Modified module definition:
public void OnInitialized(IContainerProvider containerProvider)
{
IRegionManager regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.DummyRegion, "PeopleController");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<PeopleController>();
}
How can I use the EventAggregator without using a dummy view? Do I have to add something in the "RegisterTypes" method? I've tried with:
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<PeopleControllerService>();
}
but it doesn't work either.
I've checked this post: Can I get EventAggregator Subscribe Message without view, viewmodel in prism?, and there it says that it is possible, but doesn't describe how to implement.
Most of the time you want exactly one instance of a service, and you have to tell the container:
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<PeopleControllerService>();
}
Also, you want your service to implement an interface so that you can pass different implementations to the consumers of your service, the most obvious case is your tests.
You need to actually create the instance of your service, too. Normally, you inject it into some consumer, but if it's completely decoupled and only talks through the event aggregator, you have to create the instance manually:
// in App.xaml.cs
protected override void OnInitialized()
{
Container.Resolve<PeopleControllerService>();
base.OnInitialized();
}
Hint: if the service implements an interface, the application doesn't need to personally know the controller module.

How to share data resources between widgets in GWT

I am using GWT and AppEngine for a project. I would like to know how can I share data (ArrayList objects)between widgets, so I could centralize the logic and reduce the number of RPC calls to the server.
I have thought of two ways, but I don't know which is better:
1) When I instantiate the widget, I pass the ArrayList object as a parameter, although I don't know how to do that because the widget gets instantiated with :
ThisAppShell shell = GWT.create(ThisAppShell.class);
2) By using a mechanism like eventBus
http://www.dev-articles.com/article/Gwt-EventBus-(HandlerManager)-the-easy-way-396001
When the user loads the application,after the login process is complete, I would like to download a list of employees which should be available for all widgets. This should all be done in the onModuleLoad() method. I would like to download them all at startup because I would like to implement some sort of caching mechanism. For example, I want to have 2 ArrayList instances:
- emplListOnStart which is populated when the application is loading
- emplListChanges, an array on which the user will make modifications from inside widgets.
After the user has finished making the changes (he presses the "Save" button), the two arrays will be compared, the differences will be saved in appengine (via RPC) and also updated in emplListOnStart.
This is the code for the EntryPoint class:
public class ThisApp implements EntryPoint {
ThisAppShell shell = GWT.create(ThisAppShell.class);
LoginServiceAsync loginService = GWT.create(LoginService.class);
private ArrayList<Employee> emplListOnStart;
private ArrayList<Employee> emplListChanges;
public void onModuleLoad() {
RootLayoutPanel.get().clear();
RootLayoutPanel.get().add(shell);
loginService.isAuthenticated(new AsyncCallback<UserDto>() {
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
public void onSuccess(UserDto result) {
//Here I should load the emplListOnStart list;
}
});
shell.getLogoutLink().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
loginService.logout(new AsyncCallback() {
public void onFailure(Throwable caught) {
}
public void onSuccess(Object result) {
//Here the user will get logged out
}
});
Window.Location.assign("");
}
});
}
}
And here is the code for the widget:
public class ThisAppShell extends Composite {
private static ThisAppShellUiBinder uiBinder = GWT
.create(ThisAppShellUiBinder.class);
interface ThisAppShellUiBinder extends UiBinder<Widget, ThisAppShell> {
}
#UiField
Anchor logout_link;
#UiField
StackLayoutPanel stackLPanel;
#UiField
TabLayoutPanel tabLPanel;
public ThisAppShell() {
initWidget(uiBinder.createAndBindUi(this));
initializeWidget();
}
public void initializeWidget() {
stackLPanel.add(new HTML("Manage empl."), new HTML("Employees"), 30);
stackLPanel.add(new HTML("Manage Dept."), new HTML("Departments"), 30);
// Add a home tab
HTML homeText = new HTML("This is the home tab");
tabLPanel.add(homeText, "Home");
// Add a tab
HTML moreInfo = new HTML("This is the more info tab");
tabLPanel.add(moreInfo, "More info");
// Return the content
tabLPanel.selectTab(0);
}
public Anchor getLogoutLink() {
return logout_link;
}
}
Is this possible, or how could this be done better?
Thank you.
I think there are two ways to do it:
Create a setter on your widget to set your ArrayList instances (setData()). You can then call this function in the onSuccess method of your loginService.
Inject the singleton instance of a global EventBus into your widget (using i.e. gin/guice) and fire an event containing your data. In the widget you have to attach an EventHandler for the specific event (i.e. LoadEmplListEvent).
I think both solutions are fine to use.
Solution one creates a tighter coupling to your widget but is easier to implement and I think you should take this route if you only have a small number of widgets where you work
with the data.
Solution is a cleaner approach because it de-couples your widgets from the rest. You fire the event the data in your onSuccess method once and you don't care about the widgets.
The widgets that are interested in the data will make sure that they handle the event appropriately (by handling the event). I guess if you have a lot of widgets that have to deal with the data the second approach is the one to go for.

Problem with asynchronous webservice response

In my WP7 application i'm calling and consuming a webservice with these methods:
In my page .cs file:
public void Page_Loaded(object sender, RoutedEventArgs e)
{
if (NavigationContext.QueryString["val"] == "One")
{
listAgences=JSON.callWSAgence("http://...");
InitializeComponent();
DataContext = this;
}
}
In my json class i have these methods :
public List<Agence> callWSAgence(string url)
{
WebClient webClient = new WebClient();
Uri uri = new Uri(url);
webClient.OpenReadAsync(uri);
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedTestAgence);
return listAgences;
}
public void OpenReadCompletedTestAgence(object sender, OpenReadCompletedEventArgs e)
{
StreamReader reader = new System.IO.StreamReader(e.Result);
jsonResultString = reader.ReadToEnd().ToString();
addAgencesToList();
reader.Close();
}
public void addAgencesToList()
{
jsonResultString = json.Substring(5, json.Length - 6);
listAgences = JsonConvert.DeserializeObject<List<Agence>>(json);
}
The problem is that the OpenReadCompletedTest method in the json class is not called right after
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompletedTestAgence);
So the listAgences returned is empty.
But later OpenReadCompletedTest is called and everything works fined, but my view has already been loaded.
What can i do to have a kind of synchronous call or to reload my view after the webservice response being parsed and my list being filled.
The behaviour (problem) you are seeing is because the web request is made asynchronously.
If you want to have a separate object call the web server this will need to handle a callback to process the response or make appropriate changes itself.
Also:
- the code in the question doesn't show what the variable json is defined as. In Page_Loaded it looks like a custom class but in OpenReadCompletedTestAgence and addAgencesToList it looks like a string.
- the code in Page_Loaded sets the value of listAgences twice.
check out the following question for more information about making asychronous calls synchrously Faking synchronous calls in Silverlight WP7

Silverlight Webservice Problem

I have a webservice that calls a method and returns a generic list. The webservice completed method looks like this (note: names and e.Result are both of the same type of List):
void SetNames()
{
ServiceReference1.ServiceClient webservice = new ServiceReference1.ServiceClient();
webservice.GetNameCompleted += new EventHandler<GetNameCompletedEventArgs>(webservice_GetNameCompleted);
webservice.GetNameAsync();
}
private void webservice_GetNameCompleted(object sender, ServiceReference1.GetNameCompletedEventArgs e)
{
names = e.Result;
}
The problem I'm having is that I can only retrieve the items in the names list in the webservice method. Whenever I try to access the items in the names list anywhere outside of that method it is empty. For example (this displays nothing in the textbox),
List<string> names = new List<string>();
public MainPage()
{
InitializeComponent();
SetNames();
foreach (string name in names)
textBox1.Text += name;
}
But this will display the correct thing:
private void webservice_GetNameCompleted(object sender, ServiceReference1.GetNameCompletedEventArgs e)
{
names = e.Result;
foreach (string name in names)
textBox1.Text += name;
}
I'm new to Silverlight and webservies, and I'm probably over looking something. I've been working on this for a while and I'm at the point where I feel I need to ask for help. Any help would be greatly appreciated!
In Silverlight all calls to web-services are asynchronous (unlike WPF which can also use synchronous call).
It means that the code after the call to the web-service will be invoked before the service has sent a response to the Silverlight client.
So, in the MainPage constructor, the foreach loop is iterating over the collection BEFORE the service has returned, and then iterate over an empty collection.
The right way to proceed is the second one : initializing the collection after the service has responded, in the callback method dedicated to this task : webservice_GetNameCompleted.
You have to wait for the Web Servicec call back to complete.
By defualt all Silverlight WCF web service calls are asynchronous.
you are sending a request to the webservice and unlike .asmx with WCF and Silverlight the application continues to run instead of waiting for the webservice to return a result.
So when you make a call like:
public MainPage()
{
InitializeComponent();
SetNames();
foreach (string name in names)
textBox1.Text += name;
}
The application does not stop and wait for SetNames to Return a value it just carries on and since the webservice hasn't returned a result yet you have a blank or null list still when you call your foreach.
Cheers

Passing Parameters to Silverlight

Any ideas on how to pass parameters to Silverlight on startup from URL QueryString?
Thank You
One approach you can take is to expose a method that can be accessed from JavaScript. So in your xaml.cs file you need to add the following to your constructor:
this.Loaded += new RoutedEventHandler(Page_Loaded);
Then add the following event handler:
void Page_Loaded(object sender, RoutedEventArgs e)
{
HtmlPage.RegisterScriptableObject("YourControlName", this);
}
and:
[ScriptableMember]
public void YourMethod(string yourData)
{
// Do your stuff here
}
Then in the ascx or aspx page where your Silverlight control is instantiated add the following JavaScript:
var silverlightControl;
function onSilverlightLoad(sender, args) {
silverlightControl = sender.getHost();
var yourData = "some data";
silverlightControl.Content.YourControlName.YourMethod(yourData);
}
It does also mean that your Silverlight control has to be instantiated via the <object... tag rather than via <asp:Silverlight...
Although Chris's method will work, it's easier to pass startup information through Silverlight's initialization parameters feature.
If all you need to do is get at key-value pairs of the query string, there is a much simpler way using the HtmlPage class:
HtmlPage.Document.QueryString["your_key"];

Resources