my first question is here
however since I was advised that questions should not change the original matter I created a new one.
I am saving user settings and I would like to save it in the list, I have had a look on setting by James however I found that that its not possible to save it in the list. So ia have decided to use Xamarin Essentials.
First I tried to save only a string value, which after some struggle I managed to work out and now I am trying to save an object
static void AddToList(SettingField text)
{
var savedList = new List<SettingField>(Preference.SavedList);
savedList.Add(text);
Preference.SavedList = savedList;
}
private void ExecuteMultiPageCommand(bool value)
{
var recognitionProviderSettings = new RecognitionProviderSettings
{SettingFields = new List<SettingField>()};
var set = new SettingField()
{
ProviderSettingId = "test",
Value = "test"
};
AddToList(set);
NotifyPropertyChanged("IsMultiPage");
}
and then the sterilization and des
public static class Preference
{
private static SettingField _settingField;
public static List<SettingField> SavedList
{
get
{
//var savedList = Deserialize<List<string>>(Preferences.Get(nameof(SavedList), "tesr"));
var savedList = Newtonsoft.Json.JsonConvert.DeserializeObject<SettingField>(Preferences.Get(nameof(SavedList), _settingField)) ;
SavedList.Add(savedList);
return SavedList ?? new List<SettingField>();
}
set
{
var serializedList = Serialize(value);
Preferences.Set(nameof(SavedList), serializedList);
}
}
static T Deserialize<T>(string serializedObject) => JsonConvert.DeserializeObject<T>(serializedObject);
static string Serialize<T>(T objectToSerialize) => JsonConvert.SerializeObject(objectToSerialize);
}
}
But Preferences.Get doesn't take object, is there any other way how can I save my setting to a object list? Please advise
I would recommend you to use SecureStorage. You can save your strings only into it. So the place where you have serilized your object as json. Just convert your json to string with .ToString() and save it into secure storage.
You may continue saving your serialized json object as string in Shared preferences but it is recommended to use SecureStorage Instead.
Related
I am writing unit tests for my asp.net web API application and one of them is trying to verify that AddOrGetExisting is working correctly. According to the MSDN documentation, AddOrGetExisting returns an item if it's already saved, and if not it should write it into Cache.
The problem I am having is that if I add the key to MemoryCache object from an unit test, then call AddOrGetExisting, it will always return null and overwrite the value instead of returning the value that is already saved. I am verifying that the value is in the cache right before I call AddOrGetExisting(bool isIn evaluates to true).
Here is the code for my memory cache and the test method. Any help would be much appreciated:
public static class RequestCache
{
public static TEntity GetFromCache<TEntity>(string key, Func<TEntity> valueFactory) where TEntity : class
{
ObjectCache cache = MemoryCache.Default;
var newValue = new Lazy<TEntity>(valueFactory);
CacheItemPolicy policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(60) };
bool isIn = cache.Contains(key);
// Returns existing item or adds the new value if it doesn't exist
var value = cache.AddOrGetExisting(key, newValue, policy) as Lazy<TEntity>;
return (value ?? newValue).Value;
}
}
public string TestGetFromCache_Helper()
{
return "Test3and4Values";
}
[TestMethod]
public void TestGetFromCache_ShouldGetItem()
{
ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(60) };
var cacheKey = "Test3";
var expectedValue = "Test3Value";
cache.AddOrGetExisting(cacheKey, expectedValue, policy);
var result = Models.RequestCache.GetFromCache(cacheKey,
() =>
{
return TestGetFromCache_Helper();
});
Assert.AreEqual(expectedValue, result);
}
The issue may be that you're passing a Lazy<TEntity> as newValue within RequestCache.GetFromCache but passing a string as expectedValue in the test method.
When running the test, the cache.Contains(key) confirms that there is a value stored for that key, which is true. However it is a string instead of a Lazy<TEntity>. Apparently AddOrGetExisting decides to overwrite the value in that case.
The fix for this particular scenario may be to adjust the expectedValue assignment in your test to something like this:
var expectedValue = new Lazy<string>(TestGetFromCache_Helper);
You'd also need to pull the value from the Lazy in the test's final equality comparison, for example:
Assert.AreEqual(expectedValue.Value, result);
I have different plugins in my Web api project with their own XML docs, and have one centralized Help page, but the problem is that Web Api's default Help Page only supports single documentation file
new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Documentation.xml"))
How is it possible to load config from different files? I wan to do sth like this:
new XmlDocumentationProvider("PluginsFolder/*.xml")
You can modify the installed XmlDocumentationProvider at Areas\HelpPage to do something like following:
Merge multiple Xml document files into a single one:
Example code(is missing some error checks and validation):
using System.Xml.Linq;
using System.Xml.XPath;
XDocument finalDoc = null;
foreach (string file in Directory.GetFiles(#"PluginsFolder", "*.xml"))
{
if(finalDoc == null)
{
finalDoc = XDocument.Load(File.OpenRead(file));
}
else
{
XDocument xdocAdditional = XDocument.Load(File.OpenRead(file));
finalDoc.Root.XPathSelectElement("/doc/members")
.Add(xdocAdditional.Root.XPathSelectElement("/doc/members").Elements());
}
}
// Supply the navigator that rest of the XmlDocumentationProvider code looks for
_documentNavigator = finalDoc.CreateNavigator();
Kirans solution works very well. I ended up using his approach but by creating a copy of XmlDocumentationProvider, called MultiXmlDocumentationProvider, with an altered constructor:
public MultiXmlDocumentationProvider(string xmlDocFilesPath)
{
XDocument finalDoc = null;
foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
{
using (var fileStream = File.OpenRead(file))
{
if (finalDoc == null)
{
finalDoc = XDocument.Load(fileStream);
}
else
{
XDocument xdocAdditional = XDocument.Load(fileStream);
finalDoc.Root.XPathSelectElement("/doc/members")
.Add(xdocAdditional.Root.XPathSelectElement("/doc/members").Elements());
}
}
}
// Supply the navigator that rest of the XmlDocumentationProvider code looks for
_documentNavigator = finalDoc.CreateNavigator();
}
I register the new provider from HelpPageConfig.cs:
config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/")));
Creating a new class and leaving the original one unchanged may be more convenient when upgrading etc...
Rather than create a separate class along the lines of XmlMultiDocumentationProvider, I just added a constructor to the existing XmlDocumentationProvider. Instead of taking a folder name, this takes a list of strings so you can still specify exactly which files you want to include (if there are other xml files in the directory that the Documentation XML are in, it might get hairy). Here's my new constructor:
public XmlDocumentationProvider(IEnumerable<string> documentPaths)
{
if (documentPaths.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(documentPaths));
}
XDocument fullDocument = null;
foreach (var documentPath in documentPaths)
{
if (documentPath == null)
{
throw new ArgumentNullException(nameof(documentPath));
}
if (fullDocument == null)
{
using (var stream = File.OpenRead(documentPath))
{
fullDocument = XDocument.Load(stream);
}
}
else
{
using (var stream = File.OpenRead(documentPath))
{
var additionalDocument = XDocument.Load(stream);
fullDocument?.Root?.XPathSelectElement("/doc/members").Add(additionalDocument?.Root?.XPathSelectElement("/doc/members").Elements());
}
}
}
_documentNavigator = fullDocument?.CreateNavigator();
}
The HelpPageConfig.cs looks like this. (Yes, it can be fewer lines, but I don't have a line limit so I like splitting it up.)
var xmlPaths = new[]
{
HttpContext.Current.Server.MapPath("~/bin/Path.To.FirstNamespace.XML"),
HttpContext.Current.Server.MapPath("~/bin/Path.To.OtherNamespace.XML")
};
var documentationProvider = new XmlDocumentationProvider(xmlPaths);
config.SetDocumentationProvider(documentationProvider);
I agree with gurra777 that creating a new class is a safer upgrade path. I started with that solution but it involves a fair amount of copy/pasta, which could easily get out of date after a few package updates.
Instead, I am keeping a collection of XmlDocumentationProvider children. For each of the implementation methods, I'm calling into the children to grab the first non-empty result.
public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
private IList<XmlDocumentationProvider> _documentationProviders;
public MultiXmlDocumentationProvider(string xmlDocFilesPath)
{
_documentationProviders = new List<XmlDocumentationProvider>();
foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
{
_documentationProviders.Add(new XmlDocumentationProvider(file));
}
}
public string GetDocumentation(System.Reflection.MemberInfo member)
{
return _documentationProviders
.Select(x => x.GetDocumentation(member))
.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
}
//and so on...
The HelpPageConfig registration is the same as in gurra777's answer,
config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/")));
asp.net C#4
I have a simple class to working with query strings.
A new instance is created like this:
public QueryString(string querystring)
{
try
{
_table = new Hashtable();
if (querystring.Length > 0)
{
foreach (string pair in querystring.Split('&'))
{
string[] item = pair.Split('=');
_table.Add(item[0].ToLower(), item[1]);
}
}
}
catch (Exception)
{
}
}
I want to add a method to this that will remove a key value pair. I don't want it to return a new querystring, I just want it to remove the pair from the current instance. Not sure how to do that since it says I can't assign a value to 'this'
public void Remove(string key)
{
String querystring = this.ToString();
try
{
_table = new Hashtable();
if (key.Length > 0)
{
foreach (string pair in querystring.Split('&'))
{
string[] item = pair.Split('=');
if (item[0] != key)
{
_table.Add(item[0].ToLower(), item[1]);
}
}
this = _table;
}
}
catch (Exception)
{
}
}
You're overcomplicating things. Since your class's state is made up of the _table field, all you need to do is remove the item with the given key from that field.
The following example replaces your untyped Hashtable wit a strongly-typed Dictionary. I also chose to initialize the dictionary with a LINQ statement, but you could keep your old code there if you prefer.
public class QueryString
{
private readonly Dictionary<string, string> _table;
public QueryString(string querystring)
{
if (querystring.Length > 0)
{
var pairs =
from pair in querystring.Split('&')
let item = pair.Split('=')
select new {key = item[0], value = item[1]};
_table = pairs.ToDictionary(p => p.key, p => p.value);
}
}
public void Remove(string key)
{
_table.Remove(key);
}
}
You cannot assign a value to this since it is a reference to the object itself.
However, if you remove the line this = _table; , isn't things working as they should then? I guess your ToString() is somewhat using the hashtable to generate a "printer friendly" QueryString, and if that is the case, the way I see it, your Remove() method should be working (since you are replacing the _table variable with a new HashTable not including the key-value pair you want to exclude).
you are passing a querystring into the class so the original querystring IS intact.
However you then break down the querystring into a a Hashtable of key/value pairs. If you want to keep THAT intact you need to clone the HashTable and perform the remove on the clone.
In any case it's probably a good idea to keep the querystring you are passing in as a constructor parameter in a member variable for safe keeping.
The Goal is to have a list of options (that a user can chose through radio buttons) in one place(for eg: a yaml config file). No other place should have this list hard-coded
I've done something similar to create select elements, and I think enums worked just fine. Doing radio buttons should be very similar. I've set it up so that the labels can be defined in the messages file. I'm going to try to excerpt the relevant portions from my larger auto-form-generation code (using FastTags) the best I can. It's a bit heavy for this one case but it makes sense in the larger system.
I use the tag like #{form.selector 'order.status' /}, which looks find the variable named order in the template, sees that status is declared as public Status status, and then goes to find all the values of the Status enum and generate options for them in the select element.
First, I use a FieldContext object which just contains a bunch of info that's used by the other code to determine what to generate along with some utility methods:
public class FieldContext {
public final Map<?,?> args;
public final ExecutableTemplate template;
public final int fromLine;
public Class clazz = null;
public Field field = null;
public Object object = null;
public Object value = null;
private Map<String,String> attrs = new HashMap<String,String>();
private Map<String,Boolean> printed = new HashMap<String,Boolean>();
private List<Option> options;
...
Then I have this in another helper class (its info gets added to the FieldContext):
public List<Option> determineOptions(FieldContext context) {
List<Option> options = new ArrayList<Option>();
if (context.field.getType().isEnum()) {
for (Object option : context.field.getType().getEnumConstants()) {
options.add(new Option(option.toString(), Message.get(option.toString())));
}
}
return options;
}
then the tag declaration is
public static void _selector(Map<?,?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) {
String field_name = args.get("arg").toString();
TagContext.current().data.put("name", field_name);
SelectHelper helper = HelperFactory.getHelper(SelectHelper.class);
try {
FieldContext context = new FieldContext(field_name, args, template, fromLine);
helper.autoconfigure(context);
TagContext.current().data.put("selected", helper.determineValue(context));
out.print("<div class=\"formutil-field formutil-selector\">");
out.print("<label for=\"" + context.getAttr("id") + "\">");
out.print(helper.findOrCreateLabel(context));
out.print("</label>");
out.print("<select");
context.printAttribute(out, "id", "name");
out.print(">");
if (context.hasOptions()) {
for (Option option : context.getOptions()) {
out.print("<option value=\"" + option.value + "\">" + option.label + "</option>");
}
}
out.print("</select>");
context.printErrorIfPresent(out);
context.printValidationHints(out);
out.println("</div>");
}
...
}
Given the following interface
public interface ISomething {
void DoMany(string[] strs);
void DoManyRef(ref string[] strs);
}
I would like to verify that the DoManyRef method is called, and passed any string array as the strs parameter. The following test fails:
public void CanVerifyMethodsWithArrayRefParameter() {
var a = new Mock<ISomething>().Object;
var strs = new string[0];
a.DoManyRef(ref strs);
var other = It.IsAny<string[]>();
Mock.Get(a).Verify(t => t.DoManyRef(ref other));
}
While the following not requiring the array passed by reference passes:
public void CanVerifyMethodsWithArrayParameter() {
var a = new Mock<ISomething>().Object;
a.DoMany(new[] { "a", "b" });
Mock.Get(a).Verify(t => t.DoMany(It.IsAny<string[]>()));
}
I am not able to change the interface to eliminate the by reference requirement.
For verifying against ref arguments, you need to pass the actual instance into the verify call. This means your first test should appear as follows:
[Test]
public void CanVerifyMethodsWithArrayRefParameter()
{
var a = new Mock<ISomething>().Object;
var strs = new string[0];
a.DoManyRef(ref strs);
Mock.Get(a).Verify(t => t.DoManyRef(ref strs));
}
The final sentence of the question leads me to believe you might not be able to make that change, but that is what is required for the Verify call to succeed. Hope this helps.