1. Introducción
Este artículo presentará cómo aplicar AngularJS a proyectos reales. Este artículo utilizará AngularJS para crear un sistema de gestión de permisos simple. No diré mucho a continuación, solo ve al tema.
2. Introducción al diseño general de la arquitectura
Primero, echemos un vistazo al diagrama de diseño arquitectónico de todo el proyecto:
De la figura anterior, podemos ver la estructura general de todo el proyecto. A continuación, presentaré la estructura general del proyecto en detalle:
Utilice la API web ASP.NET para implementar servicios REST. Este método de implementación ha logrado el uso público, la implementación y la mejor expansión de los servicios de fondo. La capa web depende de la interfaz de servicio de aplicaciones y utiliza Castle Windsor para implementar la inyección de dependencia.
Capa de visualización (interfaz de usuario del usuario)
La capa de visualización utiliza AngularJS para implementar páginas de spa. Todos los datos de la página se cargan de forma asincrónica y se actualizan localmente, por lo que esta implementación tendrá una mejor experiencia de usuario.
Servicio de aplicaciones
AngularJS solicita a la API web que obtenga datos a través del servicio HTTP, y la implementación de la API web es llamar a la capa de aplicación para solicitar datos.
Capa de infraestructura
La capa de infraestructura incluye la implementación de almacenamiento y la implementación de algunos métodos comunes.
La implementación de la capa de almacenamiento se implementa primero en el código EF, y el método de migración EF se utiliza para crear y actualizar la base de datos.
La capa común de LH. implementa algunos métodos comunes, como clases de ayuda log, extensiones de árboles de expresión y otras clases.
Capa de dominio
La capa de dominio implementa principalmente todos los modelos de dominio del proyecto, incluida la implementación del modelo de dominio y la definición de la interfaz de almacenamiento.
Además de introducir la estructura completa, presentaremos la implementación del servicio de fondo y la implementación del front-end web del proyecto respectivamente.
3. Implementación del servicio de fondo
Los servicios de backend utilizan principalmente la API web ASP.NET para implementar servicios de backend, y Castle Windsor se utiliza para completar la inyección de dependencia.
Aquí utilizamos la administración de usuarios en la gestión de permisos para introducir la implementación del servicio REST Web API.
Implementación del servicio REST que proporciona datos del usuario:
clase pública UserController: apicontroller {private readonly iuserService _Userservice; Public UserController (IUSERService UserService) {_USERSERVICE = Userservice; } [Httpget] [ruta ("API/user/getUsers")] public sutenBase getUsers ([fromuri] pageInput input) {return _userservice.getusers (entrada); } [Httpget] [ruta ("API/User/UserInfo")] Public outputBase getUserInfo (int id) {return _Userservice.getUser (id); } [Httppost] [ruta ("API/user/addUser")] public sutenBase CreateUser ([fromBody] userdto userdto) {return _userservice.adduser (userDTO); } [Httppost] [ruta ("API/USER/UpdateUser")] Public OutputBase UpdateUser ([fromBody] userdto userdto) {return _userservice.updateUser (userDTO); } [Httppost] [ruta ("API/User/Updateroles")] public OutputBase updateroles ([fromBody] userdto userdto) {return _userservice.updateroles (userDTO); } [Httppost] [ruta ("API/user/deletero/{id}")] public BaseBase DeleteUser (int id) {return _Userservice.deleteUser (id); } [Httppost] [ruta ("API/USER/Deleterole/{ID}/{rolyId}")] publicBase deletreetre (int id, int rolyId) {return _userservice.deleterole (id, rolyId); }}De la implementación del código anterior, se puede ver que el servicio REST de usuario depende de la interfaz con IUSERService, y no coloca toda la lógica comercial en la implementación de la API web de la manera tradicional, sino que encapsula algunas implementaciones comerciales específicas en la capa de aplicación correspondiente. La API REST solo es responsable de llamar a los servicios en la capa de aplicación correspondiente. Estos beneficios de diseño incluyen:
El Departamento de Servicio REST se basa en la interfaz con la capa de aplicación para separar las responsabilidades y entregar la instancia del servicio de la capa de aplicación a un contenedor de inyección de dependencia separado para su finalización. El servicio REST solo es responsable de llamar a los métodos de servicio de aplicación correspondientes para obtener datos. El uso de interfaces de dependencia en lugar de implementaciones con clases específicas hace un bajo acoplamiento entre clases. El servicio REST no incluye implementaciones lógicas comerciales específicas. Este diseño puede hacer que los servicios sean mejor separados. Si desea utilizar WCF para implementar servicios REST en la etapa posterior, no es necesario escribir repetidamente una lógica en la API web en la clase REST Service de WCF. En este momento, puede llamar al método de interfaz de servicio de aplicaciones para implementar el servicio WCF REST. Por lo tanto, la implementación de la lógica empresarial se extrae a la capa de servicio de aplicaciones para implementarla. Este diseño hará que las responsabilidades del servicio REST sean más solteras y la implementación del servicio REST más fácil de expandir.
Implementación de servicios de aplicaciones de usuario:
Public Class UserService: BaseService, IUSERService {Private Readonly iuserRepository _UserRepository; Readonly iuserroleRepository privado _UserrolerEpositoria; Public UserService (IUSerRepository UserRepository, iuserroleRepository UserroleRepository) {_UserRepository = userRepository; _UserrolePository = userRolePository; } public getResults <UsaterDTO> getUsers (PageInput Input) {var result = getDefault <getResults <sererDTO>> (); var filtroexp = buildExpression (entrada); Var Query = _userRepository.Find (FilterExP, user => user.id, sortorder.descending, input.current, input.size); resultado.total = _UserRepository.Find (FilterExP) .count (); result.data = query.select (user => new userDto () {id = user.id, createTime = user.creationTime, correo electrónico = user.email, state = user.state, name = user.name, realName = user.RealName, contraseña = "******", roles = user.userroles.take (4). z.role.id, name = z.role.rolename}). tolist (), totalRole = user.userroles.count ()}). tolist (); resultado de retorno; } public UpdaterSult UpdateUser (UserDTO User) {var result = getDefault <PonkingaterSult> (); var existuser = _UserRepository.FindSingle (u => u.id == user.id); if (existuser == null) {resultado.message = "user_not_exist"; resultado.statecode = 0x00303; resultado de retorno; } if (ishassamename (existuser.name, existuser.id)) {result.message = "user_name_has_exist"; result.statecode = 0x00302; resultado de retorno; } Existuser.RealName = user.RealName; Existuser.name = user.name; Existuser.state = user.state; Existuser.email = user.email; _UserRepository.Update (Existuser); _UserRepository.commit (); resultado.issaved = true; resultado de retorno; } public crereaterSult <int> addUser (userdto userdto) {var result = getDefault <CreaterSult <int> (); if (isHassamename (userdto.name, userDto.id)) {result.message = "user_name_has_exist"; result.statecode = 0x00302; resultado de retorno; } var user = new User () {CreationTime = DateTime.Now, Password = "", Correo electrónico = userDto.Email, state = userdto.state, realName = userdto.realName, name = userdto.name}; _UserRepository.Add (usuario); _UserRepository.commit (); result.id = user.id; resultado.IsCreated = True; resultado de retorno; } public DeleterSult DeleteUser (int userId) {var result = getDefault <DeleterSult> (); Var user = _userRepository.FindSingle (x => x.id == UserId); if (user! = null) {_UserRepository.Delete (usuario); _UserRepository.commit (); } resultado.isDeleted = true; resultado de retorno; } public UpdaterSult UpdatePwd (UserDTO User) {var result = getDefault <PonkingaterSult> (); var userEntity = _UserRepository.FindSingle (x => x.id == user.id); if (userEntity == null) {result.message = string.format ("el usuario editado actualmente" {0} "ya no existe", user.name); resultado de retorno; } UserEntity.password = user.password; _UserRepository.commit (); resultado.issaved = true; resultado de retorno; } public getResult <UsaterDTO> getUser (int userId) {var result = getDefault <getResult <usererdto> (); VAR MODELO = _USErRPository.FindSingle (x => x.id == UserId); if (model == null) {result.message = "use_not_exist"; resultado.statecode = 0x00402; resultado de retorno; } resultado.data = new UserDTO () {CreateTime = Model.CreationTime, Correo electrónico = model.email, id = model.id, realName = Model.RealName, state = model.state, name = model.name, contraseña = "******"}; resultado de retorno; } public UpdaterSult Updateroles (UserDTO User) {var result = getDefault <PonkingaterSult> (); VAR MODELO = _USErRPository.FindSingle (x => x.id == user.id); if (model == null) {result.message = "use_not_exist"; resultado.statecode = 0x00402; resultado de retorno; } var list = Model.Userroles.tolist (); if (user.roles! = NULL) {foreach (elemento var en user.roles) {if (! list.exists (x => x.role.id == item.id)) {_userroleRepository.add (new Userrole {rolyId = item.id, userid = model.id}); }} foreach (elemento var en la lista) {if (! user.roles.exists (x => x.id == item.id)) {_UserrolePository.Delete (item); }} _UserroleRepository.commit (); _UserRepository.commit (); } resultado.issaved = true; resultado de retorno; } public DeleterSult Deleterole (int userId, int rolyId) {var result = getDefault <DeleterSult> (); VAR MODELO = _USERROLEREPOSTORY.findSingle (x => x.userid == userId && x.roleID == rolyId); if (modelo! = null) {_UserrolePosepository.delete (modelo); _UserrolePository.ComMit (); } resultado.isDeleted = true; resultado de retorno; } public bool Exist (String UserName, String Password) {return _usErRepository.FindSingle (u => u.name == username && u.password == contraseña)! = null; } private bool ishassamename (name de cadena, int userId) {return! String.isnullorwhiteSpace (name) && _userRepository.find (u => u.name == name && u.id! = userId) .any (); } Expresión privada <func <user, bool >> buildExpression (pageInput PageInput) {Expression <func <user, bool >> filtreExP = 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); romper; Caso 1: filterEXP = user => user.name.contains (pageInput.name); romper; Caso 2: filterEXP = user => user.email.contains (pageInput.name); romper; } return FilterExP; }}Aquí, la capa de servicio de aplicaciones en realidad se puede optimizar aún más, implementar la separación de lectura y escritura a nivel de código, definir la interfaz IreadonlyService y la interfaz iWriteServie, y abstraga las operaciones de escritura en BasesService en forma de métodos genéricos. Dicha adición, deleción y operaciones de modificación son públicas. La razón por la cual se puede publicar esta operación es que estas operaciones son muy similares, y no son más que las diferentes entidades de las operaciones. De hecho, esta implementación se ha utilizado en otro proyecto de código abierto: Onlinestore. Puede consultar esto para implementarlo usted mismo.
Implementación de la capa de almacenamiento:
Los servicios de aplicaciones de usuario no dependen directamente de clases de almacenamiento específicas, sino que también dependen de sus interfaces. La clase de almacenamiento de usuario correspondiente se implementa de la siguiente manera:
clase pública BaseRepository <Tentity>: IRepository <Tentity> Where tentity: class, iEntity {private Readonly ThreadLocal <SermanagerDBContext> _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 (). FirstOffault (exp); } public IQueryable <Tentity> Find (Expression <func <tentity, bool >> exp = null) {return filtre (exp); } public IQueryable <Tentity> Find (Expression <Func <Tentity, Bool >> Expression, Expression <func <tentity, Dynamic >> sortPredicate, sortOrder sortOrder, int PageNumber, int PageSize) {if ((Pagenumber <= 0) ShougeOutOfRangeException ("Pagenumber", Pagenumber "Pagenumber debe ser genial que o igual a 1.); if (pageSize <= 0) tirar un nuevo argumentouToUfRangeException ("pageSize", pageSize, "PageSize debe ser grande o igual a 1."); var query = dbContext.set <Tentity> (). Where (Expression); var skip = (PageNumber - 1) * PageSize; var Take = PageSize; if (sortpredicate == null) tire nueva invalidOperationException ("basado en la consulta de paginación debe especificar los campos de clasificación y el orden de clasificación"); switch (sortOrder) {case sortOrder.ascending: var piGagedAscending = QUERY.SORTBY (SortPredicate) .skip (Skip) .take (tomar); regreso Pagedascending; case sortOrder devolver pagadoDescendiente; } Lanzar una nueva InvalidOperationException ("Basado en la consulta de paginación debe especificar los campos de clasificación y ordenar orden"); } public int getCount (expresión <func <tentity, bool >> exp = null) {return filtre (exp) .count (); } public void add (entidad de tentidad) {dbContext.set <Tentity> (). add (entidad); } Update public void (entidad tentidad) {dbContext.Entry (entidad) .state = entitystate.modified; } public void delete (entidad tentidad) {dbContext.Entry (entidad) .state = entityState.deleted; DbContext.set <Tentity> (). RETIR (entidad); } public void delete (iCollection <Tentity> EntityCollection) {if (entityCollection.count == 0) return; DbContext.set <Tentity> (). Attack (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); devolver dbset; } public void commit () {dbContext.SaveChanges (); }} Clase pública UserRepository: BaseRepository <serem>, iuserRepository {}4. Implementación frontal de AngularJS
La implementación del front-end web es usar AngularJS para implementarlo y adoptar un modelo de desarrollo modular. La estructura de código específica del front-end web se muestra en la figura a continuación:
App / Images // Store recursos de imagen utilizados por la aplicación web / styles // almacene archivos de estilo App / scripts // script archivos utilizados en todo el front-end / controladores // Angularjs Controller Module Module Directory / Directive // AngularJS Instrucción Módulo Director de almacenamiento de almacenamiento Configuración de almacenamiento de módulos Configuración de almacenamiento de módulos App/módulos // Biblioteca de dependencia del proyecto, Angular, Bootstrap, JQuery Biblioteca Aplicación/Vistas // Directorio de almacenamiento de plantilla de vista AngularJS
El nivel de llamada y el backend entre los códigos de aplicaciones web desarrolladas con AngularJS son básicamente los mismos que el backend, y también son la página Ver - Módulo de controlador - Módulo de servicio - Servicio de API web.
Además, la carga de recursos CSS y JS en la red front-end adopta el método del paquete para reducir la cantidad de recursos solicitados, lo que acelera el tiempo de carga de la página. Configuración de clase de paquete específica:
clase pública Bundleconfig {// Para obtener más información sobre la agrupación, visite http://go.microsoft.com/fwlink/?linkid=301862 public static void registerbundles (bundlecollection bundles) {// archivo de dependencia de la biblioteca de clases bundles.add (nuevo scriptbundle ("~/js/base/lib"). "~/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 (nuevo stylebundle ("~/js/base/style"). Incluye ("~/módulos/bootstrap/css/bootstrap.min.css", "~/app/styles/dashboard.css", "~/app/styles/console.css")); }}Inicio índice.cshtml
<! Doctype html> <html ng-app = "lh"> <head> <meta name = "viewport" content = "width = dispositivo-width"/> <title> Demo de sistema de administración de permisos simple </title> @styles.render ("~/js/base/style") @scripts.render ("~/js/base/lib") </head> <Head> <Head> ng-concontroller = "Navigation"> <Arb> <div> <div> <button type = "Botton" data-toggle = "colapse" data-target = "#navbar" aria-expandy = "false" aria-controls href = "/"> Systemdemo de gestión de permisos simples </a> </div> <viv> <ul> <li ng-depheat = "item in ls"> <a href = "#{{item.urls [0] .link}}"> {{item.nname}} </a> </li> </ul> <v> <a a. href = "@url.action (" Unlogin "," Home ", Null)"> {{lang.exit}}} </a> </div> </div> </am> <div> <div> <div> <ul> <ly ng-epeat = "ítem en urls"> <a href = "#{{item.link}}"> {{item.title}} </a> </li> </ul> </div> <div> <div ng ng-view> </div> </div> </div> </div> </div> @scripts.render (""/js/angularjs/app ") </body> </html>5. Efecto de operación
Después de introducir la implementación de los extremos frontales y traseros, echemos un vistazo al efecto de operación de todo el proyecto:
6. Resumen
En este punto, se han introducido todos los contenidos de este artículo, aunque el proyecto de aplicación AngularJS en este artículo todavía tiene muchas áreas perfectas, como ningún soporte de amortiguación, sin separación de lectura y escritura, sin pruebas de estrés en algunas API, etc. Pero la aplicación de angularjs en proyectos reales es básicamente así. Si necesita usar AngularJS en su proyecto, y el backend de su empresa es .NET, creo que compartir este artículo puede ser una buena referencia. Además, también puede consultar mi otro proyecto de código abierto: Onlinestore y FastWorks para el diseño de arquitectura.
Lo anterior es el método de usar AngularJS para crear un sistema de gestión de permisos introducido por el editor. ¡Espero que sea útil para todos!