In the previous post, grouped data was merged into one record with the generation of a CSV list of of the property values on which merged the records differ. A Derived Class was used for the view that adds a string property that is that CSV list. The question is how to generate the instance of the derived class when you have the instance of the base class and the value of the additional property.

Recap

As discussed in previous posts we have developed a Club Blazor Server app that includes the life cycle management of an club’s athletic records. Records are claimed by athletes. An admin manages verifying and accepting claims. The latest extension is the ability to list all records set by an athlete in any one season as a certificate with similar graphics and saved as a PDF which can then be downloaded by the athlete. The previous post discussed the generation of a certificate where an athlete has achieved multiple records, each of which has been achieved over multiple age groups and therefore can be merged into one entry in the table. In this case the total number of records is >25 and doesn’t fit on one page

An Example

… becomes:

The MultiAgeRecord Class

In the previous post the MultiAgeRecord class was used in the following grouping action:

        var results2 = recordsSorted.GroupBy(
        s => ((DateTime)s.Date).Ticks,
        (key, s) => new MultiAgeRecord
        {
            DateId = key,
            EventId = eventStr,
            Results = (s.OrderBy(f => f.AgeGroupView)).ToList()
        }).ToList();

This enabled explicit reference to the grouping data in code that followed. The class is defined with properties to match the assignments in this grouping. Of interest is the read-only generation of the Ages CSV string from the Results list:

    public class MultiAgeRecord
    {
        public string EventId{ get; set; }
        public long DateId { get; set; }
        public DateTime Date
        {
            get
            {
                return new DateTime(DateId);
            }
        }
        public List<RecordResult> Results { get; set; }

        public string Ages
        {
            get
            {
                return string.Join(",", Results.Select(x => x.AgeGroupView));
            }
        }
    }

The Derived Class Instance

Discussion

Whilst it is noted that it isn’t possible, a useful capability for derived classes is:

  • Where an additional property is added, with a default value.
  • Ability to have an implicit constructor which takes the base instance (and optionally the added property value).

For example as per the previous post:

RecordResultAges agesRecord = new RecordResultAges(firstMultiAgeRecord, multiAgeRecord.Ages);

The base class is RecordResult. The derived class is RecordResultAges.

The solution used here is to implement such a constructor using Reflection:

Code

using System.Reflection
    ...

    public class RecordResultAges: RecordResult
    {
        public RecordResultAges() { }

        public RecordResultAges(RecordResult res, string ages)
        {
            Ages = ages;

            string[] propertyNames = res.GetType().GetProperties().Select(p => p.Name).ToArray();
            foreach (var prop in propertyNames)
            {
                if (prop != "Id")
                {
                    try
                    {
                        object propValue = res.GetType().GetProperty(prop).GetValue(res, null);
                        this.GetType().GetProperty(prop).SetValue(this, propValue);
                    }
                    catch(Exception ex) { }
                }
            }
        }
        public string Ages { get; set; } = "";
    }

When this constructor is called:

  • The (additional) property value is assigned.
  • The property names of the base instance type are collected.
  • For each of these (if not the Id)
    • Get the base instance property value
    • Assign it to the derived instance.

Comment

Note that the derived instance only makes a copy of the base properties. If the derived data was by reference, that would be a different question. By value is only needed here as it is for display only. Also, the derived class does not add any methods or overrides.

Another approach to this is instead of Reflection, use Newtonsoft.Json to serialize the base object into a Json string then deserialize that as a derived class instance.

Footnote

Some polymorphism.

The desired coding pattern here is:

DerivedType derivedInstance = new DerivedType(baseInstance);

… with the additional property assuming the default value or a null if not specified.

Or

DerivedType derivedInstance = new DerivedType(baseInstance, value);

An alternative desired coding pattern would be:

DerivedType derivedInstance = (DerivedType) baseInstance;

… with the additional property assuming the default value or a null if not specified.

The reverse IS true:

BaseType baseInstance = (BaseType) derivedInstance;

… with the extra derived class property being discarded.


 TopicSubtopic
   
 This Category Links 
Category:Web Sites Index:Web Sites
  Next: > Entity Framework
<  Prev:   Club Record Certificate