First, the introduction
This article will describe how to apply angularjs to the actual project. This article will use ANGULARJS to create a simple rights management system. Not much said below, directly into the subject.
Second, the overall architecture design introduction
First look at the entire project's architectural plan:
From the diagram above you can see an overall structure of the entire project, and then I'll detail the overall architecture of the project:
Use the ASP.net Web API to implement REST services. This implementation has reached the common, separate deployment, and better extension of back-end services. The Web tier relies on the application service interface and implements dependency injection using Castle Windsor.
Display layer (user UI)
The display layer uses the ANGULARJS to implement the Spa page. All page data is asynchronously loaded and partially refreshed, and this implementation will have a better user experience.
Application layer (Application Service)
Angularjs the HTTP service to request the Web API to obtain data, and the Web API implementation is to invoke the application tier to request data.
Infrastructure Layer
The infrastructure layer includes implementation of warehousing and implementation of some common methods.
The implementation of the storage layer is implemented using the EF Code-a method, and the EF migration is used to create the database and update the database.
The Lh.common layer implements some common methods, such as Log helper class, expression tree extension, and so on.
Domain Layer
The domain layer mainly implements all the domain models of the project, including the implementation of the domain model and the definition of the warehousing interface.
After introducing the overall structure, the backend service implementation of the project and the implementation of the Web front-end are described separately.
Third, back-end service implementation
Back-end services use the ASP.net Web API primarily to implement backend services, and use Castle Windsor to complete dependency injection.
This introduces the implementation of the rest Web API service with user management in the rights management.
Implementation of the rest service that provides user data:
public class UserController : ApiController
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
[HttpGet]
[Route("api/user/GetUsers")]
public OutputBase GetUsers([FromUri]PageInput input)
{
return _userService.GetUsers(input);
}
[HttpGet]
[Route("api/user/UserInfo")]
public OutputBase GetUserInfo(int id)
{
return _userService.GetUser(id);
}
[HttpPost]
[Route("api/user/AddUser")]
public OutputBase CreateUser([FromBody] UserDto userDto)
{
return _userService.AddUser(userDto);
}
[HttpPost]
[Route("api/user/UpdateUser")]
public OutputBase UpdateUser([FromBody] UserDto userDto)
{
return _userService.UpdateUser(userDto);
}
[HttpPost]
[Route("api/user/UpdateRoles")]
public OutputBase UpdateRoles([FromBody] UserDto userDto)
{
return _userService.UpdateRoles(userDto);
}
[HttpPost]
[Route("api/user/DeleteUser/{id}")]
public OutputBase DeleteUser(int id)
{
return _userService.DeleteUser(id);
}
[HttpPost]
[Route("api/user/DeleteRole/{id}/{roleId}")]
public OutputBase DeleteRole(int id, int roleId)
{
return _userService.DeleteRole(id, roleId);
}
}
As you can see from the above code implementation, the User rest service relies on the Iuserservice interface, and there is no traditional way to put all the business logic into the Web API implementation, but to encapsulate some specific business implementations into the corresponding application tier, REST The API is only responsible for invoking the services in the corresponding application tier. The advantages of this design are:
Rest services relies on the interface with the application layer, separating the responsibilities and handing the application-tier service instantiation to a separate dependency injection container, while the rest service is only responsible for invoking the method of the corresponding application service to get the data. The dependency interface is not dependent on the implementation of the specific class, which causes the class to be low coupling with the class. The rest service does not include specific business logic implementations. This design allows for better separation of services, and if you later want to use WCF to implement REST services, you do not need to repeat the logic in a Web API in the WCF Rest service class, which makes it possible to invoke the interface method of the application service to implement the WCF rest service. So the implementation of the business logic is pumped into the application services layer, and the design will make the rest service responsibility more simple and rest service implementations easier to extend.
Implementation of user application services:
public class UserService : BaseService, IUserService
{
private readonly IUserRepository _userRepository;
private readonly IUserRoleRepository _userRoleRepository;
public UserService(IUserRepository userRepository, IUserRoleRepository userRoleRepository)
{
_userRepository = userRepository;
_userRoleRepository = userRoleRepository;
}
public GetResults<UserDto> GetUsers(PageInput input)
{
var result = GetDefault<GetResults<UserDto>>();
var filterExp = BuildExpression(input);
var query = _userRepository.Find(filterExp, user => user.Id, SortOrder.Descending, input.Current, input.Size);
result.Total = _userRepository.Find(filterExp).Count();
result.Data = query.Select(user => new UserDto()
{
Id = user.Id,
CreateTime = user.CreationTime,
Email = user.Email,
State = user.State,
Name = user.Name,
RealName = user.RealName,
Password = "*******",
Roles = user.UserRoles.Take(4).Select(z => new BaseEntityDto()
{
Id = z.Role.Id,
Name = z.Role.RoleName
}).ToList(),
TotalRole = user.UserRoles.Count()
}).ToList();
return result;
}
public UpdateResult UpdateUser(UserDto user)
{
var result = GetDefault<UpdateResult>();
var existUser = _userRepository.FindSingle(u => u.Id == user.Id);
if (existUser == null)
{
result.Message = "USER_NOT_EXIST";
result.StateCode = 0x00303;
return result;
}
if (IsHasSameName(existUser.Name, existUser.Id))
{
result.Message = "USER_NAME_HAS_EXIST";
result.StateCode = 0x00302;
return result;
}
existUser.RealName = user.RealName;
existUser.Name = user.Name;
existUser.State = user.State;
existUser.Email = user.Email;
_userRepository.Update(existUser);
_userRepository.Commit();
result.IsSaved = true;
return result;
}
public CreateResult<int> AddUser(UserDto userDto)
{
var result = GetDefault<CreateResult<int>>();
if (IsHasSameName(userDto.Name, userDto.Id))
{
result.Message = "USER_NAME_HAS_EXIST";
result.StateCode = 0x00302;
return result;
}
var user = new User()
{
CreationTime = DateTime.Now,
Password = "",
Email = userDto.Email,
State = userDto.State,
RealName = userDto.RealName,
Name = userDto.Name
};
_userRepository.Add(user);
_userRepository.Commit();
result.Id = user.Id;
result.IsCreated = true;
return result;
}
public DeleteResult DeleteUser(int userId)
{
var result = GetDefault<DeleteResult>();
var user = _userRepository.FindSingle(x => x.Id == userId);
if (user != null)
{
_userRepository.Delete(user);
_userRepository.Commit();
}
result.IsDeleted = true;
return result;
}
public UpdateResult UpdatePwd(UserDto user)
{
var result = GetDefault<UpdateResult>();
var userEntity =_userRepository.FindSingle(x => x.Id == user.Id);
if (userEntity == null)
{
result.Message = string.Format("当前编辑的用户“{0}”已经不存在", user.Name);
return result;
}
userEntity.Password = user.Password;
_userRepository.Commit();
result.IsSaved = true;
return result;
}
public GetResult<UserDto> GetUser(int userId)
{
var result = GetDefault<GetResult<UserDto>>();
var model = _userRepository.FindSingle(x => x.Id == userId);
if (model == null)
{
result.Message = "USE_NOT_EXIST";
result.StateCode = 0x00402;
return result;
}
result.Data = new UserDto()
{
CreateTime = model.CreationTime,
Email = model.Email,
Id = model.Id,
RealName = model.RealName,
State = model.State,
Name = model.Name,
Password = "*******"
};
return result;
}
public UpdateResult UpdateRoles(UserDto user)
{
var result = GetDefault<UpdateResult>();
var model = _userRepository.FindSingle(x => x.Id == user.Id);
if (model == null)
{
result.Message = "USE_NOT_EXIST";
result.StateCode = 0x00402;
return result;
}
var list = model.UserRoles.ToList();
if (user.Roles != null)
{
foreach (var item in user.Roles)
{
if (!list.Exists(x => x.Role.Id == item.Id))
{
_userRoleRepository.Add(new UserRole { RoleId = item.Id, UserId = model.Id });
}
}
foreach (var item in list)
{
if (!user.Roles.Exists(x => x.Id == item.Id))
{
_userRoleRepository.Delete(item);
}
}
_userRoleRepository.Commit();
_userRepository.Commit();
}
result.IsSaved = true;
return result;
}
public DeleteResult DeleteRole(int userId, int roleId)
{
var result = GetDefault<DeleteResult>();
var model = _userRoleRepository.FindSingle(x => x.UserId == userId && x.RoleId == roleId);
if (model != null)
{
_userRoleRepository.Delete(model);
_userRoleRepository.Commit();
}
result.IsDeleted = true;
return result;
}
public bool Exist(string username, string password)
{
return _userRepository.FindSingle(u => u.Name == username && u.Password == password) != null;
}
private bool IsHasSameName(string name, int userId)
{
return !string.IsNullOrWhiteSpace(name) && _userRepository.Find(u=>u.Name ==name && u.Id != userId).Any();
}
private Expression<Func<User, bool>> BuildExpression(PageInput pageInput)
{
Expression<Func<User, bool>> filterExp = user => true;
if (string.IsNullOrWhiteSpace(pageInput.Name))
return filterExp;
switch (pageInput.Type)
{
case 0:
filterExp = user => user.Name.Contains(pageInput.Name) || user.Email.Contains(pageInput.Name);
break;
case 1:
filterExp = user => user.Name.Contains(pageInput.Name);
break;
case 2:
filterExp = user => user.Email.Contains(pageInput.Name);
break;
}
return filterExp;
}
}
In fact, the application service layer can further optimize, realize the code level of read and write separation, define Ireadonlyservice interface and Iwriteservie interface, and write operation can be abstracted into the baseservice in the way of generic method. Such a number of additions and deletions to implement the public, the reason for this operation can be implemented public, because these operations are very similar, but the operation of the entity is not the same. In fact, this implementation in my other open source project has been used: Onlinestore. You can refer to this on your own to achieve.
The realization of the storage layer:
User application services are also not directly dependent on the specific warehousing classes, but also rely on their interfaces. The corresponding user warehousing classes are implemented as follows:
public class BaseRepository<TEntity> : IRepository<TEntity>
where TEntity :class , IEntity
{
private readonly ThreadLocal<UserManagerDBContext> _localCtx = new ThreadLocal<UserManagerDBContext>(() => new UserManagerDBContext());
public UserManagerDBContext DbContext { get { return _localCtx.Value; } }
public TEntity FindSingle(Expression<Func<TEntity, bool>> exp = null)
{
return DbContext.Set<TEntity>().AsNoTracking().FirstOrDefault(exp);
}
public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> exp = null)
{
return Filter(exp);
}
public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> expression, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize)
{
if (pageNumber <= 0)
throw new ArgumentOutOfRangeException("pageNumber", pageNumber, "pageNumber must great than or equal to 1.");
if (pageSize <= 0)
throw new ArgumentOutOfRangeException("pageSize", pageSize, "pageSize must great than or equal to 1.");
var query = DbContext.Set<TEntity>().Where(expression);
var skip = (pageNumber - 1) * pageSize;
var take = pageSize;
if (sortPredicate == null)
throw new InvalidOperationException("Based on the paging query must specify sorting fields and sort order.");
switch (sortOrder)
{
case SortOrder.Ascending:
var pagedAscending = query.SortBy(sortPredicate).Skip(skip).Take(take);
return pagedAscending;
case SortOrder.Descending:
var pagedDescending = query.SortByDescending(sortPredicate).Skip(skip).Take(take);
return pagedDescending;
}
throw new InvalidOperationException("Based on the paging query must specify sorting fields and sort order.");
}
public int GetCount(Expression<Func<TEntity, bool>> exp = null)
{
return Filter(exp).Count();
}
public void Add(TEntity entity)
{
DbContext.Set<TEntity>().Add(entity);
}
public void Update(TEntity entity)
{
DbContext.Entry(entity).State = EntityState.Modified;
}
public void Delete(TEntity entity)
{
DbContext.Entry(entity).State = EntityState.Deleted;
DbContext.Set<TEntity>().Remove(entity);
}
public void Delete(ICollection<TEntity> entityCollection)
{
if(entityCollection.Count ==0)
return;
DbContext.Set<TEntity>().Attach(entityCollection.First());
DbContext.Set<TEntity>().RemoveRange(entityCollection);
}
private IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> exp)
{
var dbSet = DbContext.Set<TEntity>().AsQueryable();
if (exp != null)
dbSet = dbSet.Where(exp);
return dbSet;
}
public void Commit()
{
DbContext.SaveChanges();
}
}
public class UserRepository :BaseRepository<User>, IUserRepository
{
}
Four, Angularjs front-end implementation
The realization of Web front-end is to use Angularjs to realize, and adopt modular development mode. The code structure for the specific Web front-end is shown in the following illustration:
App/images//Store the image resources used on the front end of the Web App/styles//hold the
style file
app/scripts//script files used throughout the Web front-end
/controllers// ANGULARJS Controller Module Storage Directory/
directives//ANGULARJS instruction Module Storage Directory/
filters//filter Module storage Directory/
Services//Service Module storage directory
/ App.js//Web front-end configuration module (routing configuration) App/modules//
Project Dependencies Library, angular, Bootstrap, jquery library
app/views//ANGULARJS View Template Storage Directory
The code of the Web application developed by ANGULARJS is basically consistent between the calling hierarchy and the backend, and is also the Web API Service of the view page-the Controller module-"Service module".
and the Web front-end CSS and JS resources loaded using a bundle way to reduce the number of requests for resources, so as to speed up the page load time. Configuration of specific bundle classes:
public class BundleConfig
{
// For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles (BundleCollection bundles)
{
// Class library dependent files
bundles.Add (new ScriptBundle ("~ / js / base / lib"). Include (
"~ / app / modules / jquery-1.11.2.min.js",
"~ / app / modules / angular / angular.min.js",
"~ / app / modules / angular / angular-route.min.js",
"~ / app / modules / bootstrap / js / ui-bootstrap-tpls-0.13.0.min.js",
"~ / app / modules / bootstrap-notify / bootstrap-notify.min.js"
));
// angularjs project file
bundles.Add (new ScriptBundle ("~ / js / angularjs / app"). Include (
"~ / app / scripts / services / *. js",
"~ / app / scripts / controllers / *. js",
"~ / app / scripts / directives / *. js",
"~ / app / scripts / filters / *. js",
"~ / app / scripts / app.js"));
//style
bundles.Add (new StyleBundle ("~ / js / base / style"). Include (
"~ / app / modules / bootstrap / css / bootstrap.min.css",
"~ / app / styles / dashboard.css",
"~ / app / styles / console.css"
));
}
}
first index.cshtml
<! DOCTYPE html>
<html ng-app = "LH">
<head>
<meta name = "viewport" content = "width = device-width" />
<title> Demo for simple rights management system </ title>
@ Styles.Render ("~ / js / base / style")
@ Scripts.Render ("~ / js / base / lib")
</ head>
<body ng-controller = "navigation">
<nav class = "navbar navbar-inverse navbar-fixed-top">
<div class = "container-fluid">
<div class = "navbar-header">
<button type = "button" class = "navbar-toggle collapsed" data-toggle = "collapse" data-target = "# navbar" aria-expanded = "false" aria-controls = "navbar">
<span class = "sr-only"> Toggle navigation </ span>
<span class = "icon-bar"> </ span>
<span class = "icon-bar"> </ span>
<span class = "icon-bar"> </ span>
</ button>
<a class="navbar-brand" href="/"> Simple permission management system Demo </a>
</ div>
<div class = "navbar-collapse collapse">
<ul class = "nav navbar-nav navbar-left">
<li class = "{{item.isActive? 'active': ''}}" ng-repeat = "item in ls">
<a href="#{{item.urls[0].link}}"> {{item.name}} </a>
</ li>
</ ul>
<div class = "navbar-form navbar-right">
<a href="@Url.Action("UnLogin", "Home", null)" class="btn btn-danger">
{{lang.exit}}
</a>
</ div>
</ div>
</ div>
</ nav>
<div class = "container-fluid">
<div class = "row">
<div class = "col-sm-3 col-md-2 sidebar">
<ul class = "nav nav-sidebar">
<li class = "{{item.isActive? 'active': ''}}" ng-repeat = "item in urls"> <a href="#{{item.link}}"> {{item.title }} </a> </ li>
</ ul>
</ div>
<div class = "col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div ng-view> </ div>
</ div>
</ div>
</ div>
@ Scripts.Render ("~ / js / angularjs / app")
</ body>
</ html>
Five, the Operation effect
After introducing the implementation of the front and rear ends, let's look at how the entire project works:
Vi. Summary
Here, all the content of this article has been introduced, although this article's ANGULARJS Application project also has many perfect places, for example, does not have the buffer support, does not realize reads and writes the separation, does not have some API to carry on the pressure test and so on. But angularjs in the actual project application basically is such, everybody if in the project has the need to use ANGULARJS, just your company backstage is again. NET, I believe the sharing of this article can be a good reference. In addition, the design of the architecture can also refer to my other Open source project: Onlinestore and Fastworks.
The above is a small set to introduce the use of ANGULARJS to create a rights management system method, I hope to help you!