Can Silverlight store data retrieved from a WCF service locally? - silverlight

Is it possible to save some result from wcf service in variable (inside my silverlight app)? For example : (why myvariable is null) ? thx for help
public partial class MainPage : UserControl
{
private int myvariable;
....
public void s_DoWorkCompleted(object o, ServiceReference1.DoWorkCompletedEventArgs e)
{
myvariable = e.Result;
}
}

Yes, this is totally possible and quite routine. Just remember that what you get back from the server is a copy and not the original variable itself. You can't pull and object back, modify it on the client, and expect the server to get the update. You have to send the object back to the server to do that.
If you are storing null into myvariable, you need to set a breakpoint in your s_DoWorkCompleted to make sure 1) it is getting reached and 2) e.Result isn't null itself. If e.Result is null, you need to go back to your service to ensure it is working.

Related

DataContract doesn't work after publish into web site

I tried to solve by myself, but... Looks like I need help from people.
I have Business Silverlight application with WCF RIA and EntityFramework. Access to Database I get via LinqToEntites.
Common loading data from database I making by this:
return DbContext.Customers
This code returns full Customers table from DataBase. But sometimes I do not need to show all data. Easy way is use linq filters in client side by next code:
public LoadInfo()
{
...
var LO1 = PublicDomainContext.Load(PublicDomainContext.GetCustomersQuery());
LO1.Completed += LO1Completed;
...
}
private void LO1Completed(object sender, EventArgs eventArgs)
{
...
DatatViewGrid.ItemsSource = null;
DatatViewGrid.ItemsSource = loadOperation.Entities.Where(c=>c ...filtering...);
//or PublicDomainContext.Customers.Where(c=>c ...filtering...)
...
}
However this way has very and very important flaw: all data passing from server to client side via DomainService may be viewed by applications like Fiddler. So I need to come up with another way.
Task: filter recieving data in server side and return this data.
Way #1: LinqToEntites has a beautiful projection method:
//MSDN Example
var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.ContactID == order.Contact.ContactID)
&& order.TotalDue < totalDue)
.Select(order => new
{
ContactID = contact.ContactID,
LastName = contact.LastName,
FirstName = contact.FirstName,
OrderID = order.SalesOrderID,
Total = order.TotalDue
}));
But, unfortunately, DomainServices cannot return undefined types, so this way won't work.
Way #2: I found next solution - make separate DTO classes (DataTransferObject). I just read some samples and made on the server side next class:
[DataContract]
public partial class CustomerDTO
{
[DataMember]
public int ISN { get; set; }
[DataMember]
public string FIO { get; set; }
[DataMember]
public string Listeners { get; set; }
}
And based this class I made a row of methods which return filtered data:
[OperationContract]
public List<CustomerDTO> Customers_Common()
{
return DbContext.Customers....Select(c => new CustomerDTO { ISN = c.ISN, FIO = c.FIO, Listeners = c.Listeners }).ToList();
}
And this works fine, all good...
But, there is strange problem: running application locally does not affect any troubles, but after publishing project on the Web Site, DomainService returns per each method HTTP 500 Error ("Not Found" exception). Of course, I cannot even LogIn into my application. DomainService is dead. If I delete last class and new methods from application and republish - all works fine, but without speacial filtering...
The Question: what I do wrong, why Service is dying with new classes, or tell me another way to solve my trouble. Please.
U P D A T E :
Hey, finally I solved this!
There is an answer: Dynamic query with WCF RIA Services
Your best shot is to find out what is causing the error. For that, override the OnError method on the DomainService like this:
protected override void OnError(DomainServiceErrorInfo errorInfo)
{
/* Log the error info to a file. Don't forget inner exceptions.
*/
base.OnError(errorInfo);
}
This is useful, because only two exceptions will be passed to the client, so if there are a lot of nested inner exceptions, you should still be able to see what actually causes the error.
In addition, you can inspect the error by attaching the debugger to the browser instance you are opening the site with. In VS2010 this is done by doing [Debug] -> [Attach to Process] in the menu-bar.

Storing the Cursor for App Engine Pagination

I'm trying to implement pagination using App Engine's RPC and GWT (it's an app engine connected project).
How can I pass both the query results and the web-safe cursor object to the GWT client from the RPC?
I've seen examples using a servlet but I want to know how to do it without a servelt.
I've considered caching the cursor on the server using memcache but I'm not sure if that's appropriate or what should be used as the key (session identifier I would assume, but I'm not sure how those are handled on App Engine).
Links to example projects would be fantastic, I've been unable to find any.
OK, so the best way to do this is to store the cursor as a string on the client.
To do this you have to create a wrapper class that is transportable so you can pass back it to the client via RequestFactory that can hold the results list and the cursor string. To do that you create a normal POJO and then a proxy for it.
here's what the code looks like for the POJO:
public class OrganizationResultsWrapper {
public List<Organization> list;
public String webSafeCursorString;
public List<Organization> getList() {
return list;
}
public void setList(List<Organization> list) {
this.list = list;
}
public String getWebSafeCursorString() {
return this.webSafeCursorString;
}
public void setWebSafeCursorString(String webSafeCursorString) {
this.webSafeCursorString = webSafeCursorString;
}
}
for the proxy:
#ProxyFor(OrganizationResultsWrapper.class)
public interface OrganizationResultsWrapperProxy extends ValueProxy{
List<OrganizationProxy> getList();
void setList(List<OrganizationProxy> list);
String getWebSafeCursorString();
void setWebSafeCursorString(String webSafeCursorString);
}
set up your service and requestFactory to use the POJO and proxy respectively
// service class method
#ServiceMethod
public OrganizationResultsWrapper getOrganizations(String webSafeCursorString) {
return dao.getOrganizations(webSafeCursorString);
}
// request factory method
Request<OrganizationResultsWrapperProxy> getOrganizations(String webSafeCursorString);
Then make sure and run the RPC wizard so that your validation process runs otherwise you'll get a request context error on the server.
Here's the implementation in my data access class:
public OrganizationResultsWrapper getOrganizations(String webSafeCursorString) {
List<Organization> list = new ArrayList<Organization>();
OrganizationResultsWrapper resultsWrapper = new OrganizationResultsWrapper();
Query<Organization> query = ofy().load().type(Organization.class).limit(50);
if (webSafeCursorString != null) {
query = query.startAt(Cursor.fromWebSafeString(webSafeCursorString));
}
QueryResultIterator<Organization> iterator = query.iterator();
while (iterator.hasNext()) {
list.add(iterator.next());
}
resultsWrapper.setList(list);
resultsWrapper.setWebSafeCursorString(iterator.getCursor().toWebSafeString());
return resultsWrapper;
}
a second option would be to save the webSafeCursorString in the memcache, as you already mentioned.
my idea looks like this:
the client sends always request like this "getMyObjects(Object... myParams, int maxResults, String clientPaginationString)". the clientPaginationString is uniquely created like shown below
server receives request and looks into the memcache if there is a webSafeCursorString for the key clientPaginationString
if the server finds nothing, he creates the query and save the webSafeCursorString into memcache with the clientPaginationString as the key. -> returns the results
if the server finds the webSafeCursorString he restarts the query with it and returns the results
the problems are how to clean the memcache and how to find a unique clientPaginationString:
a unique clientPaginationString should be the current UserId + the params of the current query + timestemp. this should work just fine!
i really can't think of a easy way how to clean the memcache, however i think we do not have to clean it at all.
we could store all the webSafeCursorStrings and timestemps+params+userid in a WebSafeCursor-Class that contains a map and store all this in the memcache... and clean this Class ones in a while (timestamp older then...).
one improvement i can think of is to save the webSafeCursorString in the memcache with a key that is created on the server (userSessionId + servicename + servicemethodname + params). however, important is that the client sends an information if he is interested in a new query (memcache is overriden) or wants the next pagination results (gets webSafeCursorString from memcache). a reload of the page should work. a second tap in the browser would be a problem i think...
what would you say?

Why Don't DomainService Constructor Overloads Show Up as DomainContext Constructor Overloads?

I wrote an overload for my DomainService class. Problem is, when I recompile, it's not showing up as an overload for my DomainContext. What's wrong? Here is a code sample:
[EnableClientAccess]
public class FoodDomainService : LinqToEntitiesDomainService<FoodEntities>
{
public FoodDomainService(CultureInfo cultureInfo)
{
Thread.CurrentThread.CurrentCulture = cultureInfo;
}
}
And this doesn't work:
FoodDomainContext _foodContext = new FoodDomainContext(Thread.CurrentThread.CurrentCulture);
I get an error that there is no overload matching that. Am I not allowed to do this? Do I need an attribute of some kind?
You are not allowed to do this. When newing up the context from your Silverlight client, you are not directly intantiating your service. Instead, you instantiate a proxy class that was generated by RIA Services, and that proxy class will then call your service. This is why you don't see your constructor: because RIA did not generate it in your proxy.
Doing what you're trying to do would also implicate that there is a round-trip to the server at the time of newing up that FoodDomainContext class, which is not going to happen, because you need to complete the initialisation of that object before you can do so.
Anyway, instead of that you can create a method called SetCurrentCulture() and then call it after initializing the proxy.
This will not work because DomainContext is generated on client code of silverlight, click on view all folders or jump to definition and you will see that code generated will not contain your extra constructor.
Instead you will have to create a method in your domain service and pass information to server.
public SetCultreInfo(int lang,...)
{
.. set culture info
}
On your client, inside constructor you should call,
public MyDomainContext()
{
this.SetCulture(....);
}

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

DeSerialized attribute in Silverlight

I have reasonably limited data to move between silverlight and a WCF service. So I have set up some datacontracts. I was hoping to be able to use the same classes to use for some limited data validation on the client side, without needing the overhead of DTOs and stuff.
I have a contract something like this:
[DataContract]
class MyObject
{
private String _id;
[DataMember]
public String ID
{
get
{
return _id;
}
set
{
_id = value;
DoStuff();
}
}
Now when I deserialize the object the setter will be run. This will set the private variable _id which is good. But I don't want the DoStuff() to be run on deserialization.
I had hoped to use the [OnDeserializing] and [OnDeserialized] attributes. I could have defined a bool flag that I could use to control what gets run in the setter. This actually works on the server side but Silverlight doesn't have those attributes.
Any suggestions?
I would recommend not overloading the usage of these classes to perform logic. This is your service data contract. You shouldn't touch the Silverlight proxy classes that get generated. I recommend that you put your validation logic into other classes that you invoke after deserialization is complete. Otherwise, you might start getting errors in your serialization code when your errors are actually in your data.

Resources