Generic Singleton and share data between pages - windows-phone-8

To share data (complexe data ) between pages in my windows phone 8 application I want to implement a singleton, but I want it to be generic, is it possible? I suppose that it creates a new instance for each type isn't it?
public sealed class NavigationContextService<T>
{
private static readonly NavigationContextService<T> instance = new NavigationContextService<T>();
private NavigationContextService()
{
}
public static NavigationContextService<T> Instance
{
get
{
return instance;
}
}
public List<T> ShareList { get; set; }
public T ShareData { get; set; }
}

It is creating a new instance for every type, because it is generic - you want it to be like this (if you start with generics, take a look at some tutorials, blogs or MSDN - you will easily find many in the internet).
It is still a singleton. When you use
NavigationContextService<string>.Instance.ShareList.Add("Text");
then you have one Instance for type string. Generics helps a lot when you want to create same methods/classes that differ in type.
On the other hand if you want to create Singleton that will hold different types then you can for example modify your class to be non Generic like this:
public sealed class NavigationContextServiceNonGeneric
{
private static readonly NavigationContextServiceNonGeneric instance = new NavigationContextServiceNonGeneric();
private NavigationContextServiceNonGeneric() { ShareList = new List<object>(); }
public static NavigationContextServiceNonGeneric Instance
{ get { return instance; } }
public List<object> ShareList { get; set; }
public object ShareData { get; set; }
}
As you can see in the code above I haven't defined the 'exact' type of shared data - it is object type. Then you can easily hold most of data with it:
NavigationContextServiceNonGeneric.Instance.ShareList.Add("Text");
NavigationContextServiceNonGeneric.Instance.ShareList.Add(3);
NavigationContextServiceNonGeneric.Instance.ShareList.Add(3.0f);
It is singleton, which can hold different types of shared data. BUT it has also disavantages - the main is that you have to remember what type of data you hold and in what order. In my opinion Generic version is better because of that fact.
Everything depends on the purpose of your code. There may be easier and better ways that those two approaches.
As for the Page Navigation, you can for example try to use a method from this article - you extend Navigation service to pass the object:
public static class Extensions
{
private static object Data;
public static void Navigate(this NavigationService navigationService, Uri source, object data)
{
Data = data;
navigationService.Navigate(source);
}
public static object GetNavigationData(this NavigationService service) { return Data; }
}
Then you use it:
NavigationService.Navigate(yourUri, DataToPass);
After Navigation you can get your data:
string myTextData = NavigationService.GetNavigationData() as string;
This method has to disadvantages: it is not type-safe and your data won't be preserved in Tombstone mode.
As for the second disadvantage you can easily use PhoneApplicationService.State Property for the purpose of Page Navigation - it is a dictionary (which is preserved while tombstoning):
PhoneApplicationService.Current.State.Add("data", yourData);
Then when you want to get your data:
yourDataType yourData = PhoneApplicationService.Current.State["data"] as yourDataType;
There are also more ways in which you can pass the data.

Related

Dealing with external namespaces for a POCO data model implemented through NJsonSchema auto-generated classes

In a dotnet microservice architecture, to avoid exposing my data model and its logic, I am willing to add a POCO data model layer following a mechanism: fullDataModel>json>pocoDataModel.
Here is my reduced fullDataModel
public class MyDto
{
public string Name { get; set; }
public AnExternalNamespace.MyExternalType ExternalObject { get; set; }
}
To implement the above mechanism, I am using NJSonSchema CSharpGenerator as follow:
// generate Json schema from ConsoleApp1
JsonSchema schema = await JsonSchema.FromFileAsync("MyDto.txt");
CSharpGeneratorSettings settings = new CSharpGeneratorSettings
{
ClassStyle = CSharpClassStyle.Poco,
Namespace = "MS1Namespace",
GenerateDataAnnotations = true
};
var generator = new CSharpGenerator(schema, settings);
var modelFile = generator.GenerateFile();
// generate the C# file in a another ConsoleApp2 for testing purpose
using (StreamWriter writer = new StreamWriter("..\\ConsoleApp2\\MyDtoFromJson.cs"))
{
writer.WriteLine(modelFile);
}
Problem, the MyDto class refers to a type which comes from an external library (AnExternalNamespace.MyExternalType). But the mechanism I implemented does not preserve this type of reference, rather it generates a type bounded to each namespace associated with each microservice I use that implement this type. Have a look at the C# generated file which is bounded to MS1Namespace
namespace MS1Namespace
{
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.8.0.0 (Newtonsoft.Json v13.0.0.0)")]
public partial class MyExternalType
{
[Newtonsoft.Json.JsonProperty("Name", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Name { get; set; }
[Newtonsoft.Json.JsonProperty("Description", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Description { get; set; }
}
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.8.0.0 (Newtonsoft.Json v13.0.0.0)")]
public partial class MyDto
{
[Newtonsoft.Json.JsonProperty("Name", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string Name { get; set; }
[Newtonsoft.Json.JsonProperty("ExternalId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public MyExternalType ExternalObject { get; set; }
}
}
In other words, with this mechanism, MyExternalType is kind of redefined relative to each MSnNamespace for every microservice (1 to n) where it is embedded. On the contrary, I would like to be able to receive objects that refer to the type defined in the AnExternalNamespace each time I send a request to these microservices. That would allow me treat the data of same type coming from these different microservices in a unified way with the unique AnExternalNamespace.MyExternalType
I have tried to play with Json annotations in MyDto class. For example by doing this:
[JsonProperty(TypeNameHandling = TypeNameHandling.Auto)]
public MyPublicLibrary.MyExternalType ExternalId { get; set; }
This does not allow to get out of the MS1Namespace scope to which all the auto-generated classes are bounded.
So, the only solution I see to overcome this for now is to implement an explicit&implicit cast in each microservice embedding this type from MSnNamespace.MyExternalType to AnExternalNamespace.MyExternalType.
But I was thinking NJSonSchema settings could offer me a more elegant way to deal with this situation. Am I missing something here? Do you see any other options or better practice while continuing keeping passing through the Json serialization/deserialization process?
Thanks in advance for your help!

Jackson handling of remaining fields

I wonder if this is possible.
I have a huge json file to parse and modify.
But not all fields are interresting to me : I wan't to map only fields I'm interrested in, modify them then save the whole json with all fields I didn't care left unchanged.
Then suppose I have lot of properties and only one I'm interrested in named "MyProp", I tried something like
public class MyMapper extends HashMap {
private String myProp;
public void setMyProp(String myProp) {
this.myProp = myProp;
}
public String getMyProp() {
return myProp;
}
}
But the property "MyProp" is always null (until I remove "extends HashMap").
Instead all goes in the map.
Wouldn't be useful (or is it already possible ?) to be able to mix concrete fields with maps to work with known fields without loosing unknowns ?
And it would be better to did not extend HashMap but instead having a field to hold all unknown fields like this :
public class MyMapper {
private String myProp;
private Map remaining = new HashMap();
public void setMyProp(String myProp) {
this.myProp = myProp;
}
public String getMyProp() {
return myProp;
}
#JsonRemaining
public Map getRemaining() {
return remaining;
}
}
How would you do in this case ?
Seems like #JsonAnySetter and #JsonAnyGetter annotations might help. All irrelecant proerpties get stuffed into one Map and then the map contents get dumped back to json
See how-to guide here
http://vincentdevillers.blogspot.co.il/2013/08/how-to-map-unknown-json-properties-with.html

Returning values to 'parent' viewmodel in MvvmCross

What is the recommended method of passing a parameter from one viewmodel to another, modifying it then returning it to the original viewmodel?
Much has been written about passing values in to views ie ShowViewModel(new{ paramX=valueY} ), however I am unable to find any working examples of having the displayed 'submodel' return a value back to the parent when it is closed/dismissed in some method.
The only sample I've found which seems to cover this is http://www.gregshackles.com/2012/11/returning-results-from-view-models-in-mvvmcross/ however the code doesn't seem to work on the new current v3 mvx, failing at runtime with error resolving the viewmodel type, presumably because reflection in mvx wasn't able to identify/register the type due to subtyping or generics.
After discussing with the author of the link from my question, the code does work with one minor tweak and a correction to the name of my View class to conform to the mvvmcross convention.
My view was incorrectly named SomethingViewController rather than SomethingView.
The alteration to Greg's code to work on the current MVX v3 codebase is to change his sample from:
public abstract class SubViewModelBase<TResult> : ViewModelBase
{
protected string MessageId { get; private set; }
protected SubViewModelBase(string messageId)
{
MessageId = messageId;
}
....
}
to:
public abstract class SubViewModelBase<TResult> : ViewModelBase
{
protected string MessageId { get; private set; }
public virtual void Init(string messageId){
this.MessageId = messageId;
}
}
and of course in your submodels use
public abstract class MySomeModel : SubViewModelBase<YourReturnType>
{
public override void Init(string messageId, other.. parameters..){
base.Init(messageId);
.. your other parameters init here..
}
}

Custom types in Navigation parameters in v3

In v3 if I wanted to pass two objects to another viewmodel:
public class Dog
{
}
public class Cat
{
}
var dog = new Dog();
var cat = new Cat();
ShowViewModel<SomeViewModel>(new {Dog = dog, Cat = cat });
public class SomeViewModel
{
Init(Dog dog, Cat cat)
{
}
}
As far as I can tell that won't work because the types aren't recognized and can't be stuck in a dictionary. If I wanted to have these serialized as json, passed to the view model, and deserialized as Init parameters, would I implement IExtraParser? And if that is correct, how do I go about adding the implementations to the ExtraParsers dictionary?
update:
This seems to do it:
var foo = Mvx.Resolve<IMvxFillableStringToTypeParser>();
foo.ExtraParsers.Add(new MyParser());
The default navigation mechanism in MvvmCross is deliberately lightweight.
It is really there to allow you to pass just one simple, serializable object - e.g.
public class DogNav
{
public int Id {get;set;}
public string Caption {get;set;}
}
// received in:
public class DogViewModel : MvxViewModel
{
public void Init(DogNav dogNav)
{
}
}
With this setup, if a navigation is triggered like:
// navigation
ShowViewModel<DogViewModel>(new DogNav() { Id=12, Caption="Good boy" });
then the underlying system will transport the values from that DogNav object - possibly using Uris, Intents or other serialization techniques - to the new DogViewModel and will then ensure Init is called with the correct values.
Because of the serialization, it's important:
not to pass big objects (Uris on WindowsPhone can break above a few hundred characters)
not to expect the same object instance to arrive - i.e. if you are using database-backed or stateful objects, then it's best to pass some kind of lookup key rather than the objects themselves.
not to expect that only one ViewModel will receive the message - on some operating systems, it may be that the user goes back and forth many, many times between apps causing many Views and ViewModels to be created.
not to expect that a ViewModel that receives the message is in the same process and memory space as the ViewModel that sent the request - the same may actually be received days later after a tombstoning event.
If you do want to pass multiple objects via navigation, then I think you can do this using code like:
public class CatNav
{
public int CatId {get;set;}
public string CatCaption {get;set;}
}
public class DogNav
{
public int DogId {get;set;}
public string DogCaption {get;set;}
}
// received in:
public class CatAndDogViewModel : MvxViewModel
{
public void Init(DogNav dogNav)
{
}
public void Init(CatNav catNav)
{
}
}
In this case you could navigate using:
var catNav = new CatNav() { CatId =12, CatCaption="Meow" };
var dogNav = new DogNav() { DogId =12, DogCaption="Woof" };
var bundle = new MvxBundle();
bundle.Write(catNav);
bundle.Write(dogNav);
ShowViewModel<CatAndDogViewModel>(bundle);
I think this would work...
However... please be aware that the serialization is very simple - so if CatNav and DogNav were to share a property name, then this would lead to problems - you'd end up with some Cags and Dots
Because of the Cag and Dot problem I don't recommend this approach...
If you do need more complex transitions in your apps, then one route is to:
UPDATE - see Passing complex navigation parameters with MvvmCross ShowViewModel
1. Add the Json Plugin (or any Json serializer) and change your Setup.cs code to create a MvxJsonNavigationSerializer - overriding CreateNavigationSerializer
protected override IMvxNavigationSerializer CreateNavigationSerializer()
{
return new MvxJsonNavigationSerializer();
}
Use a composite object in navigation like:
public class DogAndCatNav
{
public DogNav DogNav {get;set;}
public CatNav CatNav {get;set;}
}
This would be received by:
public void Init(DogAndCatNav dogAndCatNav)
{
}
But note that this technique does need a more powerful serialization engine - such as Json.
Overall... even after writing all this... I'd recommend you pass as little data as possible in your navigations!

MVC3 / JSON: How do I use model binding when my property names are renamed via a DataContract?

I use a DataContractJsonSerializer to create a JsonResult for my model data when sending data to the client. My model represents data to be displayed in a data table, and I wished to change the name of the model's properties in the JSON only so that less verbose property names are sent over the wire for each data table row. Now, I'm attempting to send the data table cell values via JSON to the server's controller action method. The names of the fields being sent back are still the short names, and the model binding doesn't seem to like that. What can I do to get model binding working and preserve the ability to sent alternate property names via JSON?
Model:
[DataContract()]
public class UsageListModel {
[DataMember(Name = "results")]
public IEnumerable<UsageModel> Usages { get; set; }
}
[DataContract()]
public class UsageModel {
[DataMember(Name = "job")]
public string JobId { get; set; }
[DataMember(Name = "dt")]
public DateTime UsageDate { get; set; }
[DataMember(Name = "qty")]
public int Quantity { get; set; }
[DataMember(Name = "uom")]
public string UnitOfMeasure { get; set; }
[DataMember(Name = "nts")]
public string Notes { get; set; }
}
It's not as elegant but I usually do this by just making an intermediary class (I refer to it as a ViewModel) that has those shortname properties and can be translated back and forth between it and the actual Model. Although it seems like busy work, the ViewModel can be useful beyond this stint - for example you can use it to easily cache client-side info if the need arises, or serialize/deserialize exactly what's going to/from the client in tests.
I'm still in disbelief that MVC doesn't offer some easier method to bind using custom attributes (or even the .NET data-contract attributes). Given that it doesn't... your best bet is to implement your own IModelBinder. Use reflection to get the DataMember names of the properties, and look for those values in the binding context.
Here's a great reference on model binding: http://msdn.microsoft.com/en-us/magazine/hh781022.aspx
A good general approach to maintaining custom binders: http://lostechies.com/jimmybogard/2009/03/18/a-better-model-binder/
EDIT
Generic model binder that handles a defined type. To add this to your application, add this line in global.asax:
ModelBinders.Binders.Add(typeof(UsageModel), new CustomModelBinder<UsageModel>());
And the binder:
public class CustomModelBinder<T> : IModelBinder
{
public override bool IsMatch(Type t)
{
return t == typeof(T);
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
Type t = typeof(T);
var entity = (bindingContext.Model ?? Activator.CreateInstance(t));
// Cycle through the properties and assign values.
foreach (PropertyInfo p in t.GetProperties())
{
string sourceKey;
// this is what you'd do if you wanted to bind to the property name
// string sourceKey = p.Name;
// TODO bind sourceKey to the name in attribute DataMember
Type propertyType = p.PropertyType;
// now try to get the value from the context ...
ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(sourceKey);
if (valueResult != null)
{
bindingContext.ModelState.SetModelValue(sourceKey, valueResult);
p.SetValue(entity, valueResult.ConvertTo(propertyType), null);
}
}
return entity;
}
}
I stumbled across a potential answer to this question randomly while browsing this other question.
I never realized this until now, but apparently you can add attributes to method parameters. Let's take a simple example:
public ActionResult SomeMethod(string val) {
return View(val);
}
If you call this URL -- /MyController/SomeMethod?val=mytestval -- then you'll get back "mytestval" in the model, right? So now you can write this:
public ActionResult SomeMethod([Bind(Prefix="alias")] string val) {
return View(val);
}
Now this URL will produce the same result: /MyController/SomeMethod?alias=mytestval.
Anyway, I'm still not sure if that will answer your question, but I thought it was very interesting.