Trying to be a better programmer
I have an application that keeps track of Roles and Permissions, I had classes for Role and Permission which were just value objects.
class Role
{
int RoleID
string RoleName
}
class Permission
{
int PermissionID
string PermissionName
}
If I wanted to get the permissions for a Role I would do
List<Permission> permissions = PermissionDAL.GetPermissions(RoleID)
But for convenience (it’s a Desktop application) I had to move the permission collection to the Role class so I could just do
permissions = roleInstance.GetPermissions();
and the Role class becomes
class Role
{
List<Permission> permissions = null;
int RoleID
string RoleName
public List<Permission> Permissions
{
get
{
if(permissions == null)
permissions = PermissionDAL.GetPermissions(this.RoleID);
return permissions;
}
}
}
Question 1: the challenge – here is the scenario
Role r = getARole();
//for user to see
display(r.Permissions);
//user adding permission to role
RoleDAL.AddPermission(RoleID, PermissionID);
//display new permissions
display(r.Permissions);
//But no, data is not refreshed because of the null check.
//Do I need to call a new instance of this Role?
Question 2 : I think this violate Inversion of Control?
Question 3 : How would you do an IoC for List<Role>
, setting the permissions for each Role in the collection, if one were to use IoC.
Thanks
1
-
Yes, this behavior is common in lazy-loaded data objects based on rapidly-changing data stores. You force a change here, but similar concurrency issues can happen in a multi-user system even when you’re looking at data in a read-only fashion.
The easiest solution to your specific issue is to add the permission to the Permissions list manually in addition to writing it to the database. However, that doesn’t solve concurrency; another user could add a permission after you get a list of them and you’ll never know. There are many other solutions that will solve this snag; most of them require a simple SQL statement to stand in for the larger SQL statement. For instance, you could ask for a count of the number of records, and if that doesn’t match the length of your list then repopulate it. You could also keep track of the “last modified date” of each element in the list, and if the most recent modified date of the records you would pull is newer than the newest last modified date of the data you have, refresh the data. Or, you can simply track how long ago you last refreshed, and if that’s beyond a certain threshold you set the data is “stale” and should be refreshed. These will necessarily require a round trip to the database, but they’re scalar queries so they economize on network bandwidth (and shouldn’t perform terribly against a well-structured DBMS).
-
No, this does not break Inversion of Control, because this logic should go into the Role.Permissions accessor and thus you can continue to use one resolved instance as necessary. Even if you couldn’t, there are ways to inject a factory method that can be used to produce as many “factory-scoped” (one per request) Role objects as you might need.
-
This depends on the IoC framework you’re using (or if you aren’t using one), and how you want that framework to produce Roles. If you have a factory method registered that takes a RoleID and produces a Role object, you can make that call as many times as you want. The Role object, by its nature, will then require something it can call to get the list of Permissions. Injecting the DAO is one option (and not a bad one as long as consuming code doesn’t have to know about the DAO), but I would prefer a delegate
Func<int, IEnumerable<Permission>>
(and anotherFunc<IEnumerable<Permission>, bool>
that you’ll use to determine if it’s necessary to refresh data) that you can inject into Role and that Role can use without having to know where it came from; then if you had to, you could pretty much tear your data access layer out and start completely from scratch, without any other object knowing the difference.
2