C
C#2y ago
Zil

Accidently closed last post. Need help with data structure(Noob).

Hello this is my function
c++
public async Task<IEnumerable<StepModel>> GetSteps(int workflowKey, string databaseName)
{
var steps = await _db.LoadData<dynamic, dynamic>(
"SQL QUERY"
, new { });

var mergedSteps = steps
.GroupBy(s => s.Key)
.Select(g => new StepModel
{
// Not relevant
Parameters = g.Select(p => new ParametersModel
{
Name = p.Name,
Source = p.Source,
Value = p.Value
}).ToList()
});

return mergedSteps;
}

}
c++
public async Task<IEnumerable<StepModel>> GetSteps(int workflowKey, string databaseName)
{
var steps = await _db.LoadData<dynamic, dynamic>(
"SQL QUERY"
, new { });

var mergedSteps = steps
.GroupBy(s => s.Key)
.Select(g => new StepModel
{
// Not relevant
Parameters = g.Select(p => new ParametersModel
{
Name = p.Name,
Source = p.Source,
Value = p.Value
}).ToList()
});

return mergedSteps;
}

}
Model:
c++
{
public class StepModel
{
// non relevant stuff
public List<ParametersModel> Parameters { get; set; }

}

public class ParametersModel
{
public string Name { get; set; }
public string Source { get; set; }
public string Value { get; set; }
}
}
c++
{
public class StepModel
{
// non relevant stuff
public List<ParametersModel> Parameters { get; set; }

}

public class ParametersModel
{
public string Name { get; set; }
public string Source { get; set; }
public string Value { get; set; }
}
}
The focus here is on the parameters of a stepmodel. Current return value of merged steps is something like this(example):
"parameters": [
{
"name": "namevalue1",
"source": "sourcevalue1",
"value": "valuevalue1"
},
{
"name": "namevalue2",
"source": "sourcevalue2",
"value": "valuevalue2"
},
{
"name": "namevalue3",
"source": "sourcevalue3",
"value": "valuevalue3"
},
"parameters": [
{
"name": "namevalue1",
"source": "sourcevalue1",
"value": "valuevalue1"
},
{
"name": "namevalue2",
"source": "sourcevalue2",
"value": "valuevalue2"
},
{
"name": "namevalue3",
"source": "sourcevalue3",
"value": "valuevalue3"
},
What i want is:
"parameters": {
"namevalue1": {
"source": "sourcevalue1",
"value": "valuevalue1"
},
"namevalue2": {
"source": "sourcevalue2",
"value": "valuevalue2"
},
"namevalue3:" {
"source": "sourcevalue3",
"value": "valuevalue3"
},
}
What i want is:
"parameters": {
"namevalue1": {
"source": "sourcevalue1",
"value": "valuevalue1"
},
"namevalue2": {
"source": "sourcevalue2",
"value": "valuevalue2"
},
"namevalue3:" {
"source": "sourcevalue3",
"value": "valuevalue3"
},
}
Previous help consists of changing my Model to a Record. Unfortunately i cant do this because of constraints from PO. Thanks in advance!
35 Replies
Thinker
Thinker2y ago
Why are you using dynamic catlost
Zil
ZilOP2y ago
I used a tim correy tutorial that helped me set up a minimal API. https://www.youtube.com/watch?v=dwMFg6uxQ0I minute 58
Angius
Angius2y ago
What does LoadData do that'd require the use of dynamic? As a generic parameter no less
Zil
ZilOP2y ago
This is LoadData
c++
{
private readonly IConfiguration _config;

public SqlDataAccess(IConfiguration config)
{
_config = config;
}
public async Task<IEnumerable<T>> LoadData<T, U>(
string sqlQuery,
U parameters,
string connectionId = "Default")
{
using IDbConnection connection = new SqlConnection(_config.GetConnectionString(connectionId));

return await connection.QueryAsync<T>(sqlQuery, parameters,
commandType: CommandType.Text);
}

public async Task SaveData<T>(
string sqlQuery,
T parameters,
string connectionId = "Default")
{
using IDbConnection connection = new SqlConnection(_config.GetConnectionString(connectionId));

await connection.ExecuteAsync(sqlQuery, parameters,
commandType: CommandType.Text);

}
}
c++
{
private readonly IConfiguration _config;

public SqlDataAccess(IConfiguration config)
{
_config = config;
}
public async Task<IEnumerable<T>> LoadData<T, U>(
string sqlQuery,
U parameters,
string connectionId = "Default")
{
using IDbConnection connection = new SqlConnection(_config.GetConnectionString(connectionId));

return await connection.QueryAsync<T>(sqlQuery, parameters,
commandType: CommandType.Text);
}

public async Task SaveData<T>(
string sqlQuery,
T parameters,
string connectionId = "Default")
{
using IDbConnection connection = new SqlConnection(_config.GetConnectionString(connectionId));

await connection.ExecuteAsync(sqlQuery, parameters,
commandType: CommandType.Text);

}
}
Angius
Angius2y ago
Ah, so it's some homebrew ORM thing I can see U being dynamic, although it should probably just be whatever it is in QueryAsync() T should never be dynamic or object, though
Zil
ZilOP2y ago
I see, so what would you advise doing ?
Angius
Angius2y ago
Making a class that represents your data And using that as T
Zil
ZilOP2y ago
aah yes okay, but what i dont understand is how this helps the initial problem that i had. I feel like changing the structure from what I have now to what I want isnt related to changing dynamic to a type of class ... but then again im a total noob in C# and maybe im just thinking too much in a JS way?
Angius
Angius2y ago
Well, the SQL query will return the data in a specific shape Right now, it returns data in the former shape So you'll need to rewrite the query to return the latter Or you'll need to change the shape of data after fetching it For example, some
var things = stuff.ToDictionary(s => s.Name, s = new Thing {
Source = s.Source,
Value = s.Value
});
var things = stuff.ToDictionary(s => s.Name, s = new Thing {
Source = s.Source,
Value = s.Value
});
Zil
ZilOP2y ago
Yea rewriting the sql query isnt an option to my knowledge. This is because it is fetching data from 2 tables and combining them. A step table that contains steps and a parameters table that contains the parameters belonging to a step. the SQL result will contain the steps information but duplicate because it can have multiple parameters. I then try to combine the data where the steps are unique(so the duplicated info is just 1 step) with the parameters as an property of that object. This all works fine btw, its just the structure that im trying to change.
Angius
Angius2y ago
That's why I don't do SQL lol
Zil
ZilOP2y ago
yea fml So i am changing the data after the query sucesfully. I just dont want it as an Array but i want it as:
"parameters": {
"namevalue1": {
"source": "sourcevalue1",
"value": "valuevalue1"
},
"namevalue2": {
"source": "sourcevalue2",
"value": "valuevalue2"
},
"namevalue3:" {
"source": "sourcevalue3",
"value": "valuevalue3"
},
}
"parameters": {
"namevalue1": {
"source": "sourcevalue1",
"value": "valuevalue1"
},
"namevalue2": {
"source": "sourcevalue2",
"value": "valuevalue2"
},
"namevalue3:" {
"source": "sourcevalue3",
"value": "valuevalue3"
},
}
MODiX
MODiX2y ago
angius#0000
For example, some
var things = stuff.ToDictionary(s => s.Name, s = new Thing {
Source = s.Source,
Value = s.Value
});
var things = stuff.ToDictionary(s => s.Name, s = new Thing {
Source = s.Source,
Value = s.Value
});
React with ❌ to remove this embed.
Zil
ZilOP2y ago
Okay so with dictionary I almost have what i want, next problem is this
c++
public async Task<IEnumerable<StepModel>> GetSteps(int workflowKey, string databaseName)
{
var steps = await _db.LoadData<dynamic, dynamic>(
"SELECT ws.[Key], ws.[Type], ws.[ParentKey], ws.[SubWorkflowCode], ws.[Sequence], ws.[WorkflowKey], wsp.[Name], wsp.[Source], wsp.[Value]\r\n" +
$"FROM [{databaseName}].[dbo].[TblWorkflowStep] ws\r\n" +
$"JOIN [{databaseName}].[dbo].[TblWorkflowStepParameter] wsp\r\n" +
$"ON wsp.[WorkflowStepKey] = ws.[Key]\r\n" +
$"WHERE [WorkflowKey] = {workflowKey}"
, new { });

var mergedSteps = steps
.GroupBy(s => s.Key)
.Select(g => new StepModel
{
Key = g.Key,
Type = g.First().Type,
ParentKey = g.First().ParentKey ?? 0,
SubWorkflowCode = g.First().SubWorkflowCode ?? string.Empty,
Sequence = g.First().Sequence,
WorkflowKey = g.First().WorkflowKey,
Parameters = g.Select(p => new ParametersModel
{
Name = p.Name, // Remove this one
Source = p.Source,
Value = p.Value
}).ToDictionary(p => p.Name) // Convert the list to a dictionary using the parameter name as the key
});


return mergedSteps;
}
c++
public async Task<IEnumerable<StepModel>> GetSteps(int workflowKey, string databaseName)
{
var steps = await _db.LoadData<dynamic, dynamic>(
"SELECT ws.[Key], ws.[Type], ws.[ParentKey], ws.[SubWorkflowCode], ws.[Sequence], ws.[WorkflowKey], wsp.[Name], wsp.[Source], wsp.[Value]\r\n" +
$"FROM [{databaseName}].[dbo].[TblWorkflowStep] ws\r\n" +
$"JOIN [{databaseName}].[dbo].[TblWorkflowStepParameter] wsp\r\n" +
$"ON wsp.[WorkflowStepKey] = ws.[Key]\r\n" +
$"WHERE [WorkflowKey] = {workflowKey}"
, new { });

var mergedSteps = steps
.GroupBy(s => s.Key)
.Select(g => new StepModel
{
Key = g.Key,
Type = g.First().Type,
ParentKey = g.First().ParentKey ?? 0,
SubWorkflowCode = g.First().SubWorkflowCode ?? string.Empty,
Sequence = g.First().Sequence,
WorkflowKey = g.First().WorkflowKey,
Parameters = g.Select(p => new ParametersModel
{
Name = p.Name, // Remove this one
Source = p.Source,
Value = p.Value
}).ToDictionary(p => p.Name) // Convert the list to a dictionary using the parameter name as the key
});


return mergedSteps;
}
This works, but I want to remove the name inside of the parametersModel(because its already used as the property key, exactly what i want) but if i remove it from the model it obviously doesnt know name anymore in the above code. Any idea how i could resolve that issue ?
Angius
Angius2y ago
Instead of
Parameters = g.Select(p => new ParametersModel
{
Name = p.Name,
Source = p.Source,
Value = p.Value
}).ToDictionary(p => p.Name)
Parameters = g.Select(p => new ParametersModel
{
Name = p.Name,
Source = p.Source,
Value = p.Value
}).ToDictionary(p => p.Name)
use
Parameters = g.ToDictionary(p => p.Name, p => new Thing
{
Source = p.Source,
Value = p.Value
})
Parameters = g.ToDictionary(p => p.Name, p => new Thing
{
Source = p.Source,
Value = p.Value
})
where Thing has no Name...?
Zil
ZilOP2y ago
Yea atm thing doesnt exists, i need to make a new model for that aswell?
Angius
Angius2y ago
Yes
Zil
ZilOP2y ago
but then i would need to edit the parameters model
c++
namespace DataAccess.Models
{
public class StepModel
{
public int Key { get; set; }
public string Type { get; set; }
public int ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public Dictionary<string, ParametersModel> Parameters { get; set; }

}

public class Thing
{
public string Source { get; set; }
public string Value { get; set; }
}

public class ParametersModel
{
public Thing name { get; set; }
}
}
c++
namespace DataAccess.Models
{
public class StepModel
{
public int Key { get; set; }
public string Type { get; set; }
public int ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public Dictionary<string, ParametersModel> Parameters { get; set; }

}

public class Thing
{
public string Source { get; set; }
public string Value { get; set; }
}

public class ParametersModel
{
public Thing name { get; set; }
}
}
to something like this right ?
Angius
Angius2y ago
Or just remove Name from ParametersModel And use that
Zil
ZilOP2y ago
This i dont follow, you mean like this?
c++
{
public class StepModel
{
public int Key { get; set; }
public string Type { get; set; }
public int ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public Dictionary<string, ParametersModel> Parameters { get; set; }

}

public class Thing
{
public string Source { get; set; }
public string Value { get; set; }

}

public class ParametersModel
{
public string Source { get; set; }
public string Value { get; set; }
}
}
c++
{
public class StepModel
{
public int Key { get; set; }
public string Type { get; set; }
public int ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public Dictionary<string, ParametersModel> Parameters { get; set; }

}

public class Thing
{
public string Source { get; set; }
public string Value { get; set; }

}

public class ParametersModel
{
public string Source { get; set; }
public string Value { get; set; }
}
}
No you cant mean that, that doesnt make sense
Angius
Angius2y ago
Just remove Thing
{
public class StepModel
{
public int Key { get; set; }
public string Type { get; set; }
public int ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public Dictionary<string, ParametersModel> Parameters { get; set; }

}

public class ParametersModel
{
public string Source { get; set; }
public string Value { get; set; }
}
}
{
public class StepModel
{
public int Key { get; set; }
public string Type { get; set; }
public int ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public Dictionary<string, ParametersModel> Parameters { get; set; }

}

public class ParametersModel
{
public string Source { get; set; }
public string Value { get; set; }
}
}
Parameters = g.ToDictionary(p => p.Name, p => new ParametersModel
{
Source = p.Source,
Value = p.Value
})
Parameters = g.ToDictionary(p => p.Name, p => new ParametersModel
{
Source = p.Source,
Value = p.Value
})
Zil
ZilOP2y ago
yea i tried that, but that gives me this error.
Zil
ZilOP2y ago
@ZZZZZZZZZZZZZZZZZZZZZZZZZ I feel like we just made full circle to the Dynamic issue
Angius
Angius2y ago
This is the issue, yes
Zil
ZilOP2y ago
So i need to make apart from the model also a class that has the same structure and pass that instead of dynamic ?
Angius
Angius2y ago
Yes Instead of the first dynamic, you'll probably have something like List<SomeClass> SomeClass being a class that describes the shape of data you get from the SQL query
Zil
ZilOP2y ago
Right, but that is precisely what the Model is for right ? It feels duplicate to have a model aswell as the class that basicly represent the same data
Angius
Angius2y ago
The database model and the model for the outward-facing data can — and somethimes should — be different
Zil
ZilOP2y ago
yea that makes sense
Zil
ZilOP2y ago
Okay i created the class:
c++
namespace DataAccess.Models
{
public class Thing
{
public int Key { get; set; }
public string Type { get; set; }
public int? ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public string Name { get; set; }
public string Source { get; set; }
public string Value { get; set; }
}
}
c++
namespace DataAccess.Models
{
public class Thing
{
public int Key { get; set; }
public string Type { get; set; }
public int? ParentKey { get; set; }
public string SubWorkflowCode { get; set; }
public int Sequence { get; set; }
public int WorkflowKey { get; set; }
public string Name { get; set; }
public string Source { get; set; }
public string Value { get; set; }
}
}
With this code we talked about before
c++
public async Task<IEnumerable<StepModel>> GetSteps(int workflowKey, string databaseName)
{
var steps = await _db.LoadData<List<Thing>, dynamic>(
"SELECT ws.[Key], ws.[Type], ws.[ParentKey], ws.[SubWorkflowCode], ws.[Sequence], ws.[WorkflowKey], wsp.[Name], wsp.[Source], wsp.[Value]\r\n" +
$"FROM [{databaseName}].[dbo].[TblWorkflowStep] ws\r\n" +
$"JOIN [{databaseName}].[dbo].[TblWorkflowStepParameter] wsp\r\n" +
$"ON wsp.[WorkflowStepKey] = ws.[Key]\r\n" +
$"WHERE [WorkflowKey] = {workflowKey}"
, new { });



var mergedSteps = steps
.GroupBy(s => s.Key)
.Select(g => new StepModel
{
Key = g.Key,
Type = g.First().Type,
ParentKey = g.First().ParentKey ?? 0,
SubWorkflowCode = g.First().SubWorkflowCode ?? string.Empty,
Sequence = g.First().Sequence,
WorkflowKey = g.First().WorkflowKey,
Parameters = g.ToDictionary(p => p.Name, p => new ParametersModel
{
Source = p.Source,
Value = p.Value
})
});


return mergedSteps;
}
c++
public async Task<IEnumerable<StepModel>> GetSteps(int workflowKey, string databaseName)
{
var steps = await _db.LoadData<List<Thing>, dynamic>(
"SELECT ws.[Key], ws.[Type], ws.[ParentKey], ws.[SubWorkflowCode], ws.[Sequence], ws.[WorkflowKey], wsp.[Name], wsp.[Source], wsp.[Value]\r\n" +
$"FROM [{databaseName}].[dbo].[TblWorkflowStep] ws\r\n" +
$"JOIN [{databaseName}].[dbo].[TblWorkflowStepParameter] wsp\r\n" +
$"ON wsp.[WorkflowStepKey] = ws.[Key]\r\n" +
$"WHERE [WorkflowKey] = {workflowKey}"
, new { });



var mergedSteps = steps
.GroupBy(s => s.Key)
.Select(g => new StepModel
{
Key = g.Key,
Type = g.First().Type,
ParentKey = g.First().ParentKey ?? 0,
SubWorkflowCode = g.First().SubWorkflowCode ?? string.Empty,
Sequence = g.First().Sequence,
WorkflowKey = g.First().WorkflowKey,
Parameters = g.ToDictionary(p => p.Name, p => new ParametersModel
{
Source = p.Source,
Value = p.Value
})
});


return mergedSteps;
}
But it gives me an error on Key and on Value. You might now why this is ?
Angius
Angius2y ago
Ah, maybe LoadData retuens a collection of Ts by default? Try just Thing instead of List<Thing>
Zil
ZilOP2y ago
That is it! thank you so much
Angius
Angius2y ago
Anytime
Zil
ZilOP2y ago
Ill close the post, happy coding!

Did you find this page helpful?