0

I have the following problem:

I have a class called MedicalInfo and I would like its list to have at least one Prescription. The thing is I thought of fitting in some logic in its constructor, as you can see below, but it has a major con: when I deserialize it (xml) it adds a new one.

I imagine that the Serializer first creates the objects (and therefor adds ALWAYS one instance of Prescription) and afterwards adds the values of the fields. So, in a nutshell, everytime I de-serialize I end up with one extra instance... How can I avoid this?

Just to put some context, I'm developing a WinForms application that serializes on the app's close and deserializes on its open, in order to have the data avaliable during its use. Furthermore, MedicalInfo is part of a class called Client that has many properties, one for instance is a List of Policies. What I serialize and Deserialize is a list of clients (an addressbook). I thought of doing the "check" after deserialization... but that would, in some cases, need a double foreach. I'm not sure if that would be optimal.

public class MedicalInfo
{
    public string MedicareNumber { get; set; }
    public DateTime PartAEffectiveDate { get; set; }
    public DateTime PartBEffectiveDate { get; set; }
    public List<Prescription> Prescriptions { get; set; }

    public MedicalInfo()
    {
        PartAEffectiveDate = new DateTime(1900, 01, 01);
        PartBEffectiveDate = new DateTime(1900, 01, 01);
        Prescriptions = new List<Prescription>().Min(1);

        if (Prescriptions.Count == 0)
        {
            Prescriptions.Add(new Prescription());
            Prescriptions.FirstOrDefault().Instructions = "Default Prescription";
        }
    }
}

public class Client
{
    #region Properties

    private Guid ID { get; set; }

    [XmlIgnore]
    public string FullName
    {
        get
        {
            return FirstName + " " + MiddleName + " " + LastName;
        }
    }
    [XmlIgnore]
    public string DisplayName
    {
        get
        {
            return LastName + ", " + FirstName;
        }
    }

    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public bool Male { get; set; }
    public DateTime Birthday { get; set; }
    public Address Address { get; set; }
    public int SSN { get; set; }
    public long Phone { get; set; }
    public long AlternatePhone { get; set; }
    public string Email { get; set; }
    public int Height { get; set; }
    public int Weight { get; set; }
    public string Notes { get; set; }

    public BankAccount BankInfo { get; set; }
    public MedicalInfo MedInfo { get; set; }

    public DateTime RegisteredTime { get; set; }

    #endregion Properties

    public Client()
    {
        ID = new Guid();
        RegisteredTime = DateTime.Now;

        Birthday = new DateTime(1900, 01, 01);
        BankInfo = new BankAccount();
        MedInfo = new MedicalInfo();
        Address = new Address();
    }
}

public class InsuranceClient : Client
{
    public bool Tobacco { get; set; }
    public PolicyCollection Policies { get; set; }

    public InsuranceClient() : base()
    {
        Policies = new PolicyCollection();

        if (Policies.Count == 0)
        {
            Policies.Add(new Policy());
            Policies.FirstOrDefault().Plan = "Default Policy";
        }
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Javier García Manzano
  • 1,024
  • 2
  • 12
  • 25
  • One simple option would be to make your `Policies` property private and create another public property that exposed it but also handled the empty case returning a single collection of "Default Policy" – Chris Haas Dec 08 '15 at 19:26
  • 1
    I would move that logic out of the constructor. The only place you would have to make sure there's at least one item is when first creating your brand new (Insurance)Client. So, move it to a factory/builder that is responsible for creating that object. – Mark Dec 08 '15 at 19:26
  • Oh, thanks @Mark , I hadn't thought of that! Maybe with a factory method for Client & InsuranceClient? That was really enlightening. Thanks! :) – Javier García Manzano Dec 08 '15 at 19:42
  • @JavierGarcíaManzano -- Any correct answers yet? – David Pine Dec 08 '15 at 20:15
  • You can serialize the List using a proxy array property. See [this answer to "How to ignore a specific List item when serializing a container class"](https://stackoverflow.com/questions/33786527/how-to-ignore-a-specific-listt-item-when-serializing-a-container-class/33792835#33792835) – dbc Dec 09 '15 at 00:06
  • I know where I've seen this before: [XML Deserialization of collection property with code defaults](https://stackoverflow.com/questions/27927496/xml-deserialization-of-collection-property-with-code-defaults). – dbc Dec 09 '15 at 00:42
  • @dbc thanks, that did solve the problem too. What does proxy array property mean? – Javier García Manzano Dec 12 '15 at 21:22
  • "proxy array property". Say you have a collection property `Foo`. A "proxy array property" returns `Foo.ToArray()` -- i.e. a shallow copy of the underlying property as an array. Setting it populates the underlying collection with the array elements. This can be useful because arrays cannot be resized -- they must be fully populated before being set. – dbc Dec 12 '15 at 21:46

2 Answers2

0

Add a member variable to store you prescriptions in. Add the business rule to the getter of the Prescriptions property.

public class MedicalInfo
{
    public string MedicareNumber { get; set; }
    public DateTime PartAEffectiveDate { get; set; }
    public DateTime PartBEffectiveDate { get; set; }

    private List<Prescription> _prescritpions;
    public List<Prescription> Prescriptions
    {
        get
        {
            if (_prescritpions.Count == 0)
            {
                _prescritpions.Add(new Prescription
                {
                    Instructions = "Default Description"
                });
            }

            return _prescritpions;
        }
        set
        {
            _prescritpions = value;
        }
    }

    public MedicalInfo()
    {
        PartAEffectiveDate = new DateTime(1900, 01, 01);
        PartBEffectiveDate = new DateTime(1900, 01, 01);

        _prescritpions = new List<Prescription>();
    }
}

edit: removed uneccessary instantiation of Prescriptions from constructor.

johnnycreaks
  • 101
  • 4
0

You need to remove the initialization from the constructor. I would suggest utilizing the null-coalescing operator ?? as detailed below.

public class MedicalInfo
{
    public string MedicareNumber { get; set; }
    public DateTime PartAEffectiveDate { get; set; }
    public DateTime PartBEffectiveDate { get; set; }

    public List<Prescription> Prescriptions
    {
        get
        {
            return _prescritpions ?? (_prescritpions = GetDefaultPrescriptions());
        }
        set
        {
            _prescritpions = value;
        }
    }
    List<Prescription> _prescritpions;

    public MedicalInfo()
    {
        PartAEffectiveDate = new DateTime(1900, 01, 01);
        PartBEffectiveDate = new DateTime(1900, 01, 01);
    }

    static List<Prescription> GetDefaultPrescriptions()
    {
        return new List<Prescription> 
               { 
                   new Prescription { Instructions = "Default Description" }     
               };
    }
}

The advantage is that if the _perscriptions variable is already instantiated -- it will simply return. Otherwise, if it is null it will instantiate it with the default instance as specified in the GetDefaultPrescriptions function. This should prevent the duplicate creation of the default variable as found with the serialization issue.

David Pine
  • 23,787
  • 10
  • 79
  • 107
  • Thanks David, that is definetly the faster solution. I solved it in a different way, implementing a Factory Method, since I thought of it thanks to one of the previous comments, but this is fine solution. Thanks again! :) – Javier García Manzano Dec 10 '15 at 13:50