1. はじめに
この記事では、angularjs を実際のプロジェクトに適用する方法を紹介します。この記事では、angularjs を使用して簡単な権限管理システムを構築します。以下では特に言うことはありません。本題に直接進みましょう。
2. 全体的なアーキテクチャ設計の概要
まず、プロジェクト全体の建築設計図を見てください。
上の図から、プロジェクト全体の全体構造がわかります。次に、プロジェクトの全体構造を詳しく紹介します。
asp.net web api を使用して rest サービスを実装します。この実装方法により、バックエンド サービスの共通化された個別の展開とより優れた拡張が実現されました。 web 層はアプリケーション サービス インターフェイスに依存し、castle windsor を使用して依存関係の注入を実装します。
表示レイヤー (ユーザー ui)
表示レイヤーは、angularjs によって実装された spa ページを使用します。すべてのページ データは非同期でロードされ、部分的に更新されるため、この実装によりユーザー エクスペリエンスが向上します。
アプリケーション サービス
angularjs は、http サービスを通じて web api にデータを取得するようリクエストし、web api の実装はアプリケーション層を呼び出してデータをリクエストします。
インフラストラクチャ層
インフラストラクチャ層には、ウェアハウジングの実装といくつかのパブリック メソッドの実装が含まれます。
ウェアハウス層は ef code first を使用して実装され、 を使用します。 ef 移行: データベースを作成および更新します。
lh.common レイヤーは、ログ ヘルプ クラス、式ツリー拡張機能、その他のクラスの実装など、いくつかの一般的なメソッドを実装します。
ドメイン層
ドメイン層は主に、ドメイン モデルの実装やウェアハウジング インターフェイスの定義など、プロジェクトのすべてのドメイン モデルを実装します。
完全な構造を紹介することに加えて、プロジェクトのバックエンド サービスの実装と web フロントエンドの実装も個別に紹介します。
3. バックエンド サービスの実装
バックエンド サービスは主に asp.net web api を使用してバックエンド サービスを実装し、castle windsor は依存関係の注入を完了するために使用されます。
ここでは、権限管理におけるユーザー管理を取り上げて、rest web api サービスの実装を紹介します。
ユーザー データを提供する rest サービスの実装:
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); } }
上記のコード実装から、ユーザー rest サービスは iuserservice インターフェイスに依存しており、従来の方法ですべてのビジネス ロジックを web api 実装に組み込むのではなく、いくつかの特定のビジネス実装を対応する api 実装にカプセル化していることがわかります。アプリケーション層では、rest api は対応するアプリケーション層のサービスの呼び出しのみを担当します。この設計の利点は次のとおりです。
rest サービス部門は、責任を分離し、アプリケーション層サービスのインスタンス化を別の依存関係注入コンテナーに任せて完了させるアプリケーション層インターフェイスに依存します。一方、rest サービスは、対応するアプリケーション サービスのメソッドを呼び出すことのみを担当します。データを取得します。特定のクラスの実装ではなく依存インターフェイスを使用すると、クラス間の結合が低くなります。 rest サービスには、特定のビジネス ロジックの実装は含まれません。この種の設計により、サービスをより適切に分離できます。後で wcf を使用して rest サービスを実装する場合は、wcf の rest サービス クラスで web api のロジックを繰り返す必要はありません。これはまったく問題ありません。次のインターフェイス メソッドを呼び出します。 wcf rest サービスを実装するためのアプリケーション サービス。したがって、ビジネス ロジックの実装はアプリケーション サービス層に抽出され、rest サービスの責任がより単一になり、rest サービス実装の拡張が容易になります。
ユーザー アプリケーション サービスの実装:
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; } }
ここでのアプリケーション サービス層は、コード レベルで読み取りと書き込みの分離を実現し、ireadonlyservice インターフェイスと iwriteservie インターフェイスを定義し、汎用メソッドを使用して書き込み操作を baseservice に抽象化するために、実際にさらに最適化できます。このような追加、削除、変更の操作が共通化できる理由は、これらの操作は非常に似ているが、操作の主体が異なるためである。実際、この種の実装は、私の別のオープンソース プロジェクト、onlinestore で使用されています。そこに行くために自分自身を達成します。
ウェアハウジング レイヤーの実装:
ユーザー アプリケーション サービスは、特定のストレージ クラスに直接依存するのではなく、そのインターフェイスにも依存します。対応するユーザー ストレージ クラスの実装は次のとおりです。
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 { }
4. angularjs フロントエンドの実装
web フロントエンドは angularjs を使用して実装され、モジュール型開発モデルを採用しています。 web フロントエンドの具体的なコード構造を以下の図に示します。
app/images // 存放web前端使用的图片资源 app/styles // 存放样式文件 app/scripts // 整个web前端用到的脚本文件 / controllers // angularjs控制器模块存放目录 / directives // angularjs指令模块存放目录 / filters // 过滤器模块存放目录 / services // 服务模块存放目录 / app.js // web前端程序配置模块(路由配置) app/modules // 项目依赖库,angular、bootstrap、jquery库 app/views // angularjs视图模板存放目录
angularjs を使用して開発された web アプリケーションのコード間の呼び出し階層は、基本的にバックエンドの呼び出し階層と同じであり、ビュー ページ -> コントローラー モジュール -> サービス モジュール -> web api サービスでもあります。
また、web フロントエンドの css および js リソースは bundle メソッドを使用して読み込まれるため、リソース要求の数が減り、ページの読み込み時間が短縮されます。特定のバンドル クラスの設定:
public class bundleconfig { // for more information on bundling, visit http://go.microsoft.com/fwlink/?linkid=301862 public static void registerbundles(bundlecollection bundles) { //类库依赖文件 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 项目文件 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")); //样式 bundles.add(new stylebundle("~/js/base/style").include( "~/app/modules/bootstrap/css/bootstrap.min.css", "~/app/styles/dashboard.css", "~/app/styles/console.css" )); } }
ホーム インデックス.cshtml
<!DOCTYPE html> <html ng-app="LH"> <head> <meta name="viewport" content="width=device-width" /> <title>简易权限管理系统Demo</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="/">简易权限管理系统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>
5. 操作による影響
フロントエンドとバックエンドの実装を紹介した後、プロジェクト全体の実行効果を見てみましょう。
6. 概要
この時点で、この記事の内容はすべて紹介されましたが、この記事の angularjs アプリケーション プロジェクトには、バッファリングのサポートがない、読み取りと書き込みの分離がない、一部の api のストレス テストがないなど、まだ多くの改善点があります。 。ただし、実際のプロジェクトでの angularjs の適用は基本的に次のようなものです。プロジェクトで angularjs を使用する必要があり、会社のバックエンドが .net である場合は、この記事の共有が良い参考になると思います。さらに、アーキテクチャの設計に関しては、私の他のオープンソース プロジェクト、onlinestore も参照してください。 fastworks。
以上は編集者がangularjsを使ってパーミッション管理システムを作成する方法を紹介しましたので、皆さんの参考になれば幸いです。