新年奉献MVC+EF(CodeFirst)+Easyui医药MIS系统
本人閑來無事就把以前用Asp.net做過的一個(gè)醫(yī)藥管理信息系統(tǒng)用mvc,ef?,easyui重新做了一下,業(yè)務(wù)邏輯簡(jiǎn)化了許多,旨在加深對(duì)mvc,ef(codefirst),easyui,AutoMapper,Ninject等技術(shù)的理解和運(yùn)用,今天拿出來跟大家分享,就是想對(duì)這些技術(shù)還處在入門階段的朋友做以參考,以及正在用這些技術(shù)做項(xiàng)目的朋友做一個(gè)交流和探討。
?
我會(huì)在此項(xiàng)目的基礎(chǔ)上去逐一講解這些技術(shù),簡(jiǎn)單應(yīng)用就不講了,去看項(xiàng)目,主要講重點(diǎn)難點(diǎn)以及需要注意的地方,有些地方不明白的可以去下載源代碼,估計(jì)一看就能明白,廢話不多說先上一張項(xiàng)目分層圖:
其中Domain為領(lǐng)域模型層;Reposirory為倉(cāng)儲(chǔ)層,主要負(fù)責(zé)數(shù)據(jù)庫(kù)操作;Service為服務(wù)層,項(xiàng)目的業(yè)務(wù)邏輯全在此;Infrastructure為基礎(chǔ)結(jié)構(gòu)層,項(xiàng)目通用的類庫(kù)在這里;客戶端把View和Controller分開。
一:Entityframework
1:CodeFist
Entityframework中CodeFirst功能主要目的是生成數(shù)據(jù)庫(kù),而生成數(shù)據(jù)庫(kù)的字段,約束比較簡(jiǎn)單,而生成表之間的關(guān)系有時(shí)比較麻煩,尤其是聯(lián)合主鍵有時(shí)難搞,先上張項(xiàng)目模型關(guān)系圖的一部分:
?
大家看到這張圖能在CodeFirst的ModelConfiguration中寫出相應(yīng)代碼生成表之間關(guān)系嗎?其中SaleOutDetial(銷售出庫(kù)明細(xì))表有3個(gè)主鍵SaleOutId,WarePositionId,ProductId,而同時(shí)它們又是外鍵,SaleOutId來自于SaleOut(銷售出庫(kù))表,WarePositionId,ProductId來自于PositionStock(貨位庫(kù)存)表,而WarePositionId,ProductId是PositionStock表的主鍵也是外鍵,分別來自貨位表和品種表。那如何寫代碼配置SaleOutDetail表于其他表之間的關(guān)系呢?
Public class SaleOutDetailConfiguration : EntityTypeConfiguration<SaleOutDetail>{public SaleOutDetailConfiguration(){ToTable("SaleOutDetail");HasKey(k => new { k.SaleOutId, k.WarePositionId,k.ProductId });HasRequired(o => o.SaleOut).WithMany(o => o.SaleOutDetails).HasForeignKey(k => k.SaleOutId);HasRequired(o => o.PositionStock).WithMany(o => o.SaleOutDetails).HasForeignKey(k => new { k.WarePositionId, k.ProductId }).WillCascadeOnDelete(false);}} View Code其中HasKey,HasForeignKey用于配置表主鍵和外鍵,?HasRequired,WithMany配置一對(duì)多的關(guān)系;特別注意一點(diǎn)配置主鍵字段的順序和配置外鍵字段的順序一定要一致,如果上面HasForeignKey(k?=>?new?{?k.WarePositionId,?k.ProductId?})寫成HasForeignKey(k?=>?new?{?k.ProductId?,?k.WarePositionId})將會(huì)出錯(cuò)。剛開始自己也犯了這個(gè)錯(cuò)誤,字段順序搞錯(cuò),結(jié)果調(diào)試了很久才解決問題。
用HasRequired(o?=>?o.PositionStock).WithMany(o?=>?o.SaleOutDetails)在配置一對(duì)多(一個(gè)貨位庫(kù)存對(duì)應(yīng)多個(gè)銷售出庫(kù)明細(xì))的關(guān)系時(shí),刪除數(shù)據(jù)時(shí)會(huì)出現(xiàn)級(jí)聯(lián)刪除的現(xiàn)象,上面刪除貨位對(duì)應(yīng)的銷售出庫(kù)明細(xì)記錄也將被刪除,設(shè)置WillCascadeOnDelete(false)就不會(huì)級(jí)聯(lián)刪除了。當(dāng)然也可以用HasOptional來替代HasRequired防止級(jí)聯(lián)刪除,但這樣表之間的約束已經(jīng)變了,所以得依據(jù)實(shí)際情況做選擇。
當(dāng)你配置表復(fù)雜關(guān)系都沒問題的話,簡(jiǎn)單關(guān)系像一對(duì)一,多對(duì)多就變得簡(jiǎn)單了,其他就不講了,可以下載源代碼去看看。如果想對(duì)CodeFirst有更多的了解的話可以去看<<Programming?Entity?Framework_?Code?First>>這本書,很薄不到200頁。
?
2.DbContext
Entityframework查詢數(shù)據(jù)的三種方式:延遲加載,饑餓加載,顯式加載就不多講了,去看代碼很多地方都已體現(xiàn)。講一下Entityframework刪除和修改數(shù)據(jù)跟用linq?to?sql的不同之處吧。EF刪除和修改數(shù)據(jù)不必像linq?to?sql?那樣先得查詢出某條記錄,然后再對(duì)記錄刪除或修改。
EF刪除數(shù)據(jù)只需new一實(shí)體,實(shí)體ID跟要?jiǎng)h除數(shù)據(jù)的ID相同就是了。比如
Privte void DeleteCustomer(Customer cst){Using(var context=new JXCContext()){ context.Entry(cst).State =EntityState.Deleted;context.SaveChanges();}}EF修改數(shù)據(jù)只需new一實(shí)體,實(shí)體ID跟要修改數(shù)據(jù)的ID相同,把要修改的屬性賦值就行了。比如
Privte void ModifyCustomer(Customer cst){Using(var context=new JXCContext()){ context.Entry(cst).State =EntityState.Modified;context.SaveChanges();}}這種刪除修改數(shù)據(jù)的方法能夠減少一次數(shù)據(jù)庫(kù)訪問。具體做法以及如何實(shí)現(xiàn)實(shí)體部分更新,可以去看項(xiàng)目。如果想對(duì)EF的數(shù)據(jù)操作有更多的了解,建議看<<Programming?Entity?Framework_?DbContext>>這本書,250頁左右。
二:MVC
MVC無非就是View,Controller,模型模版,模型綁定,模型驗(yàn)證,UnobtrusiveAjax等等,挑幾個(gè)講以下.
1:Controller
在Controller類中有OnActionExecuting,OnActionExecuted,OnResultExecuting,OnResultExecuted幾個(gè)方法,分別代表控制器方法執(zhí)行前,執(zhí)行后,視圖呈現(xiàn)前,呈現(xiàn)后需要調(diào)用的方法。通常我們會(huì)把一些重復(fù)調(diào)用的代碼寫在這里,然后放到BaseCtroller類中讓子Controller使用。比如客戶端用Easyui?datagrid做一個(gè)表單然后調(diào)用Controller中的GetPageData方法呈現(xiàn)數(shù)據(jù),并且需排序分頁,通常這樣GetPageData方法這樣寫:
xxService.GetPageData('查詢條件1','查詢條件2',‘查詢條件N...’,int.Parse(request["page"])?-?1,?int.Parse(request["rows"]),request["sort"],request["order"]?==?"asc"???true?:?false,?out?recordCount);
像這些從datagrid中傳遞過來的變量我們完全可以封裝起來,放到BaseCtroller中的OnActionExecuting方法中,上代碼:
public class PageDescriptor{public int PageCount { get; set; }public int PageIndex { get; set; }public string Sort { get; set; }public bool Order { get; set; }}public class BaseController:Controller{private PageDescriptor pageDescriptor;public PageDescriptor PageDescriptor{get { return pageDescriptor; }}protected override void OnActionExecuting(ActionExecutingContext filterContext){var request = filterContext.HttpContext.Request;if (request["rows"] != null && request["page"] != null){pageDescriptor = new PageDescriptor();pageDescriptor.PageCount = int.Parse(request["rows"]);pageDescriptor.PageIndex = int.Parse(request["page"]) - 1;pageDescriptor.Order = request["order"] == "asc" ? true : false;pageDescriptor.Sort = request["sort"];}base.OnActionExecuting(filterContext);} } View Code然后Controller中的GetPageData方法可以這樣寫:xxxService.GetPageData('查詢條件1','查詢條件2',‘查詢條件N...’,PageDescriptor.PageIndex,PageDescriptor.PageCount,PageDescriptor.Sort,?PageDescriptor.Order,?out?recordCount);這樣寫著簡(jiǎn)單,省得出錯(cuò),可以重復(fù)利用。
2:View
視圖就講一個(gè)子Action,@Html.Action("actionname")的使用吧。比如有一個(gè)品種管理頁面,有查詢,新增,編輯品種等功能,我們可以把新增,編輯功能分別建個(gè)view,讓后相對(duì)應(yīng)寫個(gè)controller。在品種管理視圖頁面以@Html.Action("AddProduct"),@Html.Action("ModifyProduct")的方式寄宿它們的視圖,這樣省得一個(gè)頁面html元素和javascript太多,便于管理。需注意一點(diǎn)的是宿主頁面的javascript變量,html元素在子頁面中可以調(diào)用,宿主頁面生成時(shí)也包括子頁面的javascript和html,實(shí)際上它們都在同一個(gè)頁面,寫腳本的時(shí)候要防止變量沖突,不了解的可以去該項(xiàng)目"品種管理"模塊去查看。
?
3:模型驗(yàn)證
關(guān)于模型驗(yàn)證稍微講多一點(diǎn)。Mvc的模型驗(yàn)證分為服務(wù)器端驗(yàn)證和客戶端驗(yàn)證。先講服務(wù)器端的驗(yàn)證。
mvc服務(wù)器端的驗(yàn)證你需要明白模型驗(yàn)證的基本流程:首先你得知道驗(yàn)證在什么地方被觸發(fā);驗(yàn)證觸發(fā)后如何收集驗(yàn)證信息;最后如何把驗(yàn)證信息反饋到客戶端。驗(yàn)證觸發(fā)的地方有很多:設(shè)置Model或Model屬性的ValidationAttribute;或者在控制器中通過ModelState.AddModelError(key,value)這個(gè)方法顯式添加驗(yàn)證;或者讓模型實(shí)現(xiàn)IValidatableObject接口,重寫接口的IEnumerable<ValidationResult>Validate(ValidationContext?validationContext)方法來添加驗(yàn)證;驗(yàn)證觸發(fā)后所有的驗(yàn)證信息被放到一個(gè)叫ModelStateDictionary類型的ModelState屬性中,然后被MVC自動(dòng)獲取,當(dāng)然你也可以手動(dòng)去遍歷該詞典獲取驗(yàn)證信息(后面會(huì)講到);最后MVC通過視圖引擎將驗(yàn)證信息顯示到客戶端,當(dāng)然你也可以寫點(diǎn)腳本手動(dòng)去實(shí)現(xiàn)(后面會(huì)講到)。明白該流程后你才能做到胸有成竹。
Mvc客戶端的驗(yàn)證其實(shí)完全依賴于jquery.validate和jquery.validate.unobtrusive這兩個(gè)腳本文件,在Asp.net中你也可以這么做。
這兩個(gè)腳本文件中有許多驗(yàn)證規(guī)則,你只需把驗(yàn)證規(guī)則添加到html元素的標(biāo)簽中就可以進(jìn)行客戶端的驗(yàn)證,感覺跟Easyui的驗(yàn)證很相似,只不過MVC能夠自動(dòng)去添加驗(yàn)證規(guī)則。比如一個(gè)Textbox:
<input?class="text-box?single-line"?data-val="true"?data-val-length="只能3到10個(gè)字符"?data-val-length-max="10"?data-val-length-min="3"?data-val-required="必填"??id="name"?name="name"?type="text"?value=""?/>?其中以data-val開頭的標(biāo)簽說明它要在客戶端驗(yàn)證,length和required為驗(yàn)證規(guī)則,max?,min為length規(guī)則的參數(shù)。你可以手動(dòng)去添加這些驗(yàn)證規(guī)則,也可以讓Mvc自動(dòng)添加這些規(guī)則,如果要mvc自動(dòng)添加客戶端驗(yàn)證規(guī)則,你得自定義一個(gè)ValidationAttribute,并且實(shí)現(xiàn)IClientValidatale接口的IEnumerable<ModelClientValidationRule>?GetClientValidationRules(ModelMetadata?metadata,?ControllerContext?context)方法,再把該Atrribute放到model屬性上,這樣mvc在解析model時(shí)會(huì)通過視圖引擎將驗(yàn)證規(guī)則自動(dòng)添加到html元素標(biāo)簽中.實(shí)現(xiàn)該接口的目的而且是唯一的目的就在于此。如果你想對(duì)自定義客戶端和服務(wù)器端驗(yàn)證有更多的了解,建議看下園子里的這篇文章:http://www.cnblogs.com/artech/archive/2012/05/15/custom-client-validation.html。
不過mvc的驗(yàn)證也存在一個(gè)缺陷,就是你必須單擊頁面的一個(gè)submit按鈕提交一個(gè)表單后才能觸發(fā)客戶端的驗(yàn)證,客戶端驗(yàn)證通過后再到服務(wù)器端解析模型然后進(jìn)行服務(wù)器端的驗(yàn)證。(之所以這樣是因?yàn)樘峤籪orm表單時(shí)視圖引擎有一個(gè)反饋驗(yàn)證信息的動(dòng)作)如果你想隨便單擊一個(gè)button按鈕以ajax的方式調(diào)用控制器,而不是依賴單擊submit按鈕進(jìn)行客戶端和服務(wù)器端驗(yàn)證該如何實(shí)現(xiàn)呢?
其實(shí)也挺簡(jiǎn)單的,客戶端的驗(yàn)證你只需用腳本顯式調(diào)用('#form').valid()這個(gè)方法就可以了,驗(yàn)證通過返回true,否則false。驗(yàn)證通過后你再調(diào)用controller方法進(jìn)行服務(wù)器端的驗(yàn)證。在controller中你需收集驗(yàn)證信息然后以json數(shù)據(jù)格式返回到客戶端,然后用腳本把驗(yàn)證信息顯示出來就行了。舉個(gè)例子,在項(xiàng)目品種管理頁面添加品種時(shí)候,單擊提交按鈕觸發(fā)客戶端驗(yàn)證,通過后繼續(xù)服務(wù)器端驗(yàn)證,如圖
先在BaseCtroller添加獲取驗(yàn)證信息的方法:
protected virtual List<object> GetErrorMessages(){List<object> errors = new List<object>();foreach (var key in ModelState.Keys){if (ModelState[key].Errors.Count > 0){var obj = new { key = key, errorMessage = ModelState[key].Errors.Select(o =>o.ErrorMessage).First() };errors.Add(obj);}} return errors;} View Code然后在controller中調(diào)用
?
[HttpPost]public ActionResult AddProduct(ProductDTO product){if (ModelState.IsValid){productService.AddProduct(product);return Json(null);}elsereturn Json(GetErrorMessages(), "text/html", JsonRequestBehavior.AllowGet);}?
客戶端再寫相關(guān)的腳本:
function addProduct() {$('#formAdd').form('submit', {onSubmit: function (param) {if ($('#formAdd').valid()) {param.ProductCategoryId = $('#ProductCategoryId').combotree('getValue');$('#btnAddProduct').linkbutton('disable');}},success: function (data) {$('#btnAddProduct').linkbutton('enable');if (data.length > 0)ShowValidateMessage($.parseJSON(data));else$.messager.alert("消息", "操作成功!");}})}//顯示驗(yàn)證信息function ShowValidateMessage(data) {for (var i = 0; i < data.length; i++) {var $span = $('#tableAdd').find('span[data-valmsg-for=' + data[i].key + ']');$span.css('color','red').show().text(data[i].errorMessage);}} View Code這樣就完成了客戶端和服務(wù)器端的驗(yàn)證了。不過用jquery1.6,$('#formAdd').valid()永遠(yuǎn)返回true,用jqeury1.5.1才能正確驗(yàn)證,不知道大家有木有遇到這種情況。
?
4模型綁定
mvc模型綁定無非就是把數(shù)據(jù)從客戶端傳遞到服務(wù)器端,它默認(rèn)是通過DefaultModelBinder類來操作的,當(dāng)然你也也可自定義一個(gè)ModelBinder。
當(dāng)你調(diào)用一個(gè)action時(shí)需要從客戶端傳遞一個(gè)name參數(shù),mvc默認(rèn)按Request.Form["name"],RouteData.Values["name"],Request.QueryString["name"]順序來搜索name的值,當(dāng)然你也可以通過Json數(shù)據(jù)格式把name傳遞給action,以ajax方式調(diào)用action時(shí)經(jīng)常會(huì)這么做。
模型綁定有簡(jiǎn)單類型,集合類型,復(fù)雜類型的綁定。當(dāng)你把一個(gè)復(fù)雜類型嵌套復(fù)雜類型的綁定弄明白后,其他的就簡(jiǎn)單了。比如項(xiàng)目客戶管理在新增客戶時(shí):
單擊提交按鈕,調(diào)用控制器中AddCustomer(Customer?customer)?方法時(shí),?該如何編寫一個(gè)customer的json數(shù)據(jù)把頁面數(shù)據(jù)傳遞給customer呢?
public class Customer{public string ID { get; set; }public string SimpleID { get; set; }public string Name { get; set; }public string Address { get; set; }public string Telephone { get; set; }public string Zip { get; set; }public string CstType { get; set; }public virtual IList<CustomerAddress> CustomerAddresses { get; set; }} }public class CustomerAddress{public System.Guid ID { get; set; }public string Reciever { get; set; }public string Address { get; set; }public string ZipCode { get; set; }public string Telephone { get; set; }public string CustomerId { get; set; }public virtual Customer Customer { get; set; }}客戶端腳本:
function addCustomer() {var data = $('#addressTable').datagrid('getData').rows;var cst = {};//關(guān)鍵代碼在此for (var i = 0; i < data.length; i++) {cst['CustomerAddresses[' + i + '].Reciever'] = data[i].Reciever;cst['CustomerAddresses[' + i + '].Address'] = data[i].Address;cst['CustomerAddresses[' + i + '].Telephone'] = data[i].Telephone;cst['CustomerAddresses[' + i + '].ZipCode'] = data[i].ZipCode;}cst.SimpleID = $('#SimpleID').val();cst.Name = $('#Name').val();cst.Address = $('#Address').val();cst.Telephone = $('#Telephone').val();cst.Zip = $('#Zip').val();cst.CstType = $('#CstType').val();$.ajax({url: '@Url.Action("AddCustomer")',type: 'POST',data: cst,})} View Code其實(shí)客戶端只需定義一個(gè)對(duì)象,對(duì)象的簡(jiǎn)單屬性只要跟Customer的屬性同名,對(duì)象中的對(duì)象也可以說是數(shù)組對(duì)應(yīng)Customer的IList<CustomerAddress>,名稱也必須為CustomerAddresses,對(duì)象中的對(duì)象的屬性對(duì)應(yīng)CustomerAddress的屬性,這樣一個(gè)嵌套復(fù)雜類型的復(fù)雜類型就成功傳遞到了服務(wù)器端。
?
三:Easyui
Javascript不是本人的強(qiáng)項(xiàng),Easyui也算不上精通,但熟練使用還是沒問題。只要你把Easyui最麻煩的幾個(gè)組件弄明白,我估摸著其它的看下文檔就會(huì)了,就講下datagrid和combotree吧。
1:datagrid
比如客戶管理頁面,查詢客戶數(shù)據(jù)時(shí),如圖:
該dagagrid能排序分頁,多選,單擊行+號(hào)能查看客戶收貨地址數(shù)據(jù),具體實(shí)現(xiàn)如下:
$('#grid').datagrid({title: '客戶信息',width: 1000,url: '@Url.Action("GetPageData")',collapsible: true,pagination: true,view: detailview,detailFormatter: function (index, row) {return '<div style="padding:2px"><table id="ddv"></table></div>';},queryParams: {cstType: $('#ct').combotree('getValue'),cstName: $('#searchName').val()},columns: [[{ field: 'ck', checkbox: true },{ field: 'ID', title: 'ID', hidden: true },{ field: 'Name', title: '名稱', sortable: true },{ field: 'CstType', title: '客戶類型', sortable: true },{ field: 'Telephone', title: '電話', sortable: true },{ field: 'Zip', title: '郵編', sortable: true },{ field: 'Address', title: '地址', width: 445, sortable: true }]],toolbar: [{ id: 'btnRemove', text: '刪除客戶', iconCls: 'icon-remove', handler: function () {deleteCustomer();}},{ id: 'btnEdit', text: '修改客戶', iconCls: 'icon-edit', handler: function () {showEditDialog();}}],onExpandRow: function (index, row) {var ddv = $(this).datagrid('getRowDetail', index).find('#ddv');ddv.datagrid({url: '@Url.Action("GetCustomerAddress")',queryParams: { cstId: row.ID },fitColumns: true,singleSelect: true,height:'auto',rownumbers: true,loadMsg: '正在加載客戶地址......',columns: [[{ field: 'Reciever', title: '收貨人', width: 80 },{ field: 'Telephone', title: '電話', width: 100 },{ field: 'ZipCode', title: '郵編', width: 80 },{ field: 'Address', title: '地址', width: 250 }]],onResize: function () {$('#grid').datagrid('fixDetailRowHeight', index);},onLoadSuccess: function () {setTimeout(function () {$('#grid').datagrid('fixDetailRowHeight', index);}, 0);}})$('#grid').datagrid('fixDetailRowHeight', index);}}) View Code注意兩點(diǎn):1:顯示明細(xì)功能需引入datagrid-detailview.js腳本;2:服務(wù)器端數(shù)據(jù)格式得是{total=dataCount,rows=data}這樣的object類型,便于datagrid解析數(shù)據(jù)。
2:Combotree
比如這樣一個(gè)品種分類,如何實(shí)現(xiàn)呢?
?
客戶端腳本就一行代碼$('#tree').combotree({url:'@Url.Action('actionName','controller')'}).關(guān)鍵是構(gòu)建服務(wù)器端的數(shù)據(jù)格式。你得按照[{id:'',name:'',children:[{id:'',name:'',children:[{......}]}]}]去構(gòu)造數(shù)據(jù)。很多人喜歡用拼接字符串的方式去構(gòu)造數(shù)據(jù),這樣行,但感覺代碼有點(diǎn)亂,不好控制,尤其是遞歸太多的時(shí)候。其實(shí)可以用類型對(duì)象,把數(shù)據(jù)賦值給對(duì)象屬性,好控制好遞歸。上圖服務(wù)器端具體實(shí)現(xiàn)如下:
public class TreeDescriptor{public string Id { get; set; }public string Text { get; set; }public string State { get; set; }public List<object> Children { get; set; }}private List<object> AddTopCategory(){var data = productCategoryService.GetProductCategoriesByParentId(null);var nodes = new List<object>();foreach (var o in data){TreeDescriptor tree = new TreeDescriptor();tree.Id = o.ID;tree.Text = o.CategoryName;tree.Children = AddChildrenCategory(o.ID);nodes.Add(new { id = tree.Id, text = tree.Text, children = tree.Children });}return nodes;}private List<object> AddChildrenCategory(string parentId){var data = productCategoryService.GetProductCategoriesByParentId(parentId);var nodes = new List<object>();foreach (var o in data){TreeDescriptor tree = new TreeDescriptor();tree.Id = o.ID;tree.Text = o.CategoryName;tree.Children = AddChildrenCategory(o.ID);nodes.Add(new { id = tree.Id, text = tree.Text, children = tree.Children });}return nodes;} //客戶端調(diào)需調(diào)用的方法public JsonResult CreateCategoryTree(){return Json(AddTopCategory());} View Code這樣就可以構(gòu)建能夠遞歸數(shù)據(jù)的combotree了。
其他的像彈出層,模態(tài)窗口,動(dòng)態(tài)構(gòu)建datagrid在這里就不多講了,有興趣的朋友可以去下載項(xiàng)目看看。
?
最后,寫這篇文章的目的不在于項(xiàng)目本身,而在跟大家分享技術(shù),由于時(shí)間有限,庫(kù)存和銷售模塊業(yè)務(wù)邏輯和客戶端的具體實(shí)現(xiàn)沒有去做,有前面的幾個(gè)模塊就足夠展示這些技術(shù)的運(yùn)用了,希望大家見諒。源碼:http://files.cnblogs.com/files/chenlinzhi/JXCProject.zip
轉(zhuǎn)載于:https://www.cnblogs.com/chenlinzhi/p/4332628.html
總結(jié)
以上是生活随笔為你收集整理的新年奉献MVC+EF(CodeFirst)+Easyui医药MIS系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Akka DEMO
- 下一篇: 祝贺自己操作系统JAVA项目有进展!!