Updating properties in a IEnumerable
-
I have an IEnumerable that I need to update some fields based on the user's security permissions. If a user is in the Admin or HR roles, the CanEdit and CanDelete properties should be set to true. If the user is not Admin or HR, but is their own record, CanEdit should be true. I have come up with two options, and wanted to know which would be considered the better method (or a different way) for speed, readability, and maintainability. Method 1 first checks if the user is Admin or HR, then loops through the entire collection and updates the values. If the user is not Admin/HR, then it checks each record to see if it is the users and updates. Currently, a user can only have one record, so I quit once I update the matching employee record.
// Admin and HR are allowed to edit and delete all records
if(_userManager.User.IsInRole("Admin") || _userManager.User.IsInRole("HR"))
{
foreach(var employeeRecord in employeeList)
{
employeeRecord.CanEdit = employeeRecord.CanDelete = true;
}
return employeeList;
}// Users are allowed to edit their own record.
foreach(var employeeRecord in employeeList)
{
if(employeeRecord.WorkEmail.Equals(_userManager.User.Identity.Name, StringComparison.OrdinalIgnoreCase))
{
employeeRecord.CanEdit = true;
return employeeList;
}
}// This return is needed in case there is no user record found.
return employeeList;Method 2 stores a boolean if the user is an Admin/HR, and just sets that as the permission, then checks if the current employeeRecord belongs to the user and sets the permission.
bool hasEditDeletePermission = _userManager.User.IsInRole("Admin") || _userManager.User.IsInRole("HR");
foreach(var employeeRecord in employeeList)
{
employeeRecord.CanEdit = employeeRecord.CanDelete = hasEditDeletePermission;if(\_userManager.User.Identity.Name.Equals(employeeRecord.WorkEmail, StringComparison.OrdinalIgnoreCase)) { // Users can edit their own record employeeRecord.CanEdit = true; }
}
return employeeList;
Method 2 seems shorter and easier to understand, but Method 1 can be faster when the user isn't Admin or HR, which would happen more often. There's also the issue that Method 1 leaves the CanEdit/CanDelete values to whatever they were originally supplied as (currently defaults to false), where as Method 2 will always override any preexisting values which might cause unexpected behav
-
I have an IEnumerable that I need to update some fields based on the user's security permissions. If a user is in the Admin or HR roles, the CanEdit and CanDelete properties should be set to true. If the user is not Admin or HR, but is their own record, CanEdit should be true. I have come up with two options, and wanted to know which would be considered the better method (or a different way) for speed, readability, and maintainability. Method 1 first checks if the user is Admin or HR, then loops through the entire collection and updates the values. If the user is not Admin/HR, then it checks each record to see if it is the users and updates. Currently, a user can only have one record, so I quit once I update the matching employee record.
// Admin and HR are allowed to edit and delete all records
if(_userManager.User.IsInRole("Admin") || _userManager.User.IsInRole("HR"))
{
foreach(var employeeRecord in employeeList)
{
employeeRecord.CanEdit = employeeRecord.CanDelete = true;
}
return employeeList;
}// Users are allowed to edit their own record.
foreach(var employeeRecord in employeeList)
{
if(employeeRecord.WorkEmail.Equals(_userManager.User.Identity.Name, StringComparison.OrdinalIgnoreCase))
{
employeeRecord.CanEdit = true;
return employeeList;
}
}// This return is needed in case there is no user record found.
return employeeList;Method 2 stores a boolean if the user is an Admin/HR, and just sets that as the permission, then checks if the current employeeRecord belongs to the user and sets the permission.
bool hasEditDeletePermission = _userManager.User.IsInRole("Admin") || _userManager.User.IsInRole("HR");
foreach(var employeeRecord in employeeList)
{
employeeRecord.CanEdit = employeeRecord.CanDelete = hasEditDeletePermission;if(\_userManager.User.Identity.Name.Equals(employeeRecord.WorkEmail, StringComparison.OrdinalIgnoreCase)) { // Users can edit their own record employeeRecord.CanEdit = true; }
}
return employeeList;
Method 2 seems shorter and easier to understand, but Method 1 can be faster when the user isn't Admin or HR, which would happen more often. There's also the issue that Method 1 leaves the CanEdit/CanDelete values to whatever they were originally supplied as (currently defaults to false), where as Method 2 will always override any preexisting values which might cause unexpected behav
If the user was limited to certain records in the first place, that particular process should have been "data driven" with a proper query. Instead, you've got "generic" process that attempts to apply some logic to "every record" from who knows where. The "data driver" could be data records, references or simply keys. Another design decision. What you're dealing with is affectionately known as "tramp data".
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
-
If the user was limited to certain records in the first place, that particular process should have been "data driven" with a proper query. Instead, you've got "generic" process that attempts to apply some logic to "every record" from who knows where. The "data driver" could be data records, references or simply keys. Another design decision. What you're dealing with is affectionately known as "tramp data".
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
Could you explain a bit more what you mean by 'data driven with a proper query'? Almost every tutorial/example I've found only seems to be basic "Can Access/Can Not Access", but my needs are more granular. I'm currently using CQRS to have my Controller send an EmployeeQuery request. I've added a pipeline to the request handler which has my security logic and updates the results from the query as I need to apply information not just to the records themselves, but also potentially individual properties of the record. I didn't want to mix my security logic in with my query as I thought that could get hard to expand in the future. Here's an example of the current rules/permissions: - Admin : Full Create, Read, Update, (Soft/Hard) Delete. - HR : Full Create, Read, Update, Soft Delete. - Manager : Full Read (of specific columns), Update of their own record (excluding some columns). - User : Full Read (of specific columns different than Manager), Update of their own record (excluding some columns). I'm using EFCore and LINQ for the query and I had first thought about trying to have the security logic in the pipeline 'build' the IQueryable that will get executed by the query handler, but I haven't found a good resource on how to do that, so I am updating the results of the handled query before sending it back to the Controller.
-
I have an IEnumerable that I need to update some fields based on the user's security permissions. If a user is in the Admin or HR roles, the CanEdit and CanDelete properties should be set to true. If the user is not Admin or HR, but is their own record, CanEdit should be true. I have come up with two options, and wanted to know which would be considered the better method (or a different way) for speed, readability, and maintainability. Method 1 first checks if the user is Admin or HR, then loops through the entire collection and updates the values. If the user is not Admin/HR, then it checks each record to see if it is the users and updates. Currently, a user can only have one record, so I quit once I update the matching employee record.
// Admin and HR are allowed to edit and delete all records
if(_userManager.User.IsInRole("Admin") || _userManager.User.IsInRole("HR"))
{
foreach(var employeeRecord in employeeList)
{
employeeRecord.CanEdit = employeeRecord.CanDelete = true;
}
return employeeList;
}// Users are allowed to edit their own record.
foreach(var employeeRecord in employeeList)
{
if(employeeRecord.WorkEmail.Equals(_userManager.User.Identity.Name, StringComparison.OrdinalIgnoreCase))
{
employeeRecord.CanEdit = true;
return employeeList;
}
}// This return is needed in case there is no user record found.
return employeeList;Method 2 stores a boolean if the user is an Admin/HR, and just sets that as the permission, then checks if the current employeeRecord belongs to the user and sets the permission.
bool hasEditDeletePermission = _userManager.User.IsInRole("Admin") || _userManager.User.IsInRole("HR");
foreach(var employeeRecord in employeeList)
{
employeeRecord.CanEdit = employeeRecord.CanDelete = hasEditDeletePermission;if(\_userManager.User.Identity.Name.Equals(employeeRecord.WorkEmail, StringComparison.OrdinalIgnoreCase)) { // Users can edit their own record employeeRecord.CanEdit = true; }
}
return employeeList;
Method 2 seems shorter and easier to understand, but Method 1 can be faster when the user isn't Admin or HR, which would happen more often. There's also the issue that Method 1 leaves the CanEdit/CanDelete values to whatever they were originally supplied as (currently defaults to false), where as Method 2 will always override any preexisting values which might cause unexpected behav
Have an object - user details that stores the info on the current user and a list of permissions. This should be populated when the user logs on. Each page checks the user object for permissions and set 1 or more flags that control the state of the form controls and the content of any lists for the form.
Never underestimate the power of human stupidity - RAH I'm old. I know stuff - JSOP
-
Could you explain a bit more what you mean by 'data driven with a proper query'? Almost every tutorial/example I've found only seems to be basic "Can Access/Can Not Access", but my needs are more granular. I'm currently using CQRS to have my Controller send an EmployeeQuery request. I've added a pipeline to the request handler which has my security logic and updates the results from the query as I need to apply information not just to the records themselves, but also potentially individual properties of the record. I didn't want to mix my security logic in with my query as I thought that could get hard to expand in the future. Here's an example of the current rules/permissions: - Admin : Full Create, Read, Update, (Soft/Hard) Delete. - HR : Full Create, Read, Update, Soft Delete. - Manager : Full Read (of specific columns), Update of their own record (excluding some columns). - User : Full Read (of specific columns different than Manager), Update of their own record (excluding some columns). I'm using EFCore and LINQ for the query and I had first thought about trying to have the security logic in the pipeline 'build' the IQueryable that will get executed by the query handler, but I haven't found a good resource on how to do that, so I am updating the results of the handled query before sending it back to the Controller.
Context is important. You make references to IEnumerable when operations like "create", update and delete (of a give user's record) typically don't involve "updating sets"; they involve a single record or object graph. "Browsing" implies sets, and is generally only limited in a broad sense (one may or may not access this particular "view"). When talking about "roles", this is not something that starts at query time; it starts as soon as the user has logged in and identified themselves. The app should be aware of parts to limit based on roles; which includes forms, reports, menu items, and sensitive data (which may involve limiting tables, records, columns, ranges, etc. based on need). The "back end" accesses "views" of the data based on roles; and applies "need to know" filters before it even tackles the user's request. Still a pipeline, but with some extra "role filters" in between components; which includes the interactive / online / real-time parts. So, you need to maintain and interface with a user's "role object" from start to finish. And the data that is finally handed off to that actual "insert" or update (that last "grain") is then free of any "role checking".
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
-
Could you explain a bit more what you mean by 'data driven with a proper query'? Almost every tutorial/example I've found only seems to be basic "Can Access/Can Not Access", but my needs are more granular. I'm currently using CQRS to have my Controller send an EmployeeQuery request. I've added a pipeline to the request handler which has my security logic and updates the results from the query as I need to apply information not just to the records themselves, but also potentially individual properties of the record. I didn't want to mix my security logic in with my query as I thought that could get hard to expand in the future. Here's an example of the current rules/permissions: - Admin : Full Create, Read, Update, (Soft/Hard) Delete. - HR : Full Create, Read, Update, Soft Delete. - Manager : Full Read (of specific columns), Update of their own record (excluding some columns). - User : Full Read (of specific columns different than Manager), Update of their own record (excluding some columns). I'm using EFCore and LINQ for the query and I had first thought about trying to have the security logic in the pipeline 'build' the IQueryable that will get executed by the query handler, but I haven't found a good resource on how to do that, so I am updating the results of the handled query before sending it back to the Controller.
as Gary says (imho), you should not be retrieving all records, and then using Linq: you EF query should pull only the records that need updating, and, then, write those updated records back to the data store. IEnumerable: keep an eye out for multiple enumerations: [^]
«The mind is not a vessel to be filled but a fire to be kindled» Plutarch