1. บทนำ
บทความนี้จะแนะนำวิธีการใช้ AngularJS กับโครงการจริง บทความนี้จะใช้ AngularJS เพื่อสร้างระบบการจัดการสิทธิ์อย่างง่าย ฉันจะไม่พูดด้านล่างมากแค่ไปที่หัวข้อ
2. บทนำสู่การออกแบบสถาปัตยกรรมโดยรวม
ก่อนอื่นมาดูแผนภาพการออกแบบสถาปัตยกรรมของโครงการทั้งหมด:
จากรูปด้านบนเราสามารถเห็นโครงสร้างโดยรวมของโครงการทั้งหมด ต่อไปฉันจะแนะนำโครงสร้างโดยรวมของโครงการโดยละเอียด:
ใช้ ASP.NET Web API เพื่อใช้บริการ REST วิธีการใช้งานนี้ได้บรรลุการใช้งานสาธารณะการปรับใช้และการขยายบริการแบ็คเอนด์ที่ดีขึ้น เว็บเลเยอร์ขึ้นอยู่กับอินเทอร์เฟซบริการแอปพลิเคชันและใช้ Castle Windsor เพื่อใช้การฉีดพึ่งพา
Display Layer (UIS UI)
เลเยอร์การแสดงผลใช้ AngularJS เพื่อใช้งานหน้าสปา ข้อมูลหน้าทั้งหมดจะถูกโหลดแบบอะซิงโครนัสและรีเฟรชในเครื่องดังนั้นการใช้งานนี้จะมีประสบการณ์การใช้งานที่ดีขึ้น
บริการแอปพลิเคชัน
AngularJS ขอให้ Web API รับข้อมูลผ่านบริการ HTTP และการใช้งาน Web API คือการโทรหาเลเยอร์แอปพลิเคชันเพื่อขอข้อมูล
ชั้นโครงสร้างพื้นฐาน
เลเยอร์โครงสร้างพื้นฐานรวมถึงการใช้คลังสินค้าและการดำเนินการของวิธีการทั่วไปบางอย่าง
การใช้เลเยอร์คลังสินค้าถูกนำมาใช้ในรหัส EF ก่อนและวิธีการโยกย้าย EF ใช้เพื่อสร้างและอัปเดตฐานข้อมูล
เลเยอร์ LH.Common ใช้วิธีการทั่วไปบางอย่างเช่นคลาสช่วยบันทึกการขยายต้นไม้การแสดงออกและคลาสอื่น ๆ
เลเยอร์โดเมน
เลเยอร์โดเมนส่วนใหญ่ใช้โมเดลโดเมนทั้งหมดของโครงการรวมถึงการใช้งานโมเดลโดเมนและคำจำกัดความของอินเทอร์เฟซคลังสินค้า
นอกเหนือจากการแนะนำโครงสร้างที่สมบูรณ์แล้วเราจะแนะนำการใช้บริการแบ็คเอนด์และการใช้งานส่วนหน้าเว็บของโครงการตามลำดับ
3. การใช้บริการแบ็คเอนด์
บริการแบ็กเอนด์ส่วนใหญ่ใช้ ASP.NET Web API เพื่อใช้บริการแบ็กเอนด์และ Castle Windsor ใช้เพื่อการฉีดพึ่งพาให้เสร็จสมบูรณ์
ที่นี่เราใช้การจัดการผู้ใช้ในการจัดการสิทธิ์เพื่อแนะนำการใช้บริการ REST Web API
การใช้บริการ REST ที่ให้ข้อมูลผู้ใช้:
ชั้นเรียนสาธารณะ USERCONTROLLER: APICONTROLLER {ส่วนตัว readEnly IUSERSERVICE _USERSERSERVICE; Public UserController (IUSERSERSERVICE USERSERSERVICE) {_USERSERVICE = USERSERVICE; } [httpget] [เส้นทาง ("api/user/getusers")] public outputbase getusers ([fromuri] อินพุต pageinput) {return _userservice.getusers (อินพุต); } [httpget] [เส้นทาง ("api/user/userinfo")] สาธารณะ outputbase getUserInfo (int id) {return _userservice.getUser (ID); } [httppost] [เส้นทาง ("api/user/adduser")] public outputbase createUser ([frombody] userdto userdto) {return _userservice.adduser (userdto); } [httppost] [เส้นทาง ("api/user/updateUser")] public outputbase updateUser ([frombody] userdto userdto) {return _userservice.updateUser (userDTO); } [httppost] [เส้นทาง ("api/user/updateroles")] public outputbase updateroles ([frombody] userdto userdto) {return _userservice.updateroles (userdto); } [httppost] [เส้นทาง ("api/user/deleteuser/{id}")] สาธารณะ outputbase deleteuser (ID int) {return _userservice.deleteuser (id); } [httppost] [เส้นทาง ("api/user/deleterole/{id}/{roleid}")] public outputbase deleterole (int id, int roleid) {return _userservice.deleterole (id, roleid); -จากการใช้งานรหัสข้างต้นจะเห็นได้ว่าบริการผู้ใช้ส่วนที่เหลือขึ้นอยู่กับอินเทอร์เฟซกับ IUSERSERVICE และไม่ได้วางตรรกะทางธุรกิจทั้งหมดในการใช้งาน Web API ในแบบดั้งเดิม REST API รับผิดชอบเฉพาะการโทรหาบริการในเลเยอร์แอปพลิเคชันที่เกี่ยวข้อง ประโยชน์การออกแบบนี้รวมถึง:
แผนกบริการ REST อาศัยอินเทอร์เฟซกับเลเยอร์แอปพลิเคชันเพื่อแยกความรับผิดชอบและส่งมอบการสร้างอินสแตนซ์ของบริการเลเยอร์แอปพลิเคชันไปยังคอนเทนเนอร์ฉีดพึ่งพาแยกต่างหากเพื่อให้เสร็จสิ้น บริการ REST มีหน้าที่รับผิดชอบในการเรียกใช้วิธีการบริการแอปพลิเคชันที่เกี่ยวข้องเพื่อรับข้อมูล การใช้อินเทอร์เฟซการพึ่งพาแทนการใช้งานที่มีคลาสเฉพาะทำให้การมีเพศสัมพันธ์ต่ำระหว่างคลาส บริการ REST ไม่รวมถึงการใช้ตรรกะทางธุรกิจเฉพาะ การออกแบบนี้สามารถทำให้บริการแยกได้ดีขึ้น หากคุณต้องการใช้ WCF เพื่อใช้บริการ REST ในระยะต่อมาไม่จำเป็นต้องเขียนตรรกะในเว็บ API ซ้ำ ๆ ในคลาสบริการ REST ของ WCF ในเวลานี้คุณสามารถโทรหาวิธีการเชื่อมต่อบริการแอปพลิเคชันเพื่อใช้บริการ WCF REST ดังนั้นการใช้ตรรกะทางธุรกิจจึงถูกดึงไปยังเลเยอร์บริการแอปพลิเคชันเพื่อนำไปใช้ การออกแบบนี้จะทำให้ความรับผิดชอบของบริการส่วนที่เหลือเป็นโสดมากขึ้นและการใช้งานบริการ REST ง่ายขึ้นในการขยาย
การใช้บริการแอปพลิเคชันผู้ใช้:
ผู้ใช้ระดับสาธารณะ: Baseservice, iUserService {ส่วนตัวอ่านอย่างเดียว _userrepository; ส่วนตัว readonly iuserrolerepository _userrolerepository; Public Userservice (IUSerRepository UserRepository, iUserRolerePository UserRolerePository) {_userRepository = USERREPOSITIOR; _userrolerePository = userrolerePository; } public getResults <UserDTO> getUsers (อินพุต PAGEINPUT) {var result = getDefault <getResults <UserDTO>> (); var filterExp = buildExpression (อินพุต); 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 => userdto ใหม่ () {id = user.id, createTime = user.creationtime, email = user.email, state = user.state, name = user.name, realname = user.realname, รหัสผ่าน = "******", roles = user.userroles.take (4) z.role.id, ชื่อ = z.role.rolename}). tolist (), totalrole = user.userroles.count ()}). tolist (); ผลการกลับมา; } Public UpdaterEsult UpdateUser (ผู้ใช้ userdto) {var result = getDefault <PolaterEsult> (); var applienduser = _userrepository.findsingle (u => u.id == user.id); if (appliAtUser == null) {result.message = "user_not_exist"; result.stateCode = 0x00303; ผลการกลับมา; } if (ishassamename (มี apliounduser.name, applienduser.id)) {result.message = "user_name_has_exist"; result.stateCode = 0x00302; ผลการกลับมา; } มีอยู่จริง realName = user.realName; มีอยู่จริง. name = user.name; มีอยู่จริง. state = user.state; มีอยู่ user.email = user.email; _userrepository.update (มีอยู่); _userrepository.Commit (); result.issaved = true; ผลการกลับมา; } 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; ผลการกลับมา; } var user = ผู้ใช้ใหม่ () {creationTime = dateTime.now, password = "", email = userdto.email, state = userdto.state, realname = userdto.realname, name = userdto.name}; _userrepository.add (ผู้ใช้); _userrepository.Commit (); result.id = user.id; result.iscreated = true; ผลการกลับมา; } สาธารณะ deleteresult deleteuser (int userId) {var result = getDefault <DeleterEsult> (); var user = _userrepository.findsingle (x => x.id == userId); if (user! = null) {_userrepository.delete (ผู้ใช้); _userrepository.Commit (); } result.isdeleted = true; ผลการกลับมา; } Public UpdaterEsult updatepwd (ผู้ใช้ userdto) {var result = getDefault <PolaterEsult> (); var userentity = _userrepository.findsingle (x => x.id == user.id); if (userEntity == null) {result.message = string.format ("ผู้ใช้ที่แก้ไขในปัจจุบัน" {0} "ไม่มีอยู่อีกต่อไป", user.name); ผลการกลับมา; } userentity.password = user.password; _userrepository.Commit (); result.issaved = true; ผลการกลับมา; } สาธารณะ getResult <ererdto> 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; ผลการกลับมา; } result.data = ใหม่ userDtO () {createTime = model.creationtime, email = model.email, id = model.id, realname = model.realname, state = model.state, name = model.name, รหัสผ่าน = "******"}; ผลการกลับมา; } Public UpdaterEsult Updateroles (USERDTO USER) {var result = getDefault <PolaterEsult> (); var model = _userrepository.findsingle (x => x.id == user.id); if (model == null) {result.message = "use_not_exist"; result.stateCode = 0x00402; ผลการกลับมา; } var list = model.userroles.tolist (); if (user.Roles! = null) {foreach (รายการ var ใน user.Roles) {ถ้า (! list.exists (x => x.role.id == item.id)) {_userrolerepository.add }} foreach (รายการ var ในรายการ) {if (! user.roles.exists (x => x.id == item.id)) {_userrolerepository.delete (รายการ); }} _userrolerePository.Commit (); _userrepository.Commit (); } result.issaved = true; ผลการกลับมา; } สาธารณะ 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 (รุ่น); _userrolerePository.Commit (); } result.isdeleted = true; ผลการกลับมา; } public bool มีอยู่ (ชื่อผู้ใช้สตริง, รหัสผ่านสตริง) {return _userrepository.findsingle (u => u.name == ชื่อผู้ใช้ && u.password == รหัสผ่าน)! = null; } private bool ishassamename (ชื่อสตริง, int userId) {return! string.isnullorwhitespace (ชื่อ) && _userrepository.find (u => u.name == name && U.ID! = userId) .any (); } นิพจน์ส่วนตัว <func <ผู้ใช้, บูล >> buildExpression (หน้าเว็บ PageInput) {นิพจน์ <func <ผู้ใช้, bool >> filterExp = user => true; if (string.isnullorwhitespace (pageInput.name)) ส่งคืน FilterExp; สวิตช์ (pageInput.type) {กรณี 0: filterExp = user => user.name.contains (pageInput.name) || user.email.contains (PageInput.Name); หยุดพัก; กรณีที่ 1: filterExp = user => user.name.contains (pageInput.name); หยุดพัก; กรณีที่ 2: filterExp = user => user.email.contains (pageInput.name); หยุดพัก; } return filterExp; -ที่นี่เลเยอร์บริการแอปพลิเคชันสามารถปรับให้เหมาะสมต่อไปใช้การอ่านระดับรหัสการอ่านและเขียนการแยกอินเทอร์เฟซ iReadonlyService และอินเตอร์เฟส IWRITESSERVIE และนามธรรมการดำเนินการเขียนลงในฐานในรูปแบบของวิธีการทั่วไป นอกจากนี้การดำเนินการลบและการปรับเปลี่ยนดังกล่าวเป็นสาธารณะ เหตุผลที่การดำเนินการนี้สามารถเผยแพร่ได้คือการดำเนินการเหล่านี้คล้ายกันมากและไม่มีอะไรมากไปกว่าหน่วยงานที่แตกต่างกันของการดำเนินงาน ในความเป็นจริงการใช้งานนี้ถูกนำมาใช้ในโครงการโอเพ่นซอร์สอื่น: Onlinestore คุณสามารถอ้างถึงสิ่งนี้เพื่อนำไปใช้ด้วยตัวเอง
การใช้เลเยอร์การจัดเก็บ:
บริการแอปพลิเคชันผู้ใช้ไม่ได้พึ่งพาคลาสคลังสินค้าที่เฉพาะเจาะจงโดยตรง แต่ยังต้องพึ่งพาอินเทอร์เฟซของพวกเขา คลาสคลังสินค้าผู้ใช้ที่เกี่ยวข้องจะถูกนำไปใช้ดังนี้:
ชั้นเรียนสาธารณะ BaseRepository <Tentity>: iRepository <Tentity> โดยที่ tentity: คลาส, ientity {ส่วนตัว readonly threadLocal <userManagerDbContext> _localctx = new ThreadLocal <userManagerDbContext> () => new UserManagerDbContext (); Public UserManagerDbContext dbContext {รับ {return _localctx.value; }} Tentity Public Findsingle (นิพจน์ <func <tentity, bool >> exp = null) {return dbcontext.set <tentity> (). asnotracking (). firstOrdefault (exp); } สาธารณะ iQueryable <Tentity> ค้นหา (นิพจน์ <func <tentity, bool >> exp = null) {return filter (exp); } สาธารณะ iQueryable <tentity> ค้นหา (นิพจน์ <func <tentity, bool >> นิพจน์, นิพจน์ <func <tentity, dynamic >> sortpredicate, sortorder sortorder, int pagenumber, หน้า int) {ถ้า (pagenumber <= 0) การโต้แย้งใหม่ ถ้า (pagesize <= 0) โยนอาร์กิวเมนต์ใหม่ OUTOUTOFRANgeException ("PAGESIZE", PAGESIZE, "PAGESIZE ต้องดีกว่าหรือเท่ากับ 1"); var query = dbContext.set <Tentity> (). โดยที่ (นิพจน์); var skip = (pagenumber - 1) * pagesize; var take = pagesize; if (sortPredicate == null) โยน InvalidoperationException ใหม่ ("ขึ้นอยู่กับการสืบค้นเพจเพจต้องระบุฟิลด์การเรียงลำดับและเรียงลำดับลำดับ"); สวิตช์ (sortorder) {case sortorder.ascending: var pageDascending = query.sortby (sortPredicate) .skip (skip) .take (ใช้); ส่งคืน pagedascending; case sortorder.descending: var pageddescending = query.sortbydescending (sortpredicate) .skip (skip) .take (ใช้); กลับ pageddescending; } โยน InvalidOperationException ใหม่ ("ขึ้นอยู่กับการสืบค้นเพจเพจต้องระบุฟิลด์การเรียงลำดับและเรียงลำดับลำดับ"); } สาธารณะ int getCount (นิพจน์ <func <tentity, bool >> exp = null) {return filter (exp) .count (); } โมฆะสาธารณะเพิ่ม (เอนทิตี tentity) {dbContext.set <tentity> (). เพิ่ม (เอนทิตี); } การอัปเดตโมฆะสาธารณะ (เอนทิตี tentity) {dbContext.entry (เอนทิตี) .state = EntityState.Modified; } โมฆะสาธารณะลบ (เอนทิตี TENTITY) {dbContext.entry (เอนทิตี) .state = EntityState.deleted; dbcontext.set <tentity> (). ลบ (เอนทิตี); } โมฆะสาธารณะลบ (icollection <tentity> entityCollection) {ถ้า (entityCollection.count == 0) ผลตอบแทน; dbcontext.set <tentity> (). แนบ (entityCollection.first ()); dbcontext.set <tentity> (). Removerange (entityCollection); } ตัวกรอง private iQueryable <tentity> (นิพจน์ <func <tentity, bool >> exp) {var dbset = dbContext.set <Tentity> (). AsQueryable (); if (exp! = null) dbset = dbset.where (exp); คืน DBSET; } โมฆะสาธารณะ commit () {dbcontext.savechanges (); }} คลาสสาธารณะ USerRepository: BaseRepository <user>, iUserRepository {}4. การใช้งานส่วนหน้า AngularJS
การใช้งานส่วนหน้าเว็บคือการใช้ AngularJS เพื่อนำไปใช้และนำรูปแบบการพัฒนาแบบแยกส่วนมาใช้ โครงสร้างรหัสเฉพาะของเว็บ front-end แสดงในรูปด้านล่าง:
แอพ / รูปภาพ // จัดเก็บทรัพยากรรูปภาพที่ใช้โดยแอป / สไตล์เว็บ front-end // store store files แอป / สคริปต์ // ไฟล์สคริปต์ที่ใช้ในเว็บทั้งหมด / คอนโทรลเลอร์ // angularJS คอนโทรลเลอร์โมดูลโมดูลไดเรกทอรี / คำสั่ง // AngularJS แอพ/โมดูล // ไลบรารีการพึ่งพาโครงการ, Angular, Bootstrap, jQuery Library App/Views // AngularJs ดูเทมเพลตไดเรกทอรีการจัดเก็บแม่แบบ
ระดับการโทรและแบ็กเอนด์ระหว่างรหัสของเว็บแอปพลิเคชันที่พัฒนาโดยใช้ AngularJS นั้นเหมือนกับแบ็กเอนด์และพวกเขายังเป็นหน้ามุมมอง - โมดูลคอนโทรลเลอร์ - โมดูลบริการ - บริการเว็บ API
นอกจากนี้การโหลดทรัพยากร CSS และ JS ในเว็บส่วนหน้าใช้วิธีการรวมกันเพื่อลดจำนวนทรัพยากรที่ร้องขอซึ่งจะช่วยเพิ่มเวลาในการโหลดหน้าเว็บ การกำหนดค่าคลาส Bundle เฉพาะ:
Bundleconfig คลาสสาธารณะ {// สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการรวมกลุ่มเยี่ยมชม http://go.microsoft.com/fwlink/?linkid=301862 โมฆะสาธารณะคงที่ (Bundlecollection Bundles) "~/แอพ/โมดูล/jQuery-1.11.2.min.js", "~/แอพ/โมดูล/เชิงมุม/เชิงมุม. "~/แอพ/โมดูล/bootstrap-notify/bootstrap-notify.min.js")); // AngularJS Project File File.Add (ScriptBundle ใหม่ ("~/JS/Angularjs/App") รวม ("~/app/scripts/services/*. js", "~/app/scripts/controllers/*. js", "~/แอป/สคริปต์ "~/app/scripts/app.js")); // style bundles.add (stylebundle ใหม่ ("~/js/base/style") รวม ("~/app/modules/bootstrap/css/bootstrap.min.css", "~/app/styles/dashboard.css", "~/app/styles/console.css"); -home index.cshtml
<! doctype html> <html ng-app = "lh"> <head> <meta name = "viewport" content = "width = device-width"/> <title> ระบบการจัดการการอนุญาตอย่างง่าย </title> @styles.render ("~/js/base/js/js/js/js/js/js/js ng-controller = "การนำทาง"> <av> <v> <div> <div> <button type = "ปุ่ม" data-toggle = "การล่มสลาย" data-tatarget = "#navbar" Aria-expanded = "False" Aria-controls = "Navbar"> <pan> href = "/"> ระบบการจัดการการอนุญาตอย่างง่าย ๆ ระบบการจัดการ </a> </div> <div> <ul> <li ng-repeat = "รายการใน ls"> <a href = "#{{item.urls [0] .link}}" href = "@url.action (" unlogin "," home ", null)"> {{lang.exit}} </a> </div> </div> </av> <div> <div> <div> <ul> <li ng-repeat = " href = "#{item.link}}"> {{item.title}} </a> </li> </ul> </div> </div> </div> </div>5. ผลการดำเนินงาน
หลังจากแนะนำการใช้งานด้านหน้าและด้านหลังให้ดูที่เอฟเฟกต์การดำเนินงานของโครงการทั้งหมด:
6. สรุป
ณ จุดนี้เนื้อหาทั้งหมดของบทความนี้ได้รับการแนะนำแม้ว่าโครงการแอปพลิเคชัน AngularJS ในบทความนี้ยังคงมีพื้นที่ที่สมบูรณ์แบบมากมายเช่นไม่มีการสนับสนุนบัฟเฟอร์ไม่มีการอ่านและเขียนการแยกไม่มีการทดสอบความเครียดใน APIs บางส่วน แต่การประยุกต์ใช้ AngularJs ในโครงการจริง หากคุณต้องการใช้ AngularJS ในโครงการของคุณและแบ็กเอนด์ของ บริษัท ของคุณคือ. NET ฉันเชื่อว่าการแบ่งปันบทความนี้อาจเป็นข้อมูลอ้างอิงที่ดี นอกจากนี้คุณยังสามารถอ้างถึงโครงการโอเพ่นซอร์สอื่น ๆ ของฉัน: Onlinestore และ Fastworks สำหรับการออกแบบสถาปัตยกรรม
ข้างต้นเป็นวิธีการใช้ AngularJs เพื่อสร้างระบบการจัดการสิทธิ์ที่แนะนำโดยตัวแก้ไข ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน!