I have had this question for quiet long and thought of seeking wisdom of the crowd here.
In my application there are 10 user roles allowed. Its an ASP.NET MVC2 application. Each controller method can be accessed by a specific user role only.
For implementing this I created a UserRoleType Enum.
public enum UserRoleType
{
SystemAdministrator = 1,
SeniorLevelExecutive = 2,
SeniorManager = 3,
JuniorManager = 4,
SeniorAdmin = 5,
JuniorAdmin1 = 6,
JuniorAdmin2 = 7,
SeniorAppraiser = 8,
JuniorAppraiser = 9,
SeniorResearch = 10
}
These values match with whats in database (UserRole table with 10 rows).
Also the UserRoleId of a user is stored in [User] table. As soon as a user logs in we get the roleId of the user from the database and match this to the above enum. For instance if the roleId of the user was 4, it means he/she is a Junior Manager.
This application is not in production now. The only drawback I see is when we go live if for some reason the value in User Role Type table did not match the Enum we will be in big trouble. What are the alternatives? Or should I just concentrate on making sure that we have the matching values in database. Any help will be highly appreciated.
Thanks a lot!
My opinion is, that if you can't trust your own configuration in the DB and Config files, you're up the creek anyways. Just make sure your DB records have that value as a specific column value, not the auto generated row ID.
Don't log the user in if the value for RoleID is not in the range of the Enum.
I would send an email to the admin to fix the issue.
A simple method would be to add a Name field to the UserRole table, and at your application startup, iterate over your enumeration, look up that UserRole by ID, and make sure the name matches UserRoleType.ToString(). You should be able to implement this without any major code changes.
private void VerifyUserRoles()
{
foreach (UserRoleType role in Enum.GetValues(typeof(UserRoleType)))
{
string dbName = /* SELECT Name FROM UserRole WHERE UserRoleId = (int)role */;
if(role.ToString() != dbName) throw new Exception();
}
}
A more complex method would be to not have an enum at all. If you want to have the list of roles be completely database driven, then make UserRoleType a class with a private constructor, and have it do a database read to create the list of objects. (I imagine there's a name for this pattern, not sure what it is, though.) Obviously, this would be a more significant change to your existing code.
public class UserRole
{
static List<UserRole> roles = new List<UserRole>();
static UserRole()
{
foreach (/* SELECT * FROM UserRole */)
{
roles.Add(new UserRole(...));
}
}
private UserRole(...){...}
// Permissions that the role consists of.
private bool CanEditEverything { get; private set; }
// Use this whenever you need to display a list of UserRoles.
public static ReadOnlyCollection<UserRole> AllUserRoles { get { return roles.AsReadOnly(); } }
// If you still need to explicitly refer to a role by name, rather than
// its properties, do these and set them in the static constructor.
public static UserRole SystemAdministrator { get; private set; }
}
I do this but add the appropriate constraints to the DB, with the constraint description directing the reader to the enum.
I am not sure you should be using a static data structure like an enum to model the elements from a dynamic one like a table. Would it not be better to have a UserRole entity class and a UserRoleCollection collection class? That way the set of user roles can be a bit more dynamic. Of course, anytime your code uses these data structures you would have to make sure you build in a failsafe mechanism that causes access to a particular resource to always be denied if an unknown user role is encountered. Naturally the code would generate a descriptive message if an unknown role somehow got entered into the database.
Related
I have already been working with linq in the past and I know how to access a database with SqlConnection and SqlCommand. Today I wanted to work with LinqToSql and see if and how I can make reading from and writing to a database easier. I did this Walkthrough.
Here is the code for the Customer Class (I changed it a bit but it still works perfectly fine):
[Table(Name = "Customers")]
class Customer
{
[Column(IsPrimaryKey = true)]
public string CustomerID { get; set; }
[Column]
public string City { get; set; }
}
And the code from Main:
class Program
{
static void Main(string[] args)
{
DataContext db = new DataContext(#"Data Source=(local)\SQLEXPRESS;Initial Catalog=Northwind;User ID=sa;Password=xxx");
Table<Customer> customers = db.GetTable<Customer>();
IQueryable query = from cust in customers
//where cust.City == "London"
select cust;
foreach (Customer customer in query)
Console.WriteLine("ID:{0}; City={1}", customer.CustomerID, customer.City);
Console.ReadKey();
}
}
It worked and I'm happy since this makes accessing a database so much easier for me. But there are still a couple of things that concern me:
It seems like for every column I want to include I need to create a new property in the Customer class and add a [Column] above it.
I need to create a new Class for every table that I want to read from (for example Customer, Orders and Suppliers in the Northwind DB).
First of all this seems to be quite a lot of unnecessary and repetitive work. Am I doing something wrong here?
Also I want the user to type in the name of a database and a table. This means I don't know yet which database he will pick and I also don't know the structure of the table yet. I can't create the class yet that is supposed to represent the table.
This means I will need to:
Create a type / class / object dynamically. I can't use Table<Customer> customers = db.GetTable<Customer>() because I don't know the type yet. The type will be the dynamically created class.
Mark the type / class / object as a table with [Table(Name="xxx")].
Read the columns from the SqlTable and create for every column a property and mark it with [Column].
After I'm done with this I can get the table with Table<T> table = db.GetTable<T>(), execute the query and display the datarows.
My second (and more important) question is: How can I do this? Does anyone have code examples or links to share? Or is this approach wrong?
You can store the result in dynamic object just like this.
dynamic table = db.GetTable<T>()
and use reflection to get type of the object.
I'm currently working on an app using Asp.Net MVC and C#. One of the requirement is to check what process the item is and then to only show the appropriate div. So I've decided to create a Table in the Db which consist of:
Id ProcessDescription DivOneVisible DivTwoVisible
1 Approved True False
2 Analysis True True
...
NOTE - The Id's and ProcessDescription will never change
Currently the table only holds 10 rows of data but the idea is, in future more rows/columns can be added.
I then go ahead and create the appropriate methods, one for each Div as follows
public bool ShowDivOne(int id)
{
var data = uow.GetRepository<ItemProcess>().GetById(id);
bool showDivOne = data.DivOneVisible.HasValue ? data.DivOneVisible.Value : false;
if (showDivOne)
return true;
else
return false;
}
I use the same code as above for ShowdivTwo() method, but match the different column. Then in the view I do
#if(ShowDivOne){//div one code here}
#if(ShowDivTwo){//div two code here}
This works but I was wondering if there is a more generic way where I can write one method which will cover each scenarios even if new columns or rows are added.
The main thing you still need to have a mapping between Database and ViewModel somewhere. At the moment it is hard coded in your methods.
You can make it absolutely generic if you start use reflection and have a mapping array with properties name. But I would nt recommend doing it as it over complication and hard to maintain and change.
(If you want I can go into details of implementation).
For you example I would suggest to have a viewmodel per item, that contains properties of divs to display.
public class ProcessViewModel
{
public int Id {get;set;}
public bool ShowDivOne {get;set;}
public bool ShowDivTwo {get;set;}
ProcessViewModel(){}
ProcessViewModel(ItemProcess data){
Id = data.Id;
ShowDivOne = data.DivOneVisible.HasValue ? data.DivOneVisible.Value : false;
ShowDivTwo = data.DivTwoVisible.HasValue ? data.DivTwoVisible.Value : false;
}
}
You still query for each item individually or query them altogether and pass data to viewmodel to construct it.
And a simple foreach on a view to traverse through the list of viewmodels.
Extending it to contain more properties would be very easy and strait forward, with minimum code to maintain.
This is the same of the entity that I am planning to save in the Azure Table Storage (ATS):
public class CarEntity : TableEntity
{
public CarEntity(string objPartitionKey, string objRowKey)
{
this.PartitionKey = objPartitionKey;
this.RowKey = objRowKey;
}
public string TableName
{
get { return "EntityTableName"; }
}
public string Property1 { get; set; }
// and this goes on
public string Property60 { get; set; }
}
Not all properties are required. Population of records depend on the selections that the user would be saving (e.g this is a CarEntity - if the user ordered wheels, properties WheelSize and WheelQuantity would be populated, if the user asks for repainting, RepaintingColor would be populated and so, on).
Assuming that there are 60 properties in this entity, not all properties gets saved in ATS. Despite a property being defined and no error being returned, that data doesn't get saved in the table. I know that there's a limit of 1MB per entity but considering the computations that we have done, it is kinda far from the 1MB limit.
Any help why columns don't appear even if the properties are saved accordingly? My save function is defined as follows:
public static CarEntity CarInsertOrReplace(CarEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
var table = SetupTable(entity.TableName);
table.CreateIfNotExists();
TableOperation insertOrMergeOperation = TableOperation.InsertOrReplace(entity);
TableResult result = table.Execute(insertOrMergeOperation);
CarEntity objEntity = result.Result as CarEntity;
return objEntity;
}
Sounds like the properties for your Entity vary based on the usage. What's probably happening is that Azure Table Storage is only creating columns for properties that are not null (have a value set). So you are will only see columns created for properties that have been set.
It sounds as if Table Storage is performing as you require but not necessarily as you expect. As answered by #Paul Fryer ATS will not store null values and as you do not (going by your quoted code) initialise the CarEntity properties they will be null by default. Therefore only properties set by the user will be saved to the table.
https://msdn.microsoft.com/en-us/library/azure/hh452242.aspx #Remarks:
If the Insert Or Replace Entity operation is used to replace an
entity, any properties from the previous entity will be removed if the
new entity does not define them. Properties with a null value will
also be removed.
Also, from your code
TableResult result = table.Execute(insertOrMergeOperation);
CarEntity objEntity = result.Result as CarEntity;
result will contain the TableOperation not a copy of the full entity in case that was what you were expecting.
This scenario might be the difference between using, for example, a SQL table where fields that are not given a value have a database default or default to null, against the Azure table model where fields not given a value do not exist. You just need to be aware of that difference when reading/writing to the storage chosen.
If you require all fields to be persisted to the table then provide a default value for each property, e.g. string.Empty
If strings are null or empty then it doesn't save the property at all. You're not doing anything wrong, you just have to consider this in your design when you're working with them.
If you're using TableEntity then it does the null/empty check for you. If you're using DynamicTableEntity then you have to do the check yourself.
In order to ask the question let me
write a class which contain my question.
class Student{
public int StudentId;
public string Name;
public int Age;
public string Address;
public void Save(){
if (StudentId == 0){
//// run the insert query here
/*
Insert into Student(Studentid,Name,Age,Address)values(...);
} else {
/// run the update query...
/*Update Student Set Address = #address , Age = #age , Name = #name
where StudentId = #stdid;*/
}
}
Now in order to use that class I will write the following code.
Student Std = new Student{StudentId = 1, Name="xyz", Address = "Home"};
Std.Save(); //// this will insert the value in the database
Now later on.
Student std1 = new Student();
std1.Studentid = 1;
std1.Age= 10;
std1.Save();
Now calling save this time will over write the Address with Null or empty string.
I seek advise on possible solutions to this problem.
There is a possibility that consumer wants to change the address with empty string.
Looking forward to replies.
Regards
K
}
What you are doing is instantiating a new Student object, and simply assigning new values to it. Since you never assign an address, it will be null.
Your insert/update logic is flawed, because it assumes that having a 0 for an ID means that the values have already been persisted (which may or may not be the case - your class design does allow for creation of a Student that is not persisted and give it any old ID). The result is that you are saving the values to the database, including the null address.
Your logic should be:
If student ID is not 0:
Start a transaction
Get student details from DB
Update student object with new details
Save
Commit the transaction
You should also restructure your Student class so the Id can't be simply written to (possibly just in the constructor) and have a way to load a Student from your database.
I think you should setup your structure differently.
Right now you are creating a new Student object fill it with (only) the changed data, and trying to update it by Studentid.
I think you should also create a (static) method like GetStudentById(int id). Let this return the student from the database with all properties filled, make your changes and write it back with the Save(). Now the values won't be empty or reset.
Instead of managing default values in your code, that can vary during a time, can be a lot, it's easier to manage default value setup on DB side. Just set the field of corresponding DB field default value to the value you need and stop carrying about it in your code. Naturally if you have access to DB structure and allowed to change it.
If you don't want reset the valu in a new object (it's not very clear from your post), just use a (say) Clone(..) method of your Student type, and after change only fields that very.
Example:
Student std= new Student{StudentId = 1, Name="xyz", Address = "Home"};
Std.Save();
//after
Student std1 = std.Clone(); //after clone you have Adrress field too in std1
std1.Studentid = 1;
std1.Age= 10;
std1.Save();
There's no magic method you can use for it... You'll simply have to check for null and decide whether to overwrite or not at some point.
There are ORM frameworks you can use like NHibernate or Entity Framework that will do the plumbing like this automatically if you're interested.
public interface IRule
{
bool Check(string input);
}
I have an interface that defines a rule. A rule is just a generic business rule or constraint that a user can create. So I have two sample rules:
public class ContainsRule : IRule
{
public string MustContain { get; set; }
public bool Check(string input)
{
return input.Contains(this.MustContain);
}
}
public class LengthRule : IRule
{
public int MaxLength { get; set; }
public bool Check(string input)
{
return input.Length <= this.MaxLength;
}
}
Rules may have more than one property that can be set, but in this example, each of these rules only have one property.
A user can create their own set of rules that should be saved. For example, a user have these three rules:
IRule[] rules = new IRule[]
{
new ContainsRule { MustContain = "foo" },
new ContainsRule { MustContain = "bar" },
new LengthRule { MaxLength = 5}
};
I need to persist this information to a database or some data store for each user. Since each user can have their own set of rules, I'm not sure what the database tables should look like.
User | ClassName | Parameters
-----------------------------------------------
1 | Namespace.ContainsRule | MustContain:foo
1 | Namespace.ContainsRule | MustContain:bar
1 | Namespace.LengthRule | MaxLength:5
My initial guess would be to create a table that looks something like the above, where parameters should a string. This means I would need to parse out the information and use reflection or something to set the properties. I would need to use the activator to create the class using the ClassName column. Another suggestion was instead of creating a delimited string for all the properties, there would be another table. Each of the properties would be its own row that has a foreign key relationship back to a row in the table above.
However, both examples don't seem to be the best way of saving these rules. Is there a better way to do this?
Why not use XML serialisation. The database table would contain the User # and the XML serialisation of the indvidual rule.
I believe that you are retrieving from database.
If so, try my suggestion.
Store the retrieved data into datatable, or convert them to datatable before applying the rule.
So the rule can be put as normal sql in datatable's filter property.
Instead of rule name and parameters, you can change it to a column containing
column like '%foo%'
Then apply this on the datatable's filter.
PS: I have converted List to Datatable and used such filter before
It seems to me, simply put, you need to map your objects to SQL tables (if only there was a technology that could to do this automatically... :)
Based on what you have posted, I envisage at least six SQL tables, more if there are further subclasses of rule e.g.
Rules
MustContainTextRules (subclass of Rules, 1:0..m)
MaxLengthRules (subclass of Rules, 1:0..1)
[Possibily further subclasses of Rules e.g. MinLengthRules?]
Rulesets ("a set of Rules")
Users
RulesetOwnership (relationship table between Users and Rulesets)
Whether they are distinct tables may be influenced by whether the rules are interrelated e.g. if the MinLengthRule value for a Ruleset cannot exceed its MaxLengthRule value then you may find it difficult to write a SQL constraint to validate this when they are in different tables.