tags:

views:

42

answers:

2

Hi,

I am currently working on this application(C#,WPF,LINQ2XML,.NET 4.0) that displays details for 'Animals'. An example of this would be the application displaying the types of Animals such as 'Birds','Mammals','Reptiles'. For each type of Animal, I will have sub-entries such as for 'Birds' I will have sub-entries like 'Sparrow','Hawk'; for 'Reptiles', I will have 'Alligators', 'Python'..etc..etc. The user will have the option to add comments for each of these sub-entries. An example would be for the sub-entry 'Sparrow', I will have an option to 'Add Comments'. Clicking on 'Add Comments' will pull up a textbox where the user can enter his or her comments for 'sparrow'. The user can insert multiple comments for a sub-entry. The user can have 1,2,3....n comments for 'Sparrow'. This will just entail clicking on 'Add Comments' multiple times.

The data is preloaded with information stored in XML files. I have separate XMLs for each Animal such as Birds.xml, Mammals.xml, Reptiles.xml. Each of these will have information regarding the sub-entries. Birds.xml will have XML fragments for 'Sparrow' and 'Hawk'.

I am using Linq2Xml to extract the information necessary to populate my animal objects(Model) which in turn are bound to the Presenter(I am using the Model-View-Presenter pattern) which is fed to the XAML.

I am successfully able to display the information from my XML in the view. The problem is with the 'Comments'.

There are two things that I need to achieve here:

  1. Write all the comments added to a separate XML file (AnimalComments.xml) where I can store all the comments that have been added for all the sub-entries.

  2. In case of an application failure, I would like this same XML file(AnimalComments.xml) to pre-populate all the comments that I entered before my application crashed.

In essence I am trying to serialize the state onto the disk but in reality I don't want the entire object graph to persist on the disk since I think I am going to add the feature of writing/adding to the AnimalComments.xml file whenever a comment is typed for a sub-entry and the focus is changed to some other sub-entry.

I was wondering if the collective wisdom of SO can help me with some elegant design guidance here to accumulate all the Comments added and persist them onto a disk in such a way that it can be used by the application later for a restore or to present in a human readable format. Any solutions requiring the use of an DBMS would not be pertinent in this situation since it is part of the requirement to store any data associated with the application on the local file system.

As Drake suggested below, there are ways to maintain the references to the Animals using some sort of Primary key but I am interested in how this should be tackled from an Object Oriented standpoint or if there are some prominent patterns that tackle this sort of a situuation.

Thanks

+1  A: 

In my opinions, your issues are only the matter of how we will organize data structure. So let's have a look at each issue:

1.

Write all the comments added to a separate XML file (AnimalComments.xml) where I can store all the comments that have been added for all the sub-entries

I propose that in the animals.xml file you add an unique ID field for each animal , and in the animals file we use the ID as the foreign key to reference comments back to animals.

For instance, we have a birds.xml like that

    <?xml version="1.0" encoding="utf-8"?>
<Animals>
  <Animal ID="1">
    <Name>Jack Sparrow</Name>
  </Animal>
  <Animal ID="2">
    <Name>Black Hawk</Name>
  </Animal >
</Animals>

Then the animalcomments.xml file should be like that:

<?xml version="1.0" encoding="utf-8"?>
<Animals>
  <Animal ID="1">
    <Comments>
      <Comment CommentID="1">
        <Content>Hello world</Content>
      </Comment>
      <Comment CommentID="2">
        <Content>Hello world 2</Content>
      </Comment>
    </Comments>
  </Animal>
  <Animal ID="2">
    <Comments>
      <Comment CommentID="1">
        <Content>Hello world</Content>
      </Comment>
      <Comment CommentID="2">
        <Content>Hello world 2</Content>
      </Comment>
    </Comments>
  </Animal>  
</Animals>

So you can generate the code based on that data structure, reading and writing to each element of the animalcomments.xml file.

2.

In case of an application failure, I would like this same XML file(AnimalComments.xml) to pre-populate all the comments that I entered before my application crashed.

In your

HandleUnhandledException()

method, saving all your comments before terminating the program. Once the program is reloaded, you can get all your comments back.

Hope this help.

DrakeVN
Thanks Drake. You are absolutely right about maintaining some kind of referential integrity among animals and comments. What I was actually interested in was in the object oriented design as in do I create a separate class for Comments? Do I need some sort of ObservableCollection<Comments> for all the comments that need to be persisted on disk? Are there some prominent design patterns that tackle this sort of situation. I apologize about not making my post a little more clearer.
sc_ray
+1  A: 

Hi sc_ray,

I have created the classes below. Please copy the code and pass to the editor !

The animals.cs

using System.Xml.Serialization;



/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/XMLSchema.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/XMLSchema.xsd", IsNullable=false)]
public partial class Animals {

    private Animal[] animalField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Animal")]
    public Animal[] Animal {
        get {
            return this.animalField;
        }
        set {
            this.animalField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/XMLSchema.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/XMLSchema.xsd", IsNullable=false)]
public partial class Animal {

    private string nameField;

    private int idField;

    private bool idFieldSpecified;

    /// <remarks/>
    public string Name {
        get {
            return this.nameField;
        }
        set {
            this.nameField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public int ID {
        get {
            return this.idField;
        }
        set {
            this.idField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool IDSpecified {
        get {
            return this.idFieldSpecified;
        }
        set {
            this.idFieldSpecified = value;
        }
    }
}

The AnimalComments.cs

using System.Xml.Serialization;




/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/AnimalComments.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/AnimalComments.xsd", IsNullable=false)]
public partial class Animals {

    private Animal[] animalField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Animal")]
    public Animal[] Animal {
        get {
            return this.animalField;
        }
        set {
            this.animalField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/AnimalComments.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/AnimalComments.xsd", IsNullable=false)]
public partial class Animal {

    private Comments commentsField;

    private int idField;

    private bool idFieldSpecified;

    /// <remarks/>
    public Comments Comments {
        get {
            return this.commentsField;
        }
        set {
            this.commentsField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public int ID {
        get {
            return this.idField;
        }
        set {
            this.idField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool IDSpecified {
        get {
            return this.idFieldSpecified;
        }
        set {
            this.idFieldSpecified = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/AnimalComments.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/AnimalComments.xsd", IsNullable=false)]
public partial class Comments {

    private Comment[] commentField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Comment")]
    public Comment[] Comment {
        get {
            return this.commentField;
        }
        set {
            this.commentField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://tempuri.org/AnimalComments.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/AnimalComments.xsd", IsNullable=false)]
public partial class Comment {

    private string contentField;

    private int commentIDField;

    private bool commentIDFieldSpecified;

    /// <remarks/>
    public string Content {
        get {
            return this.contentField;
        }
        set {
            this.contentField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public int CommentID {
        get {
            return this.commentIDField;
        }
        set {
            this.commentIDField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool CommentIDSpecified {
        get {
            return this.commentIDFieldSpecified;
        }
        set {
            this.commentIDFieldSpecified = value;
        }
    }
}

Cheers.

DrakeVN
@DrakeVN - The snippet is helpful. I see you have decorated your classes with Serialization attributes for storing the object on the disk in an XML format. One thing I am a little unsure of is why the classes are partial and the rationale for creating Animals in both classes. Since the properties for ID,IDfield and Animals have already been specified, what do we gain by declaring it again in the class animalcomments.csAlso, I assume the way you envision using this code is by serializing and saving the contents of the Animal object on disk and retrieving it from there upon system crash?
sc_ray
1- The classes are mirrored from the XML files above. That's why the animal class appear in both places. However, they belong to different name spaces. It's better if I had created AnimalComment rather than Animal in the AnimalComments.xml class2- Yes. You can use the code to serialize and deserialize contents to disk using XmlSerializer.Cheers.
DrakeVN
Makes sense. Thanks DrakeVN
sc_ray