Don’t load data you dont really need.

In my previous posts, we discussed my load method and how to implement a picker resulting in the following method:


private async Task LoadData()
{
if (_isDataLoaded)
return;
ModelExercises.Clear();
_isDataLoaded = true;

var modelExercises = await _workoutStore.GetModelExercisesAsync();

foreach (var modelExercise in modelExercises)
{
ModelExercises.Add(await
ModelExerciseViewModel.CreateAsync(modelExercise, _workoutStore)
);

}

var muscles = await _workoutStore.GetMusclesAsync();

foreach (var muscle in muscles)
{
Muscles.Add(muscle);
}

if (SelectedMuscle != null)
{
var selectedMuscle = from m in Muscles
where m.Name == SelectedMuscle
select m;
Muscles.Clear();
Muscles.Add(selectedMuscle.SingleOrDefault());
}

foreach (var muscle in Muscles)
{
var currentMuscle = new ExerciseGroup(muscle.Name, muscle.Name);

var foundMuscles = from e in ModelExercises
where e.PrimaryMuscles.Contains(muscle, _compareMuscle)
select e;
currentMuscle.AddRange(foundMuscles);
GroupedExercises.Add(currentMuscle);
}

}

The primary problem with this approach is in:


foreach (var modelExercise in modelExercises)
{
ModelExercises.Add(await
ModelExerciseViewModel.CreateAsync(modelExercise, _workoutStore)
);

}

Which triggers the static async method (which replaces the constructor which itself cannot be async):


public static async Task<ModelExerciseViewModel> CreateAsync(ModelExercise exercise, IWorkoutStore workoutStore)
{
_workoutStore = workoutStore;

ModelExerciseViewModel exerciseViewModel = new ModelExerciseViewModel(exercise);
exerciseViewModel.PrimaryMuscles = await GetMuscles("Primary");
exerciseViewModel.SecondaryMuscles = await GetMuscles("Secondary");
return exerciseViewModel;
}

It works… but it is super slow, on my Galaxy A52016 it took ~6sec to generate the original list and ~2sec for each filtering. Unacceptable.

Then it dawned on me… Why do I generate objects I don’t need at this time?

The original logic was: I am going to create all the objects and then use the lists of muscles I build for them for filtering.

What seemed like a good plan was loading information I did not need into the application when I have a perfectly good simple object:


public class MuscleExerciseAssosiation
{
public int MuscleId { get; set; }
public int ModelExerciseId { get; set; }
public string MuslceType { get; set; }
}

In my database based on which I can use for filtering:


public async Task<IEnumerable<MuscleExerciseAssosiation>> GetMuscleExerciseAssosiations(string muscleType, int? muscleID = null)
{
List<MuscleExerciseAssosiation> assosiations = new List<MuscleExerciseAssosiation>();
if (muscleID == null)
{
assosiations = await _connection.Table<MuscleExerciseAssosiation>()
.Where(e=>e.MuslceType == muscleType).ToListAsync();
}
else
{
assosiations = await _connection.Table<MuscleExerciseAssosiation>()
.Where(e => e.MuscleId == muscleID && e.MuslceType == muscleType).ToListAsync();
}

return assosiations;
}

Now when I can pull at will either a filtered or full list of exercises I need I can simply:


if (SelectedMuscle != null)
{
var assosiatedExercises = await _workoutStore.GetMuscleExerciseAssosiations("Primary", SelectedMuscle.Id);
foreach (var assosiation in assosiatedExercises)
{
ModelExercises.Add(modelExercises.Where(e => e.Id == assosiation.ModelExerciseId).SingleOrDefault());
}
}
else
{
muscles = await _workoutStore.GetMusclesAsync();

foreach (var muscle in muscles)
{
Muscles.Add(muscle);
}
foreach (var exercise in modelExercises)
{
ModelExercises.Add(exercise);
}

}

And thats it. Perfomance to being unoticable for both the initial load and filtering, so I am preatty proud of myself 🙂

The full new ViewModel can be found on github.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s