I'm trying to implement this architecture for the first time in a Winform. So I have a simple but very important question for me.
Take a simple example. I want the form to retrieve a user list and to allow a modification of the phone number.
I have this for the first step (simplified and I normally use interfaces)
public Form1()
{
InitializeComponent();
UserService _userService = new UserService();
listBoxUsers.DataSource = _userService.GetAllUsers();
}
class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
class UserService
{
UserRepository _userRepository=new UserRepository();
public Dictionary<int, string> GetAllUsers()
{
DataTable dtbl= _userRepository.AllUsers();
//Some code here
return dict;
}
}
class UserRepository
{
public DataTable AllUsers()
{
//Sql query
return dtbl;
}
}
Now by selecting a user in the lisbox, I'm able to display some information as the Phone number. When I'm changing the phone number, I need a method called UpdatePhoneNumber to update the SQL database.
But, where to place it? User or UserService (I don't talk about the SQL query, just the logic)
And after that, how to access (here or somewhere else in the app) to this user property to display it in the form? Directly with _user.Id (User must be instantiated in the form) or implement a _userService.The id which retrieves User.ID (in this case Form knows only UserService class).
Many thanks for your precious help
Put all Methods working on the User's data in the user class. Ask yourself the question what the user can do? Put all the logic which controlls the users in UserService like GetUserById, GetAllUsers, CreateUser and so..
Put all the method which the user can perform in the User class.
Or lately i was building such kind of thing and i merged User and UserServices into one and made the UserServices class method static so i can access them without instantion of the User.
Hope this help.
Here is what your basic 3-layered app looks like.
UI (your form and ui supporting objects)
BLL (GetAllUsers, SaveUser, DeleteUser, etc)
Data (ADO, EF, etc)
In your particular case, you really looking for Master-detail concept. A master usually the one where you display list of users
// Master
var _userList = Service.GetAllUsers(); // List<UserModel>
userGrid.Datasource = _userList;
I will not discuss it here but you can set bindings so that click on grid will result in detail control being populated. Or manually
// detail
UserModel model = master._userList[currIndex];
txtFirstName.Text = model.FirstName;
txtPhone.Text = model.Phone;
// . . . .
Now, of course, you're about to change the text box and save user...
// detail
UserModel model = master._userList[currIndex];
Service.SaveUser(model);
Master.Reload();
This is general idea, how this is done. If you follow, you have distinct layers. UI calls Service, which calls Data. For example, you have BLL
// Service
private IUserDataProvider _provider;
public List<UserModel> GetAllUsers()
{
var data = _provider.Get<User>();
// massage your 'data' and return List<UserModel>
. . . .
}
your provider might return some unwanted data, so you can use BLL to trim it and return only appropriate data. But you don't know what provider is doing inside. May be it is doing Ado.net or Entity Framework. Hence a true separation of layers.
Related
I am trying to implement search properties functionality in ASP.NET MVC web application, where the page has Experience list and each experience has a unique id assigned to it and has a list of properties. When the user clicks on one Experience it will display properties related to that experience. Now when the user clicks on another experience it will take both id's and return common properties. I have a class which cached the database result and store it.
public class GetExperienceResponse
{
public string Experienceid { get; set; }
public List<string> Properties { get; set; }
//Example: Expereienceid="E1", Properties="P1,P2,P3"
Experienceid="E2", Properties="P3,P4,P5"
}
public static List<GetExperienceAmenityResponse> GetExperience()
{
//returns experience and related properties here and cached it
}
The method I am using for Search properties based on Experience clicked is:
public ActionResult ExperienceSearch(string id)
{
List<GetExperienceResponse> Experiences = new List<GetExperienceResponse>();
Experiences = GetExperience(); //Populate the Experience result list
//logic for searching. When Click on Experience1, it will pass id "E1" and should display Properties "P1", "P2" and "P3". When Click on both Experience it should display common property which is "P3" here (Intersection of Experience 1 and Experience 2).
return View();
}
What is the best approach to implement this search? Can I use LINQ query on the returned result? I will appreciate any help. Thank you very much in advance!
Can have unique Cachekey for individual experience and have common cache key(hashkey by Experienceid1 &Experienceid2) for common property.
This is actually 2 questions in one.
I have an asp.net mvc application where I have to load a list of Modules, its just a simple list with ID, modulename and a class name to render it on the view with font awesome.
My model is like this:
public class Module
{
[Key]
public int Id { get; set; }
public string ModuleName { get; set; }
public string FontAwesomeClass { get; set; }
}
Because the module list is a Partial View that will render some icons on the top navigation bar, I dont want that for each refresh of the app, it goes to the DB, so it must be cached(I am using Azure REDIS Cache, not relevant for the question anyway), so instead of calling the DB context directly from the controller, I am calling a Cache Class that will check if the cache object exists, if not it will retrieve it from DB, if it does, it will return it from cache.
This my solution structure:
http://screencast.com/t/uayPYiHaPCav
Here is my controller Module.cs
public ActionResult GetModules()
{
return View(Cache.Module.GetModules());
}
As you can see the Controller does not have any logic where to get the data from.
Here is the Module.cs (on the Cache Namespace)
public class Module
{
private AppDataContext dbApp = new AppDataContext();
//Load modules from cache or from database
public static List<Models.Module> GetModules()
{
IDatabase cache = Helper.Connection.GetDatabase();
List<Models.Module> listOfModules = (List<Models.Module>)cache.Get("Modules");
if (listOfModules == null)
{
return dbApp.ModuleList.ToList();
}
else
{
return listOfModules;
}
}
}
Here I have a compiler error which I am not sure how to best fix it:
Error CS0120 An object reference is required for the non-static field,
method, or property 'Module.dbApp'
So that was my first question.
The 2nd question is more about the design pattern, do you consider this correct or not? the way I am trying to get the data from Cache, and its actually the Cache class which checks if data is on it or if it has to go to the DB.
First Question: make your private member static
private static AppDataContext dbApp = new AppDataContext();
2nd Question: your cache strategy seems pretty standard. The only thing is that you might want to expire cache data. For example, the cached data can get old and the longer it stays in the cache the older it gets. You might at some point want to expire it and get fresh data again.
Update:
#EstebanV for code sample (this off the top of my head, don't assume that it compiles):
/**
ICachedPersonDao abstracts away the caching mechanism
away from the core of your application
**/
public CachedPersonDao : ICachedPersonDao
{
private IPersonDao personDao = null;
public CachedPersonDao(IPersonDao personDao)
{
this.personDao = personDao;
}
public Person GetPersonById(int id){
bool isInCache = CACHE.SomeFunctionThatChecksInYourCache(id);
if (isInCache)
{
return CACHE.SomeFunctionThatReturnsTheCachedPerson(id);
}
else
{
//Well it's not in the cache so let's get it from the DB.
return this.personDao.GetPersonById(id);
}
}
}
/**
IPersonDao abstracts database communication
away from the core of your application
**/
public class PersonDao : IPersonDao
{
public Person GetPersonById(int id)
{
/** Get the person by id from the DB
through EntityFramework or whatever
**/
}
}
Usage:
In your controller, use ICachedPersonDao if you want to attempt to get from cache or use IPersonDao if you want to get it directly from the database without checking the cache.
Like I said, you should learn Dependency Injection it will help "inject" these dependencies into the classes that uses them.
I say again, this is off the top of my head. It won't compile. It's just to illustrate the concept.
Is there any disadvantage to using public static read-only objects in a web application? I have not used them before.
I have a web application that needs to change the display on the page depending on the user's permissions and the state of their existing action.
This is the static class which returns the custom object for their abilities.
public static class Abilities
{
public static readonly Ability ViewPage = new Ability()
{
LinkText = "something",
Hidden = false,
//Any other additional unchanging properties
}
}
Then for each user when they log in there is a method that sets the abilities that they should have
public class User
{
public List<Ability> GetMyAbilities()
{
var myAbilities = new List<Ability>();
if //some programming logic here
myAbilities.Add(Abilities.ViewPage);
return myAbilities;
}
}
This seems to be working as expected, but I wonder if there were any disadvantages or problems when using this method for multiple users?
As per comments - to add a bit of detail. The user class is created for each user when they log in to the web application. The list of abilities refers to stuff that they can do when they have logged in. Each user will therefore get a list of the abilities that they have when they log in to the Default page.
I'm having hard times trying to resolve cross-dependency between the projects in one solution.
The solution it's an asp.net application that has several projects.
The web project consumes the services of a sessionwrapper class in the SLN.Core project.
Declared like:
public sealed class SW
{
public static tblUserRow User
{
get
{
if (HttpContext.Current == null) return null;
return HttpContext.Current.Session["dtUser"] == null ? null : (tblUsersRow)(HttpContext.Current.Session["dtUser"] as tblUsers).Rows[0];
}
}
public static void User_Load(string userId)
{
tblUsers users = new tblUsers();
users.LoadByID(userId);
if (users.Count != 0)
{
HttpContext.Current.Session["dtUser"] = users;
}
}
}
The tblUserRow is part of the model definition of the users class (Strong typed datatable) in a separate models project. There are some other methods in the SW class, but aren't relevant.
So in SLN.Web I can access the user data like "SW.User.Name"
The models project is composed by the structures (model classes) and the DB engine classes in two different namespaces.
As seen above, the SW class depends on the models to declare User.
Up to here it's all OK.
But the fact is that model objects, need the User, Company, etc data when the classes are first created so they can get the default values. F.Ex: If you create a new invoice, it's nice to get the user (customer) assigned warehouse or payment type.
And as per working requeriments, the DB engine needs company or user data for making things like getting the DB (one per company) or saving log entries with user info.
Until the day that objects where passed on every call to DB engine classes or even methods, but now I'm refactoring this and I thought It would be cleaner and less memory consuming if that info could be got directly on the respective places from SW.
But there is cross-dependency between them. And as the SW members are declared static, so they can persist indepentent from session, I can't make an interface.
Any suggestions?
UPDATE: There was an issue with the scope of the user data that was previously solved, so I corrected here.
Also I'll add some more code for better understanding. From here VB, sorry dudes, but that's a nice model of diversity.
Example of the tblUser model in SLN.Models:
<DesignerCategory("Code"), System.SerializableAttribute()>
Partial Public Class tblUsers
Inherits TypedTableBase(Of tblUsersRow)
<DebuggerNonUserCodeAttribute()>
Public Sub New()
MyBase.New()
BeginInit()
InitClass()
EndInit()
End Sub
<DebuggerNonUserCodeAttribute()>
Private Sub InitClass()
TableName = TABLE_NAME
With Columns
.Add(New DataColumn(FIELD_ID, GetType(String)))
.Add(New DataColumn(FIELD_Name, GetType(String)))
...
'Added that last columns as example
.Add(New DataColumn(FIELD_Company, GetType(String)) With {.DefaultValue=SW.Company.ID})
.Add(New DataColumn(FIELD_Warehouse, GetType(String)) With {.DefaultValue=SW.Company.Warehouse})
End With
Dim keys(1) As DataColumn
keys(0) = Columns(0)
PrimaryKey = keys
End Sub
...
<DebuggerNonUserCodeAttribute()>
Public Sub LoadByID(Id As String)
Rows.Clear()
Merge(New SLN.DBEngine.Generic(SW.Company.Connection, doLog:=False).ExecuteQuery(COMMAND_LOADBY_ID, Id))
End Sub
...
End Class
Partial Public Class tblUsersRow
Inherits DataRow
<DebuggerNonUserCodeAttribute()>
Friend Sub New(ByVal builder As DataRowBuilder)
MyBase.New(builder)
End Sub
<DebuggerNonUserCodeAttribute()>
Public Property ID() As String
Get
Return DirectCast(MyBase.Item(0), String)
End Get
Set(value As String)
MyBase.Item(0) = value
End Set
End Property
<DebuggerNonUserCodeAttribute()>
Public Property Name() As String
Get
Return DirectCast(MyBase.Item(1), String)
End Get
Set(value As String)
MyBase.Item(1) = value
End Set
End Property
...
End Class
**Model classes are like this while I get a solution on EF for multiple DB. They where plain datatables.
Yes, there's another model class named Company that is also used in SW to provide company's data in session (multiple users, multiple companies logged onto)
You can see that on user construction, default values could be retrieved from SW. Same for other models like invoice heads, per example. That's the behaviour I want. By now all models that get default values, they get a complete object as a parameter of New(). Some models only want one field from a +25 flds object.
Also they use DBEngine for loading/querying/saving/deleting data.
In SLN.Web something like this may be seen (login):
SW.Company_Load(ddlCompany.Text)
sDescription = New Generic(SW.Company.Connection,False).ExecuteQuery("sp_Warehouse_LoadBy_Id",SW.User.Warehouse).Rows(0)("rDescription").ToString
Thaaats a roough example.
So SLN.Core.SW needs SLN.Models.tblUsersRow and SLN.DBEngine.Generic
And SLN.Models.tblUsers needs SLN.DBEngine but would also like to get SLN.Core.SW
...
SLN.DBEngine... needs SLN.Core.SW so he knows the DB to point to (some other things)
And SLN.Web need all of them. (sigh!)
Clear? (Errr...)
It is hard to understand your story clearly. But from what I get, your model need current_user to create objects (well, I don't get the business logic why this is required), and your web sln need the model for current_user.
Basically, the flow should be like this:
User class is defined
current_user is initiated
using the current_user, the model will initiate other objects
There are many ways available to accomplish this, however I propose 2 solution:
This implemenatation is to be done assuming you do dependency injection. Clean, and testable. This design is for Model solution.
public interface IUserProvider
{
User CurrentUser { get; }
}
public class ModelCreator
{
public ModelCreator(IUserProvider provider)
{
this.provider = provider;
}
IUserProvider provider;
public Invoice Get(){
User currentUser = provider.CurrentUser;
// do other
}
}
This implementation is to be done without dependency injection. Not clean, but mockable and easy to design. This design is for Model or Entity solution.
public static class UserProvider
{
private static Func<User> currentUserDelegate = new Func<User>(NullUser);
public static Func<User> CurrentUserDelegate
{
set
{
currentUserDelegate = value;
}
}
private static User NullUser()
{
return null;
}
public static User CurrentUser
{
get
{
return currentUserDelegate();
}
}
}
The usage:
public sealed class SW
{
private static User _currentUser;
public static User GetCurrentUser()
{
if (_currentUser == null)
{
tblUsers users = new tblUsers();
users.LoadByID(userId);
HttpContext.Current.Session["dtUser"] = users;
_currentUser = users[0];
}
return _currentUser;
}
public static void User_Load(string userId)
{
UserProvider.CurrentUserDelegate = new Func<User>(GetCurrentUser);
}
}
I finally solved the cross dependecy by moving the data entity classes related to those in SW class to the same CORE project.
Also moved the DB access engine classes to CORE, so in the models project there are only the entities and it can depend on CORE for DB access.
The CORE project does not depend on anything, as all it's entities are on it and also has the DB access engine.
So finally it was another problem of bad estructure design.
I also would like to give credit to Fendy, because the answer was agood one in other scenario.
#Fendy if you know a way for giving you credit, please tell me.
Thanks.
I don't have a lot of experience with this and I am really hoping to get a good suggestion from you guys. I need to implement the following security scenario and I would like to know the best way to do it.
Imagine we have Employees, Supervisors and Department managers.
Both Employees and Supervisors have ManagerId assigned based off and pointing to the department manager they belong to.
When a supervisor user logs in I want him to only see records for employees that belong to the same ManagerId as his.
If another supervisor with another ManagerId user logs in and manually punches other employee's information in url (ex: wwww.domain.com/employee/details/{id} ),
because his ManagerId != employee's ManagerId I would like the access to be restricted.
Does it make sense ?
I started typing out checks on all ActionMethods such as:
public ActionResult Details(int id)
{
var employee = employeeRepository.Get(id)
var user = (CustomIdentity)ControllerContext.HttpContext.User.Identity;
if(employee.managerId == user.managerId)
{
Do whatever...
}
else
{
Not allowed
}
}
But typing that out in all ActionMethods seems redundant and just..ehh... I know there must be a better way.
Here is a stab at a solution. It needs a bit of cleanup but should give you everything you need.
Create a custom ActionFilter, and then decorate your methods with it.
[ManagerIdAuthentication]
public ActionResult Details(int id)
{
// Gets executed if the filter allows it to go through.
}
The next class can be created in a separate library so you can include it in all your actions that require this validation.
public class ManagerIdAuthentication : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// the next line needs improvement, only works on an httpGet since retrieves
// the id from the url. Improve this line to obtain the id regardless of
// the method (GET, POST, etc.)
var id = filterContext.HttpContext.Request.QueryString["id"];
var employee = employeeRepository.Get(id);
var user = filterContext.HttpContext.User.Identity;
if (employee.managerId == user.managerId)
{
var res = filterContext.HttpContext.Response;
res.StatusCode = 402;
res.End();
filterContext.Result = new EmptyResult(); //may use content result if want to provide additional info in the error message.
}
else
{
// OK, let it through.
}
}
}
I had a similar issue in the past, what I would consider per-object permissions. What I did was add a member to the object similar to:
public bool CanUserAccess(User user) {
return managerId == user.managerId;
}
Then, at the top of each action providing access to a controlled resource:
public ActionResult Details(int id)
{
var employee = employeeRepository.Get(id)
var user = (CustomIdentity)ControllerContext.HttpContext.User.Identity;
if(!employee.CanUserAccess(user))
return new HttpUnauthorizedResult();
// Normal logic here
}
It's certainly not perfect, but it does centralize the permission handling and allows you to easily increase the complexity in the future (allow access up the chain, special rules for HR, etc.). You could also write another overload/extension to access the User.Identity property for a bit more automation (or at least handle the type conversions).
Since I was dealing with ACL's, I would have additional methods/parameters to specify the basic nature of the action (e.g. Read, Write, Delete, Create, etc.).