patterncsharpMinor
Loading data into a number of different typed List<T>s: Can I do without reflection?
Viewed 0 times
cannumberwithoutreflectionintodifferenttypedloadinglistdata
Problem
I'm not sure whether my distaste for reflection is justified or not, but it feels ucky that I'm using reflection here.
I'm trying to make a very simple class which has a number of
The load method should:
The save method should:
Originally, I began writing code to loop through my
I'll attach my current completed class below. I don't mind doing
That's really bothering me. It just feels wrong and suboptimal and I'm sure I'm not thinking this through properly.
Full code below:
```
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Linq;
using Chess_Club.Models;
using Windows.Storage;
using System.Diagnostics;
namespace Chess_Club.DAL {
class ChessClubContext {
public List> Models = new List>();
public List Games = new List();
public List Members = new List();
p
I'm trying to make a very simple class which has a number of
List where T : Model with a load and save method.The load method should:
- For every list,
- Open or create
"Data/()/"
- Sequentially load each file in the folder into memory by creating a new instance of
Tand callingDeserialize(Text)
The save method should:
- For every list,
- For every instance,
- Call
Serialize(), and dump the result into"/Data/()/(ID)"
Originally, I began writing code to loop through my
Member list and my Game list and then realised I was going to be writing duplicate code for every list of data and switched to having a List> and looping that and ended up having to use reflection. I was wondering if anyone can think of a way of achieving my goals without using reflection or having duplicate code for every type of model stored.I'll attach my current completed class below. I don't mind doing
string modelClassName = models.GetType().GenericTypeArguments.First().Name; so much but it's the code:Type modelType = models.GetType().GenericTypeArguments.First();
dynamic list = (this.GetType().GetRuntimeProperty(modelClassName + "s").GetValue(this));
dynamic loaded = Activator.CreateInstance(modelType);
loaded.Deserialize("");
list.Add(loaded);That's really bothering me. It just feels wrong and suboptimal and I'm sure I'm not thinking this through properly.
Full code below:
```
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Linq;
using Chess_Club.Models;
using Windows.Storage;
using System.Diagnostics;
namespace Chess_Club.DAL {
class ChessClubContext {
public List> Models = new List>();
public List Games = new List();
public List Members = new List();
p
Solution
Reflection performance penalty would be negligible comparing to the serialization and IO cost, but you can surely work without reflection here.
In this code example, i'm demonstrating NET generics approach, which allows you to get specific type information statically (
You can take it a step further, replacing typeof(T).Name with an explicit collection folder name, this would require changing CreateRepository method signature slightly:
You can see the fix for async pattern usage. The correct signature is
Usage of public properties with setters for collections is dangerous, even if it works in provided example, because delegate accesses the newest assigned collection each time it's called.
This code example is not for a Windows Store app, but the approach still applies.
In this code example, i'm demonstrating NET generics approach, which allows you to get specific type information statically (
typeof(T)) and access members of a client class (ModelContext) with delegates, in place of reflection access to class members in your code.You can take it a step further, replacing typeof(T).Name with an explicit collection folder name, this would require changing CreateRepository method signature slightly:
modelRepositories.Add(CreateRepository(() => Games, "Games"));You can see the fix for async pattern usage. The correct signature is
async Task for a void method, so you could use await.Usage of public properties with setters for collections is dangerous, even if it works in provided example, because delegate accesses the newest assigned collection each time it's called.
This code example is not for a Windows Store app, but the approach still applies.
public class ModelContext
{
private readonly List modelRepositories = new List();
public List Games = new List();
public List Members = new List();
public ModelContext()
{
modelRepositories.Add(CreateRepository(() => Games));
modelRepositories.Add(CreateRepository(() => Members));
}
public async Task LoadData()
{
var rootFolderName = GetRootFolder();
foreach (var modelRepository in modelRepositories)
{
await modelRepository.Load(rootFolderName);
}
}
public async Task SaveData()
{
var rootFolderName = GetRootFolder();
foreach (var modelRepository in modelRepositories)
{
await modelRepository.Save(rootFolderName);
}
}
private static string GetRootFolder()
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "TestRepository");
}
private static IModelRepository CreateRepository(Func> modelProperty) where T : Model, new()
{
return new ModelRepository(modelProperty);
}
}
public interface IModelRepository
{
Task Save(string rootFolderName);
Task Load(string rootFolderName);
}
public class ModelRepository : IModelRepository where T: Model, new()
{
private readonly Func> modelProperty;
private readonly static string ModelName = typeof(T).Name;
public ModelRepository(Func> modelProperty)
{
this.modelProperty = modelProperty;
}
public async Task Save(string rootFolderName)
{
var modelFolderName = GetFolderName(rootFolderName);
if (!Directory.Exists(modelFolderName))
{
Directory.CreateDirectory(modelFolderName);
}
foreach (var model in modelProperty())
{
var modelFilePath = Path.Combine(modelFolderName, model.Id.ToString(CultureInfo.InvariantCulture));
var model1 = model;
await Task.Run(() => File.WriteAllText(modelFilePath, model1.Serialize()));
}
}
public async Task Load(string rootFolderName)
{
var modelFolderName = GetFolderName(rootFolderName);
foreach (var fileName in Directory.GetFiles(modelFolderName))
{
var fileName1 = fileName;
var readAllText = await Task.Run(()=>File.ReadAllText(fileName1));
LoadItem(readAllText);
}
}
private static string GetFolderName(string rootFolderName)
{
return Path.Combine(rootFolderName, ModelName + "s");
}
protected void LoadItem(string s)
{
var loaded = new T();
loaded.Deserialize(s);
modelProperty().Add(loaded);
}
}Code Snippets
modelRepositories.Add(CreateRepository(() => Games, "Games"));public class ModelContext
{
private readonly List<IModelRepository> modelRepositories = new List<IModelRepository>();
public List<Game> Games = new List<Game>();
public List<Member> Members = new List<Member>();
public ModelContext()
{
modelRepositories.Add(CreateRepository(() => Games));
modelRepositories.Add(CreateRepository(() => Members));
}
public async Task LoadData()
{
var rootFolderName = GetRootFolder();
foreach (var modelRepository in modelRepositories)
{
await modelRepository.Load(rootFolderName);
}
}
public async Task SaveData()
{
var rootFolderName = GetRootFolder();
foreach (var modelRepository in modelRepositories)
{
await modelRepository.Save(rootFolderName);
}
}
private static string GetRootFolder()
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "TestRepository");
}
private static IModelRepository CreateRepository<T>(Func<List<T>> modelProperty) where T : Model, new()
{
return new ModelRepository<T>(modelProperty);
}
}
public interface IModelRepository
{
Task Save(string rootFolderName);
Task Load(string rootFolderName);
}
public class ModelRepository<T> : IModelRepository where T: Model, new()
{
private readonly Func<List<T>> modelProperty;
private readonly static string ModelName = typeof(T).Name;
public ModelRepository(Func<List<T>> modelProperty)
{
this.modelProperty = modelProperty;
}
public async Task Save(string rootFolderName)
{
var modelFolderName = GetFolderName(rootFolderName);
if (!Directory.Exists(modelFolderName))
{
Directory.CreateDirectory(modelFolderName);
}
foreach (var model in modelProperty())
{
var modelFilePath = Path.Combine(modelFolderName, model.Id.ToString(CultureInfo.InvariantCulture));
var model1 = model;
await Task.Run(() => File.WriteAllText(modelFilePath, model1.Serialize()));
}
}
public async Task Load(string rootFolderName)
{
var modelFolderName = GetFolderName(rootFolderName);
foreach (var fileName in Directory.GetFiles(modelFolderName))
{
var fileName1 = fileName;
var readAllText = await Task.Run(()=>File.ReadAllText(fileName1));
LoadItem(readAllText);
}
}
private static string GetFolderName(string rootFolderName)
{
return Path.Combine(rootFolderName, ModelName + "s");
}
protected void LoadItem(string s)
{
var loaded = new T();
loaded.Deserialize(s);
modelProperty().Add(loaded);
}
}Context
StackExchange Code Review Q#62906, answer score: 4
Revisions (0)
No revisions yet.