1. Introdução
Este artigo introduzirá como aplicar o AngularJs a projetos reais. Este artigo usará o AngularJS para criar um sistema simples de gerenciamento de permissão. Não vou dizer muito abaixo, basta ir ao tópico.
2. Introdução ao design geral da arquitetura
Primeiro, vamos dar uma olhada no diagrama de design arquitetônico de todo o projeto:
A partir da figura acima, podemos ver a estrutura geral de todo o projeto. Em seguida, apresentarei a estrutura geral do projeto em detalhes:
Use a API da Web ASP.NET para implementar serviços de repouso. Esse método de implementação alcançou o uso do público, a implantação e a melhor expansão dos serviços de back-end. A camada da Web depende da interface do serviço de aplicativo e usa o Castle Windsor para implementar a injeção de dependência.
Exibir camada (interface do usuário do usuário)
A camada de exibição usa o AngularJS para implementar páginas de spa. Todos os dados da página são carregados de forma assíncrona e atualizada localmente; portanto, essa implementação terá uma melhor experiência do usuário.
Serviço de aplicativo
O AngularJS solicita a API da Web para obter dados através do serviço HTTP, e a implementação da API da Web é ligar para a camada de aplicativo para solicitar dados.
Camada de infraestrutura
A camada de infraestrutura inclui a implementação do armazenamento e a implementação de alguns métodos comuns.
A implementação da camada de armazenamento é implementada primeiro no código EF e o método de migração de EF é usado para criar e atualizar o banco de dados.
A camada lh.common implementa alguns métodos comuns, como classes de ajuda de log, extensões de árvores de expressão e outras classes.
Camada de domínio
A camada de domínio implementa principalmente todos os modelos de domínio do projeto, incluindo a implementação do modelo de domínio e a definição da interface de armazenamento.
Além de introduzir a estrutura completa, apresentaremos a implementação do serviço de back-end e a implementação do front-end da Web do projeto, respectivamente.
3. Implementação de serviço de back-end
Os serviços de back -end usam principalmente a API da Web ASP.NET para implementar serviços de back -end, e o Castle Windsor é usado para concluir a injeção de dependência.
Aqui, usamos o gerenciamento de usuários no gerenciamento de permissão para introduzir a implementação do serviço de API da Web REST.
Implementação do serviço REST que fornece dados do usuário:
classe pública 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] [rota ("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] [rota ("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.deleterol (id, roleid); }}A partir da implementação do código acima, pode -se observar que o Serviço de REST de Usuário depende da interface com o IUSERSERVICE e não coloca toda a lógica de negócios na implementação da API da Web da maneira tradicional, mas encapsula algumas implementações comerciais específicas na camada de aplicativos correspondente. A API REST é responsável apenas por chamar os serviços na camada de aplicativo correspondente. Esses benefícios de design incluem:
O Departamento de Serviço REST depende da interface com a camada de aplicativo para separar responsabilidades e entregar a instanciação do serviço da camada de aplicativo a um contêiner de injeção de dependência separado para conclusão. O serviço REST é responsável apenas por chamar os métodos de serviço de aplicativos correspondentes para obter dados. O uso de interfaces de dependência em vez de implementações com classes específicas faz com que o baixo acoplamento entre as classes. O serviço REST não inclui implementações de lógica de negócios específicas. Esse design pode melhorar os serviços separados. Se você deseja usar o WCF para implementar serviços de repouso no estágio posterior, não há necessidade de escrever repetidamente uma lógica na API da Web na classe de serviço restante do WCF. No momento, você pode chamar o método da interface do serviço de aplicativo para implementar o serviço REST WCF. Portanto, a implementação da lógica de negócios é extraída à camada de serviço de aplicativo para implementá -la. Esse design tornará as responsabilidades de serviço restantes mais solteiras e a implementação do serviço REST mais fácil de expandir.
Implementação de serviços de aplicativos de usuário:
classe pública UserService: BaseService, IUSERSERVICE {Private ReadOnly iUserRepository _UserRepository; Private ReadOnly IuSerrolErepository _USERROLEROPOSIITY; Public UserService (IUSERRepository UserRepository, IUSERROLEROPOSIENTE UserroleRoPository) {_UserRepository = UserRepository; _userrolerepository = userrolerepository; } public getResults <suserdto> getUsers (PageInput input) {var result = getDefault <getResults <userdto >> (); var filterExp = BuildExpression (entrada); var query = _userRepository.find (filtroxp, user => user.id, sortorder.descending, input.current, input.size); resultado.Total = _UserRepository.find (filterExp) .Count (); resultado.data = query.select (user => new userdto () {id = user.id, createTime = user.creationTime, email = user.mail, state = user.state, name = user.name, realname = user.realName, senha = "*****", RONS = user.USERRoles.Take (4). z.role.id, nome = z.role.rolename}). tolist (), totalrole = user.userRoles.count ()}). tolist (); resultado de retorno; } public updateResult updateUser (userdto user) {var resultado = getDefault <vupTeresult> (); var existuser = _userRepository.findsingle (u => u.id == user.id); if (existuser == null) {result.message = "user_not_exist"; resultado.statatecode = 0x00303; resultado de retorno; } if (ishassamename (existuser.name, existuser.id)) {result.message = "user_name_has_exist"; resultado.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 CreaterSult <int> addUser (userdto userdto) {var resultado = getDefault <creeaterSult <int>> (); if (ishassamename (userdto.name, userdto.id)) {result.message = "user_name_has_exist"; resultado.STATECODE = 0x00302; resultado de retorno; } var user = new user () {creationTime = dateTime.now, senha = "", email = userdto.email, state = userdto.state, realname = userdto.realname, name = userdto.name}; _userRepository.add (usuário); _UserRepository.Commit (); resultado.id = user.id; resultado.IScreated = true; resultado de retorno; } public DeLeteSult DeleteUser (int userID) {var result = getDefault <LeteSult> (); var user = _userRepository.findsingle (x => x.id == userID); if (user! = null) {_userRepository.delete (usuário); _UserRepository.Commit (); } resultado.isdeleted = true; resultado de retorno; } public updateResult updatepwd (userdto user) {var resultado = getDefault <adateResult> (); var userentity = _userRepository.findsingle (x => x.id == user.id); if (userentity == null) {result.message = string.format ("o usuário atualmente editado" {0} "não existe mais", user.name); resultado de retorno; } userentity.password = user.password; _UserRepository.Commit (); resultado.issaved = true; resultado de retorno; } public getResult <suserdto> getUser (int userId) {var result = getDefault <getResult <userdto >> (); var modelo = _userRepository.findsingle (x => x.id == userID); if (modelo == null) {result.message = "use_not_exist"; resultado.STATECODE = 0x00402; resultado de retorno; } resultado.data = new userdto () {createTime = model.creationTime, email = model.mail, id = model.id.id, realname = model.realname, state = model.state, nome = model.name, senha = "******"}; resultado de retorno; } public updateResult updateRoles (userdto user) {var resultado = getDefault <vupTeresult> (); var modelo = _userRepository.findsingle (x => x.id == user.id); if (modelo == null) {result.message = "use_not_exist"; resultado.STATECODE = 0x00402; resultado de retorno; } var list = model.userroles.tolist (); if (user.roles! = null) {foreach (var item em user.roles) {if (! list.exists (x => x.role.id == item.id)) {_userroleRoPository.add (new userrole {roled = item.id, userId = model.id.id}); }} foreach (var item na lista) {if (! user.roles.exists (x => x.id == item.id)) {_userrolerepository.delete (item); }} _userrolerepository.commit (); _UserRepository.Commit (); } resultado.issaved = true; resultado de retorno; } Public DeLeteResult deleterol (int userID, int roleid) {var resultado = getDefault <LeteSult> (); var modelo = _userroleRoPository.findsingle (x => x.UserId == UserID && x.roleid == roleid); if (modelo! _userrolerepository.Commit (); } resultado.isdeleted = true; resultado de retorno; } public bool existe (string nome de usuário, senha de string) {return _userRepository.findsingle (u => uk nome == nome de usuário && u.password == senha)! = null; } private bool ishassamename (nome da string, int userId) {return! string.isnullorwhitapace (nome) && _userRepository.find (u => u.name == nome && u.id! = userID) .any (); } Expressão privada <func <usuário, bool >> buildExpressão (PageInput PageInput) {Expression <func <usuário, bool >> filtroxp = user => true; if (string.isNullorWhitespace (PageInput.Name)) retornará filtroxp; Switch (PageInput.Type) {case 0: filterExp = user => user.name.contains (PageInput.Name) || user.email.contains (PageInput.Name); quebrar; Caso 1: filtroxp = user => user.name.contains (PageInput.Name); quebrar; Caso 2: filtroxp = user => user.email.contains (PageInput.Name); quebrar; } retornar filtroxp; }}Aqui, a camada de serviço do aplicativo pode ser otimizada ainda mais, implementar a separação de leitura e gravação no nível de código, definir a interface IRADONLYSERVICE e a interface iWriteServie e abstrair as operações de gravação em serviço de base na forma de métodos genéricos. Essa adição, as operações de exclusão e modificação são públicas. A razão pela qual essa operação pode ser divulgada é que essas operações são muito semelhantes e nada mais são do que as diferentes entidades das operações. De fato, essa implementação foi usada em outro projeto de código aberto: OnlineStore. Você pode se referir a isso para implementá -lo sozinho.
Implementação da camada de armazenamento:
Os serviços de aplicativos de usuário não confiam diretamente em classes de armazenamento específicas, mas também dependem de suas interfaces. A classe de armazenamento de usuário correspondente é implementada da seguinte maneira:
classe pública BaseRepository <Tentity>: IREPOSITION <Tentity> Onde Tentidade: Classe, Ientidade {Private ReadOnly Threadlocal <VuserManagerDbContext> _localctx = new Threadlocal <UserManagerDbContext> () => userManagerDbContext ()); public userManagerDbContext dbContext {get {return _localctx.value; }} Tentidade pública encontra (expressão <func <Tentity, bool >> exp = null) {return dbContext.Set <Tentity> (). Asnotracking (). Firstordefault (exp); } public iQueryable <Tentity> encontre (expressão <func <Tentity, bool >> exp = null) {return filtro (exp); } public iQueryable <Tentity> encontre (expressão <func <Tentity, bool >> Expressão, expressão <func <Tentity, dinâmica >> Sortredicate, sortorder sortorder, int pagEnumber, int papesize) {if (pagenumber <= 0) lança um novo argumentoToFrangeException ("Pagenumber", número "PagenBeNeN; if (PageSize <= 0) lança o novo argumentOutOfRangeException ("Pagesize", Pagesize, "PageSize deve ser grande ou igual a 1."); var Query = dbContext.set <Setity> (). Onde (expressão); var skip = (Pagenumber - 1) * PageSize; var Take = PageSize; if (SortPredicate == NULL) lançar novos InvalidoPerationException ("Com base na consulta de paginação, deve especificar campos de classificação e classificar a ordem."); Switch (Sortorder) {Case Sortorder.ascending: var PagedasCending = query.sortby (SortPredicate) .skip (Skip) .Take (Take); retornar Pagedascending; Case Sortorder.Descending: var Pageddescending = query.sortByDescending (SortPredicate) .Skip (Skip) .Take (Take); Retornar Pageddescending; } lança nova InvalidoPerationException ("Com base na consulta de paginação, deve especificar campos de classificação e classificar a ordem"); } public int getCount (Expressão <func <Tentity, bool >> exp = null) {retorna filtro (exp) .count (); } public void add (entidade da tentativa) {dbContext.Set <Tentity> (). Add (entidade); } public void update (entidade da tentativa) {dbContext.entry (entidade) .state = entityState.modified; } public void Delete (entidade da tentativa) {dbContext.entry (entidade) .state = entityState.deleted; DbContext.Set <Tentity> (). Remover (entidade); } public void Delete (ICOLLECTION <Tentity> entityCollection) {if (entityCollection.count == 0) return; DbContext.Set <Tentity> (). Anexe (entityCollection.first ()); DbContext.Set <Tentity> (). Removenge (entitycollection); } private iQueryable <Tentity> filtro (expressão <func <Tentity, bool >> exp) {var dbset = dbContext.set <Tentity> (). Asquerable (); if (exp! = null) dbset = dbset.where (exp); retornar dbset; } public void Commit () {dbContext.SaveChanges (); }} classe pública UserRepository: BasErepository <suser>, iuserRepository {}4. Implementação do front-end
A implementação do front-end da Web é usar o AngularJS para implementá-lo e adotar um modelo de desenvolvimento modular. A estrutura de código específica do front-end da Web é mostrada na figura abaixo:
App / Images // Armazene os recursos de imagem usados pelo aplicativo / estilos de extremidade frontal da web // Arquivos de estilo de armazenamento App / Scripts // Arquivos de script usados em toda a Web front-end / controllers // angularjs controlador Module Diretório de Armazenamento / // Módulo de Instrução do Módulo de Instrução do Módulo de Instrução / Filters // Filters // Serviço // Módulo de Módulo de Módulo de Módulo de Serviço. App/Módulos // Biblioteca de Dependência do Projeto, Angular, Bootstrap, JQuery Library App/Views // AngularJS Visualizar Modelo Diretório de Armazenamento
O nível de chamada e o back -end entre os códigos de aplicativos da Web desenvolvidos usando o AngularJS são basicamente os mesmos que o back -end, e eles também são o serviço de API da página de visualização - Módulo Controlador - Módulo de Serviço - Web.
Além disso, o carregamento dos recursos CSS e JS na Web front-end adota o método do pacote para reduzir o número de recursos solicitados, acelerando assim o tempo de carregamento da página. Configuração específica da classe de pacote:
public class BundleConfig { // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 public static void RegisterBundles(BundleCollection bundles) { // Class library dependency file 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.0.min.js", "~/app/modules/bootstrap-notify/bootstrap-notify.min.js")); // AngularJS Project File Bundles.add (new ScriptBundle ("~/js/angularjs/app"). "~/app/scripts/app.js")); // pacote de estilo.add (new stylebundle ("~/js/base/style"). }}Home Index.cshtml
<! Doctype html> <html ng-app = "lh"> <head> <meta name = "viewport" content = "width = width device-width"/> <title> Sistema de gerenciamento de permissão simples </title> @styles.render ("~/js/base/estilo/estilo) @scripts.render (" ~ ng-controller="navigation"> <nav> <div> <div> <button type="button" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span>Toggle navigation</span> <span></span> <span></span> <span></span> </button> <a href = "/"> Gerenciamento de permissão simples SystemDemo </a> </div> <div> <ul> <li ng-repeat = "item em ls"> <a href = "#{{item.urls [0] .link}}"> {{ite.name}} </a> href = "@url.action (" despontoin "," home ", null)"> {{lang.exit}} </a> </div> </div> </av> <div> <div> <div> href = "#{{item.link}}"> {{item.title}} </a> </li> </ul> </div> <div> <div ng-view> </div> </divrular5. Efeito de operação
Depois de introduzir a implementação das extremidades da frente e de trás, vamos dar uma olhada no efeito de operação de todo o projeto:
6. Resumo
Nesse ponto, todo o conteúdo deste artigo foi introduzido, embora o projeto de aplicação do AngularJS neste artigo ainda tenha muitas áreas perfeitas, como suporte buffersing, sem separação de leitura e gravação, nenhum teste de estresse em algumas APIs etc. Mas a aplicação de angulares em projetos reais é basicamente assim. Se você precisar usar o AngularJS em seu projeto e o back -end da sua empresa é .NET, acredito que o compartilhamento deste artigo pode ser uma boa referência. Além disso, você também pode se referir ao meu outro projeto de código aberto: OnLineStore e Rasted Works for Architecture Design.
O exposto acima é o método de usar o AngularJS para criar um sistema de gerenciamento de permissão introduzido pelo editor. Espero que seja útil para todos!