How to save ObservableCollection in Windows Store App? - c#

I am creating Windows Store App based on Split App template. What is the best way to save data from SampleDataSource for later use?
I tried:
Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
roamingSettings.Values["Data"] = AllGroups;
It throws exception: 'Data of this type is not supported'.

RoamingSettings only supports the runtime data types (with exception of Uri); additionally, there's a limitation as to how much data you can save per setting and in total.
You'd be better off using RoamingFolder (or perhaps LocalFolder) for the storage aspects.
For the serialization aspect you might try the DataContractSerializer. If you have a class like:
public class MyData
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
}
public ObservableCollection<MyData> coll;
then write as follows
var f = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("data.txt");
using ( var st = await f.OpenStreamForWriteAsync())
{
var s = new DataContractSerializer(typeof(ObservableCollection<MyData>),
new Type[] { typeof(MyData) });
s.WriteObject(st, coll);
and read like this
using (var st = await Windows.Storage.ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("data.txt"))
{
var t = new DataContractSerializer(typeof(ObservableCollection<MyData>),
new Type[] { typeof(MyData) });
var col2 = t.ReadObject(st) as ObservableCollection<MyData>;
}

Related

Execute request model binding manually

In Asp.Net you can automatically parse request data into an input model / API contract using for example the attributes FromBody, FromQuery, and FromRoute. I want to execute this behavior myself. Let me explain.
I want to have a custom policy requirement based on a combination of data passed to the requirement and the target entity which is passed inside the request data. But this target entity id can be in different locations. Usually the body, but for example the route or the query when using HttpGet. So I thought about putting this information about the location above the controller endpoint using an attribute. The following pseudo-code is based on the guess that I need the BindingSource.
I would create API contracts using an interface defining the location of the target id.
public interface ITargetEntityContract {
public string TargetEntityId { get; set; }
}
public class ExampleRequest : ITargetEntityContract {
public string TargetEntityId { get; set; } = default!;
public string SomeOtherData { get; set; } = default!;
}
Then I would create an attribute to define the location:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class TargetEntityLocationAttribute : Attribute {
public Type ContractType { get; }
public BindingSource BindingSource { get; }
public TargetEntityLocationAttribute(Type contractType, BindingSource bindingSource) {
if (!typeof(ITargetEntityContract).IsAssignableFrom(contractType))
throw new Exception("Contract has to implement the interface ITargetEntityContract");
this.ContractType = contractType;
this.BindingSource = bindingSource;
}
}
And you would apply this onto a controller endpoint the following way:
[TargetEntityLocation(typeof(ExampleRequest), BindingSource.Body)]
public async Task<IActionResult> SomeEndpointAsync([FromBody] ExampleRequest requestData) {
}
Within the IAuthorizationHandler I would use these classes the following way:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ExampleAuthorizationRequirement requirement) {
var endpoint = this._httpContextAccessor.HttpContext!.GetEndpoint();
if (endpoint is null)
throw new Exception("Some error");
var targetEntityLocation = endpoint.MetaData.GetMetaData<TargetEntityLocationAttribute>();
if (targetEntityLocation is null)
throw new Exception("some error");
var targetEntityModel = SomeAlmightyParser.ParseFromSource(this._httpContextAccessor.HttpContext!, targetEntityLocation.ContractType, targetEntityLocation.BindingSource) as ITargetEntityContract;
// do something with targetEntityModel.TargetEntityId
}
Is there a way to parse the request data of the HttpContext into the given model based on the data location?
If the source of the data is RequestBody,you could read the stream with StreamReader and deserialize the string you get;
If the source of the data is RequestQuery,you could map the key-value pairs to your target object with reflection
For example,I tried as below:
//read obj from requestbody
using var reader = new StreamReader(context.Request.Body);
var bodystr = reader.ReadToEndAsync().Result;
if (bodystr != "")
{
var obj1 = JsonSerializer.Deserialize(bodystr, typeof(ExampleRequest), new JsonSerializerOptions() { });
}
//read simple obj from query
var querycollection = context.Request.Query;
var type = typeof(ExampleRequest);
var properties = type.GetProperties();
object obj = Activator.CreateInstance(type);
foreach (var propinfo in properties)
{
var propname = propinfo.Name;
if (querycollection.ContainsKey(propname))
{
var proptype = propinfo.PropertyType;
propinfo.SetValue(obj, Convert.ChangeType(querycollection[propname].ToString(), proptype),null);
}
}
var obj2 = obj as ExampleRequest;
Result:

Calling ARM Template from C#

I am working on a requirement where I want to call ARM Template dynamically from code passing parameters, like Resource Name, Resource Location etc.,
I am able to create ARM template. For example, created ARM Template for storage account and I have parameter file with values to pass to the template. However these values I want to pass from c# code dynamically and provision the resource in Azure.
From Power shell I am able to achieve this but I want the same to be done from c#.
Any suggestions/technical links that I can explore.
I use the following function to deploy an ARM Template using c#
private async Task DeployTemplate(string resourceGroupName, string deploymentName, JObject templateFileContents, JObject parameters)
{
var deployment = new Deployment
{
Properties = new DeploymentProperties
{
Mode = DeploymentMode.Incremental,
Template = templateFileContents,
Parameters = parameters
}
};
var serviceCredentials = await ApplicationTokenProvider.LoginSilentAsync(_tenantId, _clientId, _clientSecret);
var resourceManagementClient = new ResourceManagementClient(serviceCredentials)
{
SubscriptionId = _subscriptionId
};
await resourceManagementClient.Deployments.CreateOrUpdateAsync(resourceGroupName, deploymentName, deployment);
}
The templateFileContents get filled by this function:
private static JObject GetJsonFileContents(string pathToJson)
{
var path = Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), pathToJson);
using (var file = File.OpenText(path))
{
using (var reader = new JsonTextReader(file))
{
return (JObject)JToken.ReadFrom(reader);
}
}
}
This is how I create a dynamic parameters object:
var parameters = JObject.FromObject(
new DbCloneArmParameters
{
databaseName = new ArmParameterValue { Value = databaseName },
serverName = new ArmParameterValue { Value = servername },
location = new ArmParameterValue { Value = "westeurope" } }
});
Helper classes:
public class DbCloneArmParameters
{
public ArmParameterValue databaseName { get; set; }
public ArmParameterValue serverName { get; set; }
public ArmParameterValue location { get; set; }
}
public class ArmParameterValue
{
public string Value { get; set; }
}
I think Microsoft.Azure.Management.Fluent solves your problem.
If you look at Microsoft documentation "Deploy an Azure Virtual Machine using C# and a Resource Manager template", it walks you through deploying a VM using a template file from C#. The documentation could be used as a starting point to deploy any ARM template to Azure.

c# how to decide whether to add to sub-list or start new one

I have an sql query that provides me my data where I sometimes have lines that should be clustered (the data is aligned with an order by). The data is grouped by the field CAPName. Going through those rows line by line, I need to decide whether a new list should be initiated (content of CAPName differs to previous itteration), or whether the (already) initated list (from the previous iteration) should be added, too.
My pain lays with the location of the declaration of the relatedCapabilitySystem list.
I wanted to declare it within the if statement (Because, as I stated I need to decide whether the list from the previous iteration should be added too, or whether it should start a new list), but I can't as the compiler throws an exception, as the RLCapSys.Add(rCs); is non-existing in this content (which is only theoretically true). I understand why the compiler throws this exception. But if I declare the list on a "higher" level, than I always have a new list, which I don't want in the case that the item should be added to the list defined in the iteration(s) (1 or more) before
So what I want to achieve is, generate the list RLCapSys and add to it, in case the previous iteration contains the same CAPName (for clustering), otherwise create a new list.
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
string lastCapShown = null;
while (rdrDetail.Read())
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
//List<relatedCapabilitySystem> RLCapSys2 = new List<relatedCapabilitySystem>();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
and for reason of completness (but I think it is not needed here):
internal class CapabilitiesC
{
public List<Capability>Capabilities{ get;set;}
}
public class Capability
{
public string name { get; internal set; }
public string tower { get; internal set; }
public string color { get; internal set; }
public List<relatedCapility> related { get; set; }
}
public class relatedCapility
{
public string Capability { get; set; }
public List<relatedCapabilitySystem> systemsRelated { get; set; }
}
public class relatedCapabilitySystem
{
public string system { get; set; }
public string start { get; set; }
public string end { get; set; }
}
The purpose of your code is to take the input data and group it by capability. However, that is not immediately obvious. You can change your code to use LINQ so it becomes easier to understand and in the process solving your problem.
First you need a type to represent a record in your database. For lack of better name I will use Record:
class Record
{
public string System { get; set; }
public string Start { get; set; }
public string End { get; set; }
public string Capabilty { get; set; }
}
You can then create an iterator block to return all the records from the database (using an OR mapper like Entity Framework avoids most of this code and you can even shift some of the work from your computer to the database server):
IEnumerable<Record> GetRecords()
{
// Code to create connection and command (preferably in a using statement)
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
while (rdrDetail.Read())
{
yield return new Record {
System = rdrDetail["name"].ToString(),
Start = rdrDetail["SysStart"].ToString(),
End = rdrDetail["SysEnd"].ToString(),
Capability = rdrDetail["CAPName"].ToString()
};
}
// Close connection (proper using statement will do this)
}
Finally, you can use LINQ to perform the grouping:
var RLCaps = GetRecords()
.GroupBy(
record => record.Capability,
(capability, records) => new relatedCapility
{
Capability = capability ,
systemsRelated = records
.Select(record => new relatedCapabilitySystem
{
system = record.System,
start = record.Start,
end = record.End
})
.ToList()
})
.ToList();
Why not just assign it as NULL. The pattern would be
List<> myList = null;
if(condition)
{
myList = new List<>();
}
else
{
myList = previousList;
}
myList.Add();
previousList = myList;
I've got it working now. Thx everyone for your help. #martin, thx for your solution, you have put quite some effort into this, but that would have required for me to completely re-write my code. I am sure your approach would work and will be my next approach should I have a similar problem again.
It was a combination of the other answers that helped me figure it out. Let me show you what I ended up with:
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
string lastCapShown = null;
while (rdrDetail.Read())
{
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
RLCapSys = relatedCapabilitySystemList();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
So that's the section already shown bevor including my changes. Plus I added this:
private List<relatedCapabilitySystem> relatedCapabilitySystemList()
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
return RLCapSys;
}
Now I have new list reference everytime the CapName changes that is then added to the "higher" list. Before I had the issue of the very same list repeatedly assigned rather than a fresh one started. So thx again for your effort.

Listen for object changes from the outside in C#

I have an object which I would like to monitor in case its data changes. However I am not allowed to add additional events or interfaces like INotifyPropertyChanged to the object, so I need to purely watch it from the outside and see if it has changed in character and the go through all it's properties to identify what has changed.
So I basically need a FileSystemWatcher, but for an object.
Does C# offer any functionality I am looking for here?
You'll have to brute-force it and do something like capturing the state of the object and then look for differences when you need to detect changes (either through periodic polling or at specific points where you need to know the diffs).
This, I believe, is what a number of systems that monitor for changes in data model objects do (e.g. RavenDB).
I made a quick and dirty console app that roughly does what you are wanting. It should be enough to give you an idea but will need a whole lotta work if you want to use it in production code. :)
class Program
{
static void Main(string[] args)
{
DemoClass demo = new DemoClass { IntValue = 1, StringValue = "asdf" };
var watcher = new DemoClassWatcher();
watcher.Capture(demo);
demo.StringValue = "1234";
var results = watcher.Compare(demo); // results = "StringValue"
demo.IntValue = 1234;
results = watcher.Compare(demo); // results = "StringValue", "IntValue"
watcher.Capture(demo);
results = watcher.Compare(demo); // results = empty
}
}
public class DemoClass
{
public string StringValue { get; set; }
public int IntValue { get; set; }
}
public class DemoClassWatcher
{
private DemoClass lastRecorded = null;
public void Capture(DemoClass objectToWatch)
{
lastRecorded = new DemoClass()
{
IntValue = objectToWatch.IntValue,
StringValue = objectToWatch.StringValue
};
}
public List<string> Compare(DemoClass currentObject)
{
var changes = new List<string>();
var props = typeof(DemoClass).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var propertyInfo in props)
{
var currentVal = propertyInfo.GetValue(currentObject);
var prevVal = propertyInfo.GetValue(lastRecorded);
if (currentVal is IComparable)
{
if ((currentVal as IComparable).CompareTo(prevVal) != 0)
changes.Add(propertyInfo.Name);
continue;
}
throw new NotSupportedException("Properties must support IComparable until someone fixes me up");
}
return changes;
}
}
You can't listen for events that are not there, but you can poll the states and compare them to their previous state.
Pseudocode:
///initial config:
yourObject = getAReferenceToTheObject();
previousState = new ObjectClass(yourObject);
///somewhere in a thread/loop
if (previousState.SomeProperty!= yourObject.SomeProperty){
//state changed for SomeProperty on yourObject
}
//TODO: check other properties
previousState = new ObjectClass(yourObject); //implement copy constructor

How to cast from a list with objects into another list.

I have an Interface [BindControls] which takes data from GUI and store it into a list „ieis”.
After that, Into another class, which sends this data through WebServices, I want to take this data from „ieis” and put it into required by WS Class fields (bottom is a snippet of code)
This is the interface:
void BindControls(ValidationFrameBindModel<A.B> model)
{
model.Bind(this.mtbxTax, (obj, value) =>
{
var taxa = TConvertor.Convert<double>((string)value, -1);
if (taxa > 0)
{
var ieis = new List<X>();
var iei = new X
{
service = new ServiceInfo
{
id = Constants.SERVICE_TAX
},
amount = tax,
currency = new CurrencyInfo
{
id = Constants.DEFAULT_CURRENCY_ID
}
};
ieis.Add(iei);
}
},"Tax");
}
This is the intermediate property:
//**********
class A
{
public B BasicInfo
{
get;
set;
}
class B
{
public X Tax
{
get;
set;
}
}
}
//***********
This is the class which sends through WS:
void WebServiceExecute(SomeType someParam)
{
//into ‚iai’ i store the data which comes from interface
var iai = base.Params.FetchOrDefault<A>( INFO, null);
var convertedObj = new IWEI();
//...
var lx = new List<X>();
//1st WAY: I tried to put all data from ‚Tax’into my local list ‚lx’
//lx.Add(iai.BasicInfo.Tax); - this way is not working
//2nd WAY: I tried to put data separately into ‚lx’
var iei = new X
{
service = new ServiceInfo
{
id = iai.BasicInfo.Tax.service.id
},
amount = iai.BasicInfo.Tax.amount,
currency = new CurrencyInfo
{
id = iai.BasicInfo.Tax.currency.id
}
};
lx.Add(iei);
// but also is not working
Can you help me please to suggest how to implement a way that will fine do the work (take data from ‚ieis’ and put her into ‚lx’).
Thank you so much
As noted in my comment, it looks like iai.BasicInfo.Tax is null, once you find out why that is null your original Add() (#1) will work.

Categories

Resources