Share via

Dynamically Resizing Models

Kmcnet 1,356 Reputation points
2026-05-12T23:13:19.0233333+00:00

Hello everyone and thanks for the help in advance. I'm trying to think through a solution for a calendar application. The model for the appointment table looks like:

        public class tblAppointments
    	{
        public int ID { get; set; }

        public string? PatientID { get; set; }

        public string? ProviderID { get; set; }

        public string? ProviderName { get; set; }

        public DateTime? ApptDateTime { get; set; }

        public DateTime? ScheduledDateTime { get; set; }

        public string? AppointmentDuration { get; set; }
	}

The data for each day has a variable number of providers (for purposes of display "Columns" i.e. Provider 1, Provider 2, etc.).

For example, appointments begin at 8:30 am and end at 5:00 pm. I need to loop through the appointment times at 20 minute increments. If there is a matching appointment, the appointment needs to be added to the appropriate column for the assigned provider.

I considered using a data table, but I realize this may not be the optimal method, but can't envision how to do it with a model. Any help would be appreciated.

Developer technologies | ASP.NET Core | Other
0 comments No comments

2 answers

Sort by: Most helpful
  1. Jack Dang (WICLOUD CORPORATION) 18,640 Reputation points Microsoft External Staff Moderator
    2026-05-13T04:08:41.3766667+00:00

    Hi @Kmcnet ,

    Thanks for reaching out.

    I would keep your appointment entity as-is and avoid trying to make it dynamically change shape. The entity should just represent the appointment record itself. For the calendar display, the cleaner approach is to build a separate view model that represents the day as a grid.

    That usually works well as a list of providers for the columns and a list of 20-minute time slots for the rows. Each row can hold a dictionary keyed by ProviderID, where the value is the appointment for that provider at that time, or null if that cell is empty.

    The code below is just meant as a reference pattern, so you may need to adjust the naming or structure a little to fit the way your own project is set up.

    Something like this:

    public class CalendarDayViewModel
    {
      public DateTime Date { get; set; }
      public List<ProviderColumnViewModel> Providers { get; set; } = new();
      public List<CalendarSlotViewModel> Slots { get; set; } = new();
    }
    
    public class ProviderColumnViewModel
    {
      public string ProviderId { get; set; } = string.Empty;
      public string ProviderName { get; set; } = string.Empty;
    }
    
    public class CalendarSlotViewModel
    {
      public DateTime SlotTime { get; set; }
      public Dictionary<string, tblAppointments?> AppointmentsByProvider { get; set; } = new();
    }
    

    Then you can build that view model from the appointments for the selected day:

    public CalendarDayViewModel BuildCalendarDay(
      DateTime date,
      IEnumerable<tblAppointments> appointments)
    {
      var slotLength = TimeSpan.FromMinutes(20);
      var start = date.Date.AddHours(8).AddMinutes(30);
      var end = date.Date.AddHours(17);
    
      var dayAppointments = appointments
        .Where(a => a.ApptDateTime.HasValue &&
              a.ApptDateTime.Value.Date == date.Date &&
              !string.IsNullOrWhiteSpace(a.ProviderID))
        .ToList();
    
      var providers = dayAppointments
        .GroupBy(a => new { a.ProviderID, a.ProviderName })
        .Select(g => new ProviderColumnViewModel
        {
          ProviderId = g.Key.ProviderID!,
          ProviderName = g.Key.ProviderName ?? g.Key.ProviderID!
        })
        .OrderBy(p => p.ProviderName)
        .ToList();
    
      var model = new CalendarDayViewModel
      {
        Date = date.Date,
        Providers = providers
      };
    
      var appointmentLookup = dayAppointments
        .Select(a => new
        {
          Appointment = a,
          SlotTime = GetSlotTime(a.ApptDateTime!.Value, start, slotLength)
        })
        .Where(x => x.SlotTime >= start && x.SlotTime < end)
        .GroupBy(x => new { x.Appointment.ProviderID, x.SlotTime })
        .ToDictionary(
          g => (ProviderId: g.Key.ProviderID!, SlotTime: g.Key.SlotTime),
          g => g.First().Appointment);
    
      for (var slotTime = start; slotTime < end; slotTime = slotTime.Add(slotLength))
      {
        var slot = new CalendarSlotViewModel
        {
          SlotTime = slotTime
        };
    
        foreach (var provider in providers)
        {
          appointmentLookup.TryGetValue((provider.ProviderId, slotTime), out var appointment);
          slot.AppointmentsByProvider[provider.ProviderId] = appointment;
        }
    
        model.Slots.Add(slot);
      }
    
      return model;
    }
    
    private static DateTime GetSlotTime(DateTime appointmentTime, DateTime start, TimeSpan slotLength)
    {
      if (appointmentTime <= start)
      {
        return start;
      }
    
      var minutesFromStart = (appointmentTime - start).TotalMinutes;
      var slotIndex = (int)Math.Floor(minutesFromStart / slotLength.TotalMinutes);
    
      return start.AddMinutes(slotIndex * slotLength.TotalMinutes);
    }
    

    Your Razor view can then loop through Model.Providers to create the columns and Model.Slots to create the rows:

    <table class="table">
      <thead>
        <tr>
          <th>Time</th>
          @foreach (var provider in Model.Providers)
          {
            <th>@provider.ProviderName</th>
          }
        </tr>
      </thead>
      <tbody>
        @foreach (var slot in Model.Slots)
        {
          <tr>
            <td>@slot.SlotTime.ToString("h:mm tt")</td>
    
            @foreach (var provider in Model.Providers)
            {
              var appointment = slot.AppointmentsByProvider[provider.ProviderId];
    
              <td>
                @if (appointment != null)
                {
                  <div>
                    Patient: @appointment.PatientID
    
                    Duration: @appointment.AppointmentDuration
                  </div>
                }
              </td>
            }
          </tr>
        }
      </tbody>
    </table>
    

    This way, the number of provider columns can change from day to day without requiring a different model or a DataTable. The providers determine the columns, and the generated time slots determine the rows.

    I adjusted the sample above to normalize appointment times into a 20-minute slot before matching them, which is a little safer than relying on an exact DateTime equality check. I would also consider storing AppointmentDuration as an int number of minutes, or possibly a TimeSpan, instead of a string, especially if you may later need appointments to span multiple slots.

    Hope this helps! If my answer was helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.

    Was this answer helpful?

    2 people found this answer helpful.

  2. AI answer

    2026-05-12T23:13:36.37+00:00

    Use the appointment model as the raw data source and build a view model that represents the calendar grid for a single day. The grid can be constructed dynamically from the appointments without changing the entity model.

    One approach:

    1. Define view models for the calendar:
    public class CalendarSlotViewModel
    {
        public DateTime Time { get; set; }
        public Dictionary<string, tblAppointments?> ProviderAppointments { get; set; } = new();
    }
    
    public class CalendarDayViewModel
    {
        public DateTime Date { get; set; }
        public List<string> ProviderIds { get; set; } = new();
        public List<CalendarSlotViewModel> Slots { get; set; } = new();
    }
    
    1. Build the calendar for a given day from the appointments:
    public CalendarDayViewModel BuildCalendarDay(
        DateTime date,
        IEnumerable<tblAppointments> appointments,
        TimeSpan startTime,
        TimeSpan endTime,
        TimeSpan slotLength)
    {
        // Filter appointments for the specific day
        var dayAppointments = appointments
            .Where(a => a.ApptDateTime.HasValue && a.ApptDateTime.Value.Date == date.Date)
            .ToList();
    
        // Determine distinct providers for that day
        var providerIds = dayAppointments
            .Select(a => a.ProviderID)
            .Where(id => !string.IsNullOrEmpty(id))
            .Distinct()
            .ToList();
    
        var model = new CalendarDayViewModel
        {
            Date = date.Date,
            ProviderIds = providerIds
        };
    
        // Build time slots
        var current = date.Date + startTime;
        var end = date.Date + endTime;
    
        while (current < end)
        {
            var slot = new CalendarSlotViewModel
            {
                Time = current
            };
    
            // Initialize dictionary with all providers
            foreach (var providerId in providerIds)
            {
                slot.ProviderAppointments[providerId] = null;
            }
    
            // Fill appointments for this time slot
            foreach (var appt in dayAppointments)
            {
                if (!appt.ApptDateTime.HasValue || string.IsNullOrEmpty(appt.ProviderID))
                    continue;
    
                if (appt.ApptDateTime.Value == current)
                {
                    slot.ProviderAppointments[appt.ProviderID] = appt;
                }
            }
    
            model.Slots.Add(slot);
            current = current.Add(slotLength);
        }
    
        return model;
    }
    

    Usage example:

    var calendar = BuildCalendarDay(
        date: new DateTime(2026, 5, 12),
        appointments: allAppointmentsForClinic,
        startTime: TimeSpan.FromHours(8.5),   // 8:30
        endTime: TimeSpan.FromHours(17),      // 17:00
        slotLength: TimeSpan.FromMinutes(20));
    

    In the view, iterate over calendar.Slots for rows (time slots) and calendar.ProviderIds for columns (providers). For each cell, look up slot.ProviderAppointments[providerId] to see whether there is an appointment.

    This keeps the persistence model (tblAppointments) simple and uses a separate view model to dynamically represent a variable number of providers and time slots.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

    1 person found this answer helpful.

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.