I've got a bit of a strange situation going on. I've created a very simple class as such.
[Serializable]
[SettingsSerializeAs(SettingsSerializeAs.Xml)]
public class StockSyncCollection : ObservableCollection<StockSyncDatum>
{
}
public class StockSyncDatum
{
public StockSyncDatum() { }
public StockSyncDatum(int posStockId, int posStockCatalogueId, DateTime lastUpdated)
{
this.PosStockId = posStockId;
this.PosStockCatalogueId = posStockCatalogueId;
this.LastUpdated = lastUpdated;
}
public int PosStockId { get; set; }
public int PosStockCatalogueId { get; set; }
public DateTime LastUpdated { get; set; }
}
I have then created a custom setting through the designer for which the generated code looks like this
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::Sync.Config.StockSyncCollection StockSyncCollection {
get {
return ((global::Sync.Config.StockSyncCollection)(this["StockSyncCollection"]));
}
set {
this["StockSyncCollection"] = value;
}
}
However the problem I am running into is that later in my code (after adding some items to the collection) I will call
Settings.Default.Save();
But what happens is that only some subset of the items that have been added to the collection up until the point the save method is called is actually persisted. So for example, I might add 50 items but only 10 of them will be saved.
Now note that I am calling Settings.Default.Save() in a loop. My gut instinct is telling me that there is some sort of hash that is generated of the object that isn't being updated.
So what I am wondering is how the hell do you debug Application Settings in C# apps? I can see various events like SettingsChanging and SettingsSaving but I do not see an event for SettingsSaveError or similar.
Anyone have any idea what might be going on or how to debug this?
EDIT: The loop code basically looks like this.
foreach (var partition in stockTransfers.Partition(PartitionCount))
{
IList<StockTransferContract> stockTransferContracts = partition.ToStockTransferContracts();
//Do all the magic syncing stuff...
foreach (var item in stockTransferContracts)
_StockSyncCollection.Add(new StockSyncDatum(posStockId, posStockCatalogueId, DateTime.UtcNow));
Settings.Default.Save();
}
Keep in mind that I need to call save because I expect errors to occasionally arise and need to ensure that the previously "Sync'd" items are noted.
Related
For school homework I'm supposed to work out a class diagram in C#. Everything went smoothly, but I'm struggling with the constructor for the Track part.
So I think I have to convert a SectionTypes -> Section to put it in the LinkedList, but this doesn't seem logical to me, or am I missing something? Should I convert it in any way or is my overall code for Section wrong?
Here is the class diagram
Here is the part of Section:
namespace Model
{
public enum SectionTypes { Straight, LeftCorner, RightCorner, StartGrid, Finish }
internal class Section
{
public SectionTypes SectionType { get; set; }
}
}
And finally here is where I'm trying to make the constructor, Track:
namespace Model
{
internal class Track
{
public string Name { get; set; }
public LinkedList<Section> Sections { get; set; }
public Track(string name, SectionTypes[] sections)
{
Name = name;
// set Sections here
}
}
}
The error that I get is CS1503, when I try to add anything to Sections in the front, which means the types aren't the same.
Thanks for reading, and thank you for helping in advance!
Here's what I did. By the way, I renamed the SectionTypes enumeration to SectionType (that way, it reads SectionType.Finish, not SectionTypes.Finish).
First I created the enum the same as you:
public enum SectionType
{
Straight,
LeftCorner,
RightCorner,
StartGrid,
Finish,
}
and the Section class pretty much the same way:
public class Section
{
public SectionType SectionType { get; set; }
}
I'm not sure why the class diagram is laid out the way it is, but you need to translate a SectionType to a Section in order to get it to work. That's pretty easy; a Section is a pretty stupid/simple wrapper around a single SectionType. So, things end up looking like:
public class Track
{
public string Name { get; set; }
public LinkedList<Section> Sections { get; set; } = new LinkedList<Section>();
public Track(string name, SectionType[] sections)
{
Name = name;
foreach (var section in sections)
{
Sections.AddLast(new Section { SectionType = section });
}
}
}
Note that I construct the Sections LinkedList. It can either be done the way I show, or it could be done in the constructor. But, the magic is to convert the incoming SectionType[] sections into a collection of Section type. I'm sure that there is a way to do this with LINQ (though I don't have a lot of experience with the LinkedList collection). But, doing it explicitly like this makes it more clear.
I'm only at chapter 5 in "Essential C#" and not sure if i understand the difference correctly. I tried to make the model below to test one instance of everything in the properties chapter - and it works - but is the example acceptable use of the two ways one can implement properties or are there better ways?
using MarkdownSharp; // StackOverflow's md processor
public class Article
{
public string Headline { get; set; }
public string Content
{
get
{
return _content;
}
set
{
var md = new Markdown();
var html = md.Transform(value);
_content = html;
}
}
private string _content;
public DateTime Published { get; set; } = DateTime.Now;
}
This question may be better suited for codereview.stackexchange, although it's perhaps too tiny a snippet and to vague a question for that.
Personally, I shy away from magic properties that act in surprising ways. It tends to make for APIs that can be hard to use because they are surprising, even if they are somehow “clever” under the hood. You have a property where you set a different value than the one you get out. One thing where this can break would be the += operator, which suddenly would work in very weird ways with your Content property.
I'd probably go with something like
public class Article
{
private string content;
private string renderedContent;
public string Headline { get; set; }
public string Content
{
get { return content; }
set
{
content = value;
renderedContent = null; // reset cached rendered content
}
}
public string RenderedContent
{
get
{
if (renderedContent == null)
{
renderedContent = new Markdown().Transform(content);
}
return renderedContent;
}
}
public DateTime Published { get; set; } = DateTime.Now;
}
As for whether to use field-backed properties, or auto-properties, or computed properties ... that's up to you to decide based on what the property is supposed to do. Auto-properties are fine for simply storing and retrieving a value, e.g. Published or Headline here. You need the explicit backing field as soon as you do something more than just reading or writing it in the getter and setter, as shown here in Content. RenderedContent could be just a computed property, but I chose to cache the value after initial conversion because you kinda do the same. This pattern here doesn't convert the Markdown until it's actually needed, though.
I am going crazy... I am missing something and I can't see what?!?!
I have created a property called "GAME_SETTINGS" inside the gameSparks admin area and have included this in it:
{
"AppVersionIOS": 1,
"AppVersionAndroid": 1
}
I am then trying to retrieve it inside Unity like this:
new GameSparks.Api.Requests.GetPropertyRequest().SetPropertyShortCode("GAME_SETTINGS").Send((response) => {
if (!response.HasErrors) {
Debug.Log("Setting Achieved: "+response.JSONString);
} else {
Debug.Log("Error Getting Settings");
}
});
I can see that I am getting the settings in my Debug.Log:
Setting Achieved: {"#class":".GetPropertyResponse","property":{"AppVersionIOS":1,"AppVersionAndroid":1},"requestId":"XXXXXXXXXXXXXXX","scriptData":null}
My question is though... How do I get the properties AppVersionIOS and AppVersionAndroid inside an Dictionary so I can call on them from other scripts...
Really hoping for help in this matter and thanks in advance :-)
I actually work for GameSparks and noticed your question so set up an account to answer you.
The property values returned in the JSON are of nullable type : https://msdn.microsoft.com/en-us/library/1t3y8s4s.aspx
Best practice is to parse the values before they are cached in a Dictionary or otherwise.
The following code should allow you to get those properties, then you may store them in a dictionary as you see fit.
public void GetProperties()
{
new GameSparks.Api.Requests.GetPropertyRequest()
.SetPropertyShortCode("GAME_SETTINGS")
.Send((response) =>
{
if (!response.HasErrors)
{
print(response.JSONString);
int androidProperty = (int)response.Property.GetInt("AppVersionAndroid");
int IOSProperty = (int)response.Property.GetInt("AppVersionIOS");
print("AndroidProperty:" + androidProperty);
print("IOSProperty:" + IOSProperty);
}
else
{
Debug.LogWarning(response.JSONString);
}
});
}
Hopefully this solves your problem. If you have any other questions please feel free to head to our website and log a ticket with us.
Regards, Patrick.
Notice: This answer assumes that the API doesn't have a way of converting this into a nice object which you can easily manipulate / parse, so it converts it itself using some class. It's however very likely that your API offers such a function somewhere, so you'd be better be looking in the documentation again. I guess it's somewhere near https://api.gamesparks.net/#getpropertyrequest .
You have the JSON document already, all you have to do is parse it. That'd be easier in a JavaScript file than in C#, but you can also use the JsonUtils class there, see http://docs.unity3d.com/Manual/JSONSerialization.html . Let http://json2csharp.com/ convert that JSON to a class layout for you and you get
public class Property
{
public int AppVersionIOS { get; set; }
public int AppVersionAndroid { get; set; }
}
public class RootObject
{
public string __invalid_name__#class { get; set; }
public Property property { get; set; }
public string requestId { get; set; }
public object scriptData { get; set; }
}
Now just take the string and serialize it into an RootObject.
new GameSparks.Api.Requests.GetPropertyRequest().SetPropertyShortCode("GAME_SETTINGS").Send((response) => {
if (!response.HasErrors) {
Debug.Log("Setting Achieved: "+response.JSONString);
//Serialization
var info = JsonUtility.FromJson<RootObject>(response.JSONString);
//Print the AppVersionIOS property
Debug.Log("App Version iOS: " + info.Property.AppVersionIOS);
} else {
Debug.Log("Error Getting Settings");
}
});
You might need some mofication in the data types of your class (e.g. make object scriptData to string scriptData if there can be an actual string in it), but that should be it. Have fun.
i'm building a control that inherits from CompositeControl and creates/maintains a number of dynamically generated child controls. i'm trying to save a class to viewstate before the control tears down the control hierarchy and rebuilds it with a new set of dynamically generated controls. other than persisting this class (as xmlserialized) in viewstate on save, i have no viewstate customization and i am not overriding SaveViewState, SaveControlState, etc.
what's happening in the code below: i have a MySuperClass property on the control that handles my info from load to render. to persist the info through paging events i added a MySuperClassStore to hold onto it. currently, the only data being changed by users is within SubClassControl's SubClass.SubClassResponse (not shown). these changes are being correctly handed back to the SuperClass at the SuperClassControl level (using INotifyPropertyChanged).
the problem: seems to occur on postback (OnControlSave & OnControlPageChange) when you have an existing SuperClass already in viewstate. i'm picking up the existing SuperClass, updating it with new responses, and saving it back to viewstate. but if the SubClassResponse coming from user input is set to empty (by erasing text from or un-selecting the items in the child controls of the SubClassControl), the empty response class (not null; actually initialized as ValueType.Empty) does not update the corresponding entry in the MySuperClassStore before saving back to the viewstate. i've even tried to clear the value in SubClassCollection.UpdateResponse() before setting it, but no luck. i thought i might have a problem with my IEquatable<> but it seems fine. new response values and (non-empty) updated response values are both correctly updated and saved to the viewstate on each save/page change.
BUT (and here's where i'm losing it), when i pop some breakpoints in and step through the code (vs2010), it correctly overwrites the removed entry with the empty (but initialized!) response class and saves back to viewstate (in MySuperClassStore). perfect. every single time. until i quit the debugger, and then it stops working.
stripped down version of my classes:
public class SuperClass {
public Guid SuperClassId { get; set; }
public SubClassCollection ThisCollection { get; set; }
public static SuperClass Deserialize(string s) { /* deserialize xml */ }
}
public class SubClassCollection : KeyedCollection<Guid, SubClass> {
public void UpdateResponse(Guid id, SubClassResponse scr) {
this[id].Response = scr;
//this[id].Response = (!scr.IsEmpty) ? scr : new SubClassResponse();
//** seriously?!? that didn't fix it?
}
}
public class SubClass {
public Guid SubClassId { get; set; }
public int SomeIntProp { get; set; }
private SubClassResponse _response;
public SubClassResponse Response {
get { return (_response != null) ? this._response : new SubClassResponse(); }
set { if (!_response.Equals(value)) { _response = value; OnPropertyChanged("Response"); } }
}
}
public class SubClassResponse : IEquatable<SubClassResponse> {
public string Value { get; set; }
public ValueType ThisValueType { get; set; }
public bool IsEmpty {
switch (this.ThisValueType) {
case ValueType.Empty: return true; break;
case ValueType.StringValue: return String.IsNullOrEmpty(this.Value); break;
case ValueType.ListItemCollection: /* check for null, then .Count > 0 */ break;
default: return true; break;
}
}
public enum ValueType { Empty, StringValue, ListItemCollection, Etc }
}
and my top-level control has this going on:
public class SuperClassControl : CompositeControl, INamingContainer {
// OnInit: load MySuperClass from db
// OnPreRender: get MySuperClassStore from viewstate and output as debug info
protected SuperClass MySuperClass { get; set; }
protected SuperClass MySuperClassStore {
get {
return (ViewState["SuperClassStore"] == null) ? new SuperClass() : SuperClass.Deserialize((string)ViewState["SuperClassStore"]);
}
set { ViewState["SuperClassStore"] = value.ToSerialized(); }
}
protected override void CreateChildControls() {
// generate control hierarchy
SuperClass mysuperclass = this.MySuperClass;
foreach (SubClass mysubclass in mysuperclass.ThisCollection) {
this.Controls.Add(new SubClassControl(mysubclass));
}
// add top-level control linkbuttons, hook up events, etc...
}
protected void OnControlSave(object sender, EventArgs e) {
SuperClass mysuperclass = this.MySuperClass;
SuperClass mystorageclass = this.MySuperClassStore;
// loop through control hierarchy, make sure we've got any response changes
// copy the changes to mystorageclass for persistence
// ***** problem is occurring here?? *****
foreach (Guid g in mysuperclass.ThisCollection.HasChanges) {
mystorageclass.ThisCollection.UpdateResponse(g, mysuperclass.ThisCollection[g].Response);
}
// if the current page only displayed i=5 to 8, will only update i=5 to 8
this.MySuperClassStore = mystorageclass;
}
protected void OnControlPageChange(object sender, EventArgs e) {
OnControlSave(sender, e);
// set next or previous page index then reset control hierarchy
}
}
any thoughts? i've been messing with this for several days now, and i'm running out of ideas. thanks in advance!
will
so, i solved this problem by inverting the update process:
mystorageclass.ThisCollection.UpdateResponse(g, mysuperclass.ThisCollection[g].Response);
this.MySuperClassStore = mystorageclass;
to:
mysuperclass.ThisCollection.UpdateResponse(g, mystorageclass.ThisCollection[g].Response);
this.MySuperClassStore = mysuperclass;
where the loop gets all SubClass NOT existing on the current page.
i works, so "yay?" - but i'm still at a loss for why this actually works better than the original piece of code, so if anyone has some insight into this, i'll happily give you points for the answer!
I have a really strange behavior that actually gives me different result if I step through the code using the debugger or just run the app (press play).
The model looks like this;
public class QuestionPack
{
public int QuestionPackID { get; set; }
public string Hashcode { get; set; }
public virtual ICollection<Question> Questions { get; set; }
}
And I save this class to the DB using a repository-pattern like this;
public class EFQuestionPackRepository : IQuestionPackRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<QuestionPack> QuestionsPacks
{
get { return context.QuestionPacks; }
}
public void SaveQuestionPack(QuestionPack questionpack)
{
if (questionpack.QuestionPackID == 0)
{
context.QuestionPacks.Add(questionpack);
}
else
{
QuestionPack dbEntry = context.QuestionPacks.Find(questionpack.QuestionPackID);
if (dbEntry != null)
{
dbEntry.Hashcode = questionpack.Hashcode;
dbEntry.Questions = questionpack.Questions;
}
}
context.SaveChanges();
}
}
This works perfectly when adding data, but when I try to update (the else statement) I get doubles on the Questions, eg. the questions get saved but with the already saved questions so if i have 4 questions, update that question with ONE question and save, i will end up with 9 questions.
BUT; if I step through the code using the debugger (start from the SaveQuestionPack-function and hit "continue", after context.saveChanges()" I get the expected result. I’m so confused I don’t even know where to start looking.
I guess it have something to do with the fact that I’m trying to store a collection, but why does it work when I step through the save process?
Any input is appreciated.
I think i solved it but im not really sure why it works...
I added this;
dbEntry.Questions.Clear();
before;
dbEntry.Questions = questionpack.Questions;
Anyone having a idea why this corrected the fault?