<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      MVC3+EF4.1學習系列(十)----MVC+EF處理樹形結構

      文章索引和簡介

      通過前幾篇文章 我們處理了 一對一, 一對多,多對多關系 很好的發揮了ORM框架的做用 但是 少說了一種 樹形結構的處理, 而這種樹形關系 我們也經常遇到,常見的N級類別的處理, 以及經常有數據與類別掛鉤。今天主要寫下EF處理樹形結構以及 MVC如何展示樹形結構。 前面幾篇的例子 一直用的是一個例子,內容是連貫的。這篇是完全單獨的~

      先來說下工作中會遇到的常見場景 針對這幾個場景來處理~

      1.類別

      a.類別可以有無限級別

      b.類別的最末端 不確定是第幾級 某個節點 可以到二級 其他的節點 有可能到四級

      c.tree型展示整個類別 并可以對tree進行CRUD   (可以一次遞歸全部加載  也可以異步加載 )

      d.面包屑型展示類別

      e.刪除父類 應把下面所有的子類刪除

      2.與類別掛鉤的數據 (本文是文章)

      a. 可以根據任意級別的類別 查看文章

      b. 合并兩個類別的文章

      上面這些場景 基本覆蓋了類別操作的常見情況 如果大家覺得還有什么要處理 可以給我說 我補充上去~~

      下面開始講解~

      一.準備工作

      1.如何建立類別實體類 來展示樹形結構

      上代碼

       /// <summary>
      /// 類別
      /// </summary>
      public class Category
      {
      /// <summary>
      /// 主鍵
      /// </summary>
      public int CategoryId { get; set; }

      /// <summary>
      /// 類別名字
      /// </summary>
      [Required()]
      [StringLength(
      5)]
      public string CategoryName { get; set; }

      /// <summary>
      /// 父ID
      /// </summary>
      public Nullable<int> ParentId { get; set; }

      /// <summary>
      /// 上面的父節點
      /// </summary>
      public virtual Category Parent { get; set; }

      /// <summary>
      /// 下面的子節點
      /// </summary>
      [ForeignKey("ParentId")]
      public virtual ICollection<Category> ChildKeys { get; set; }

      /// <summary>
      /// 該類別的文章集合
      /// </summary>
      public virtual ICollection<Article> articleList { get; set; }

      /// <summary>
      /// 編號
      /// </summary>
      public string Note { get; set; }


      /// <summary>
      /// 狀態
      /// </summary>
      public string State
      {
      get;
      set;
      }

      /// <summary>
      /// 級別
      /// </summary>
      public Nullable<int> Lev
      {
      get;
      set;
      }
      /// <summary>
      /// 排序
      /// </summary>
      public int Sort
      {
      get;
      set;
      }
      }

      這樣的設計 很好的展示了樹形結構 一個節點有一個父類 多個子類 一個類別可以有多個文章 這里說下 后面的四個屬性 不是必要的~

      2.文章實體類

      上代碼 這個比較好理解 不解釋了~

      文章實體類
      /// <summary>
      /// 文章
      /// </summary>
      public class Article
      {
      /// <summary>
      /// 文章ID
      /// </summary>
      public int ArticleId { get; set; }

      /// <summary>
      /// 文章名
      /// </summary>
      public string ArticleName { get; set; }

      /// <summary>
      /// 創建時間
      /// </summary>
      public DateTime CreateTime { get; set; }

      /// <summary>
      /// 文章順序
      /// </summary>
      public int Sort { get; set; }

      /// <summary>
      /// 類別ID
      /// </summary>
      public int CategoryId { get; set; }

      /// <summary>
      /// 類別
      /// </summary>
      [ForeignKey("CategoryId")]
      public Category Category { get; set; }
      }

      3.建立Context

      上代碼

      public class TreeDemoContext : DbContext
      {

      private readonly static string CONNECTION_STRING = "name=WlfSys_EFCF_ConnString";

      public DbSet<Category> Category { get; set; }
      public DbSet<Article> Article { get; set; }

      public TreeDemoContext()
      :
      base(CONNECTION_STRING)
      {
      // this.Configuration.ProxyCreationEnabled = false;
      }


      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
      modelBuilder.Conventions.Remove
      <PluralizingTableNameConvention>();//移除復數表名的契約
      }
      }

      這里不需要使用Fluent API 來映射實體類與數據庫的關系 里面也沒什么亮點( 其實我一直想知道 怎么用Fluent API 映射 來解決下面的問題 有知道的高人指點下~~ 感激 )

      4.數據庫初始化

      這是我這種方法 用ef處理樹形結構最關鍵的一點 熟練使用ef的人 看了我上面的類別實體類的建立 就會發現這是錯誤的 因為這會造成自引用 看下圖生成的數據庫表結構

      由于自引用 插入時會出現 INSERT 語句與 FOREIGN KEY SAME TABLE 約束"Category_ChildKeys"沖突。

      我的解決辦法是 在初始化數據庫時 刪除這個外鍵約束 上代碼

       public class TreeDemoInitializer : DropCreateDatabaseIfModelChanges<TreeDemoContext>
      {
      protected override void Seed(TreeDemoContext context)
      {
      //刪除關聯
      context.Database.ExecuteSqlCommand("ALTER TABLE [dbo].[Category] DROP CONSTRAINT [Category_ChildKeys]");


      //必須加上ID
      var Category = new List<Category>
      {
      new Category{ CategoryId=1, CategoryName="亞洲", Lev=1, ParentId=0, Note="001",ChildKeys=new List<Category>{
      new Category{CategoryId=2,CategoryName="中國",Lev=2,Note="00101",ChildKeys=new List<Category>{ new Category{CategoryId=6, CategoryName="河南", Lev=3, Note="0010101" },new Category{CategoryId=7, CategoryName="廣州", Lev=3, Note="0010102" } }},
      new Category{CategoryId=3,CategoryName="日本",Lev=2,Note="00102",ChildKeys=new List<Category>{ new Category{CategoryId=8, CategoryName="日本省1", Lev=3, Note="0010201" },new Category{CategoryId=9, CategoryName="日本省2", Lev=3, Note="0010202" } }
      } }
      },
      new Category { CategoryId=4, CategoryName="歐洲", Lev=1, ParentId=0, Note="002", ChildKeys=new List<Category>{
      new Category{CategoryId=5,CategoryName="荷蘭",Lev=2,Note="00201"}
      } }

      };
      Category.ForEach(c
      => context.Category.Add(c));



      var Articles
      = new List<Article>{

      new Article{ ArticleName="小說13", CreateTime=DateTime.Now, CategoryId=5},
      new Article{ ArticleName="小說14", CreateTime=DateTime.Now, CategoryId=5},
      new Article{ ArticleName="小說15", CreateTime=DateTime.Now, CategoryId=5},
      new Article{ ArticleName="小說1", CreateTime=DateTime.Now, CategoryId=6},
      new Article{ ArticleName="小說2", CreateTime=DateTime.Now, CategoryId=6},
      new Article{ ArticleName="小說3", CreateTime=DateTime.Now, CategoryId=6},
      new Article{ ArticleName="小說4", CreateTime=DateTime.Now, CategoryId=7},
      new Article{ ArticleName="小說5", CreateTime=DateTime.Now, CategoryId=7},
      new Article{ ArticleName="小說6", CreateTime=DateTime.Now, CategoryId=7},
      new Article{ ArticleName="小說7", CreateTime=DateTime.Now, CategoryId=8},
      new Article{ ArticleName="小說8", CreateTime=DateTime.Now, CategoryId=8},
      new Article{ ArticleName="小說9", CreateTime=DateTime.Now, CategoryId=8},
      new Article{ ArticleName="小說10", CreateTime=DateTime.Now, CategoryId=9},
      new Article{ ArticleName="小說11", CreateTime=DateTime.Now, CategoryId=9},
      new Article{ ArticleName="小說12", CreateTime=DateTime.Now, CategoryId=9}

      };
      Articles.ForEach(a
      => context.Article.Add(a));

      context.SaveChanges();
      }
      }

      并初始化一些數據進去 這個初始化 就算是類別的添加了 在這添加時 遇到個小問題 我們的數據庫類別ID默認是自增長的 按理說不用指定主鍵ID 但是不指定ID 像我上面 一下次插入多條時 插入時卻報錯 ~~ 無法確定“ContosoUniversity.DAL.Category_ChildKeys”關系的主體端。添加的多個實體可能主鍵相同。指定了ID 才解決了這個問題

      5. 搭建基本項目結構

      依然使用 unit of work +Repository ( 項目大的話 加入Iservice, Service 再加上IOC,這里只是個簡單的demo) 如圖

      二.關于類別的操作以及展示

      1.tree型展示整個類別

      在webfrom時代 實現tree展示很容易 因為我們有犀利的控件 treeview 用treeview控件 再加個遞歸綁定 就很簡單的完成了 這是webfrom的好處 但也是不好的地方 比如 treeview生成出來的 是 table 嵌套table的 我如果想換成ul li怎么辦 控件開發 造成了耦合度過高 題外話說多了 回歸正題~

      這里 我用兩種方法實現 treeview的展示

      A方法 擴展一個 HtmlHelper

      實現 HTML.Tree(類別) 就能展示出treeview

      用這個方法前 再說幾句 不喜歡這個方法 因為這有點像用控件了 代碼與視圖依然在一起 第二 這里我使用了遞歸, 但是小項目的話 沒什么問題 直接上code

          public static MvcHtmlString Tree(this HtmlHelper html, Category treeModel)
      {
      return BindTree(treeModel);
      }

      private static MvcHtmlString BindTree(Category treeModel)
      {
      StringBuilder sb
      = new StringBuilder();
      if (treeModel != null)
      {
      sb.Append(
      "<ul>");

      List
      <Category> list = treeModel.ChildKeys.ToList();
      foreach (var item in list)
      {
      sb.Append(
      "<li>");
      sb.Append(item.CategoryName);
      sb.Append(
      "</li>");
      sb.Append(BindTree(item));
      }
      sb.Append(
      "</ul>");

      }
      MvcHtmlString mstr
      = new MvcHtmlString(sb.ToString());
      return mstr;
      }

      上面實現了最最簡單的展示樹形結構 無非就是遞歸的運用 這個可以擴展 是否展示 checkbox 啊 上來默認展示幾級啊 后面的增刪改連接啊 and so on~~

      b. 利用ajax 實現異步加載 ( 個人喜歡的方法 ) 先上一個實現后的圖 我沒做任何美工 樣子很難看~ 大家將就看下

      前面有小箭頭表示可以打開~~ 打開后 變成打開的狀態~

      下面上視圖 解釋和思路 直接加在里面了

      <script type="text/javascript">
      $(
      function () {
      var clickLi
      = function () {
      $(
      this).children("ul").toggle(); // 切換隱藏和顯示li下面的ul


      //切換img圖標是 選中還是未選中
      if ($(this).children("img").attr("src") == "http://www.rzrgm.cn/Content/img/selectNode.jpg") {

      $(
      this).children("img").attr("src", "http://www.rzrgm.cn/Content/img/noselectNode.jpg");
      }
      else {
      $(
      this).children("img").attr("src", "http://www.rzrgm.cn/Content/img/selectNode.jpg");
      }


      //什么時候發送加載下面節點的請求呢?
      //在img 屬性不為空 證明下面有節點 因為沒有節點是不會有img 樹形的 并且他的下面的ul個數為0
      if ($(this).children("img").attr("src") != undefined && $(this).children("ul").length == 0) {
      var cid
      = $(this).attr("id");
      var li
      = $(this);
      $.post(
      "GetCategoryById", { id: cid }, function (data) {

      if (data == "-1") {
      alert(
      "失敗");
      }
      else {
      li.append(data);
      }
      });
      }
      return false;
      }

      //為什么用 live 不是直接click?

      //因為 ajax請求加載的 click事件是不管用的 要用live才可以 ~~切記
      $("#CategoryTree li").live("click", null, clickLi);
      }
      );

      </script>

      <h2>Index</h2>

      <p>
      @Html.ActionLink(
      "Create New", "Create")
      </p>

      @
      *@Html.Tree(Model);*@

      <ul id="CategoryTree">
      @foreach (var item
      in Model.ChildKeys)
      {
      <li id="@item.CategoryId">
      @if(item.ChildKeys.Count
      > 0)
      {
      <img src="http://www.rzrgm.cn/Content/img/noselectNode.jpg" />
      }
      <span class="name">@item.CategoryName</span>
      @Html.ActionLink(
      "添加", "Create", new { id=@item.CategoryId})
      @Html.ActionLink(
      "修改", "Edit", new { id=@item.CategoryId})
      </li>
      }
      </ul>

      這里說下jquery ajax請求時 我們經常返回json 然后來構建 這里說下mvc另一種方法 返回一個部分視圖~~ 我很喜歡這種方法 上code~ 不解釋啦~

      ajax返回部分視圖代碼
      [HttpPost]
      public ActionResult GetCategoryById(int id)
      {
      if (Request.IsAjaxRequest())
      {
      Category CategoryModel
      = unitOfWOrk.CategoryRepository.GetTEntityByID(id);
      if (CategoryModel != null)
      {
      return PartialView(CategoryModel);
      }
      else
      {
      return Content("-1");
      }
      }
      else
      {
      return Content("-1");
      }
      }

      部分視圖的視圖
      @model TreeDemoCore.Model.Category
      <ul>
      @foreach (var childitem in Model.ChildKeys)
      {
      <li id=@childitem.CategoryId>
      @if (childitem.ChildKeys.Count > 0)
      {
      <img src="http://www.rzrgm.cn/Content/img/noselectNode.jpg" />
      }
      <span class="name">@childitem.CategoryName </span>
      @Html.ActionLink("添加", "Create")
      @Html.ActionLink("修改", "Edit")

      </li>
      }
      </ul>

      2.展示面包屑

      樹形結構的展示 我們經常遇到treeview型的 還會遇到另一種 面包屑這樣的 如

      我們這里要做的就是 根據當前類別 向上一層層推到最上面 并把最后一個加粗顯示~ 我的思路是這樣的 比如當前所在的類別 為最小的  廣州   根據這個 一層層推到最上面 通過遞歸得到 廣州>中國>亞洲 在通過反轉字符串 并給加粗就行了~~ 代碼如下

          /// <summary>
      /// 根據當前類別建造面包屑
      /// </summary>
      /// <returns></returns>
      public static MvcHtmlString Menu(this HtmlHelper html, Category treeModel)
      {

      return new MvcHtmlString(MenuReverse(BindMenu(treeModel)));
      }

      /// <summary>
      /// 遞歸調用 得到 廣州>中國>亞洲
      /// </summary>
      /// <returns></returns>
      private static string BindMenu(Category Model)
      {
      StringBuilder sb
      = new StringBuilder();
      sb.Append(Model.CategoryName);
      if (Model.Parent != null)
      {
      sb.Append(
      ">");
      sb.Append(BindMenu(Model.Parent));
      }
      return sb.ToString();
      }

      /// <summary>
      /// 反轉字符串 并給最后一個加上黑體字標簽
      /// </summary>
      /// <returns></returns>
      private static string MenuReverse(string menu)
      {
      return string.Join(">", menu.Split('>').Select((s, i) => i == 0 ? string.Format("<strong>{0}</Strong>", s) : s).Reverse().ToArray());
      }

      3.刪除父類要把下面的子類全部刪除

      這里就涉及到一個樹形結構的重要方法 通過當前類 得到該類的所有子類子子類等的ID集合 這時刪除時用 delete in(子類集合 ) 就行了 ~~ 忘了 怎么直接執行SQL語句的~~ 去看上一篇文章.. 通過現在類 獲得下面子類集合的方法 如下

        /// <summary>
      /// 獲得父類下所有子類的集合
      /// </summary>
      /// <returns></returns>
      private List<int> GetCidbyPid(int pid)
      {
      List
      <int> cidList = new List<int>();
      Category CategoryModel
      = unitOfWork.CategoryRepository.GetTEntityByID(pid);
      foreach (var item in CategoryModel.ChildKeys)
      {
      cidList.Add(item.CategoryId);
      }

      foreach (var item in CategoryModel.ChildKeys)
      {
      cidList.AddRange(GetCidbyPid(item.CategoryId));
      }
      return cidList;

      // 獲得 1,2,3,4,5 tring strcid= string.Join(",", cidList);
      }

      依然是通過遞歸 獲得所有的 子節點集合   再通過string.Join(",", cidList) 得到 delete in ( ) 里需要的的 就行了~~


      三.與類別掛鉤的數據的展示

      一. 可以根據任意級別的類別查看文章

      依然提供兩個方法~

      1.根據類別 查詢文章 利用遞歸 

      不過這個效率就太糾結了~~

       /// <summary>
      /// 通過任意類別 獲得下面的全部文章
      /// </summary>
      /// <param name="cid"></param>
      /// <returns></returns>
      private List<Article> GetArticleByCid(int cid)
      {
      List
      <Article> ArticleList=new List<Article>();
      Category CategoryModel
      =unitOfWork.CategoryRepository.GetTEntityByID(cid);
      if (CategoryModel.ChildKeys.Count == 0)
      {
      ArticleList.AddRange(CategoryModel.articleList.ToList());
      }

      foreach (var item in CategoryModel.ChildKeys)
      {
      ArticleList.AddRange(GetArticleByCid(item.CategoryId));
      }
      return ArticleList;

      }

      2. 利用前面說到的 通過當前類 得到該類的所有子類子子類等的ID集合  再查詢的時候  IN 就行了

      這里有個小知識~ 用linq 執行 sql in的操作~  用 Contains就行了  代碼如下

          public ActionResult Index()
      {
      //準備測試數據 測試不同情況 GetCidbyPid為根據ID獲得所有子類以及子子類等的集合
      //測試最高級
      List<int> CidList = GetCidbyPid(1);

      // var CidList = GetCidbyPid(2);

      //測試最低級
      //var CidList = GetCidbyPid(6);

      // 貪婪加載類別 為了顯示類別名字~
      var ArticleList = unitOfWork.ArticleRepository.Get(c => CidList.Contains(c.CategoryId), includeProperties: "Category");
      return View(ArticleList);
      }

      二.合并兩個類別的文章

      這個很簡單啦~  合并兩個類  就是把一個類下的文章id  都變成另一個   也就是說批量操作  不要用EF 的一個個更新就行  太慢了~還要發送多條更新語句 

      用context.Database.SqlQuery 直接執行 update  這個就不寫代碼啦~

      .通過第三方工具Telerik更加酷炫的展示tree

      我上面寫的東西 都是小打小鬧 自娛自樂的玩下 有很多地方不完善 不合理,而MVC實現Tree 第三方工具已經有了幫我們做的非常優秀的了 

      這里給大家推薦個開源的  是Telerik的 tree------介紹與連接

      把該有的操作基本全部都封裝在了里面啦~非常方便  而且里面還有很多其他的控件~

      還有,推薦大家看看源碼  第三方工具一定要多看看實現  不要只會用

      五.總結

      這篇是完全獨立的  和前面幾篇沒什么關系~ 代碼貼的都是核心片段~ 聰明的大家看看就能明白了 而且應該有更好的實現

      希望大家分享下EF MVC處理 tree的經驗以及遇到的問題~~

      我也提個問題 因為tree結構的操作 很多都用到了遞歸  在EF  高并發 大數據量處理時  我覺得會出現一些問題 希望大家說說怎么解決

      總體內容沒有太多難度~ 大家自己敲敲練練吧 

      實在太懶的同學 留個郵件  我把demo發給你們~

      (ps:文章寫在自己的22歲生日, 學習.net3年多了 希望自己通過不斷的學習 再次進步~ 也希望在園子里 得到更多的人的指點與幫助  祝福自己! )

      posted on 2011-09-05 09:27  wlf  閱讀(18752)  評論(123)    收藏  舉報

      主站蜘蛛池模板: 免费观看添你到高潮视频| 东京热人妻丝袜无码AV一二三区观| 极品尤物一区二区三区| 欧美丰满熟妇性xxxx| 精品偷自拍另类精品在线| 干老熟女干老穴干老女人| 精品粉嫩国产一区二区三区| 色综合天天色综合久久网| 免青青草免费观看视频在线| 国产91午夜福利精品| 91老肥熟女九色老女人| 伊人久久大香线蕉网av| 在线看国产精品自拍内射| 日韩亚洲国产激情一区二区| 国产成人高清精品免费软件| 国产成人精品无码专区| 国产激情福利短视频在线| 高清无码18| 亚洲人成亚洲人成在线观看| 亚洲无线码中文字幕在线| 亚洲国产成人无码av在线影院| 日韩午夜无码精品试看| 天堂V亚洲国产V第一次| 韩日午夜在线资源一区二区| 国产亚洲精品综合一区二区| 一道本AV免费不卡播放| 国产精品多p对白交换绿帽| 国产在线国偷精品免费看| 无码日韩做暖暖大全免费不卡| 长海县| 永久免费在线观看蜜桃视频| 东京热人妻丝袜无码AV一二三区观| 金湖县| 99热门精品一区二区三区无码| 久久精品国产亚洲AV麻豆长发| 亚洲欧洲日韩国内高清| 俄罗斯老熟妇性爽xxxx| 野花在线观看免费观看高清| 国产免费一区二区三区在线观看 | 亚洲香蕉网久久综合影视| 亚洲中文字幕第一页在线|