.NET MVC4 實訓記錄之四(Unit of work + Repository)


  今日后開啟進階模式!

  談到MVC與EntityFramework,則不得不說一說事務與倉儲(Unit of work + Repository)。

  倉儲(Repository):領域對象集合。用於操作領域對象與數據庫上下文(DbContext)的交互(在此不得不說一聲,領域對象和數據庫表對象還是有區別的。領域對象實際上是一組有業務關系的數據庫對象的抽象。最簡單的形式就是主表、關系表在同一個領域對象中進行定義。例如我們前幾章看到的UserProfile,它即定義了用戶信息,又定義了用戶角色關系信息)。

  事務(Transaction):多個業務處理有必然的順序性、依賴性,則默認這些業務為一個原子操作。在這里我們使用工作單元,即Unit of work模式,來維護事務的原子性。

  首先,先讓我們構建倉儲接口。為了更好的使用EntityFramework的延遲加載,所有查詢集合的接口我們均使用IQueryable接口類型作為返回值。

 1  1     /// <summary>
 2  2     /// 基礎倉儲類型接口
 3  3     /// 采用泛型類接口定義
 4  4     /// </summary>
 5  5     /// <typeparam name="T">泛型參數</typeparam>
 6  6     public interface IRepository<T> : IDisposable where T : class
 7  7     {
 8  8         /// <summary>
 9  9         /// 返回當前表的所有記錄
10 10         /// </summary>
11 11         /// <returns>T</returns>
12 12         IQueryable<T> Entries();
13 13 
14 14         /// <summary>
15 15         /// 通過過濾條件進行查詢
16 16         /// </summary>
17 17         /// <param name="predicate">過濾條件表達式</param>
18 18         /// <returns>T</returns>
19 19         IQueryable<T> Filter(Expression<Func<T, bool>> predicate);
20 20 
21 21         /// <summary>
22 22         /// 通過過濾條件進行查詢
23 23         /// </summary>
24 24         /// <param name="predicate">過濾條件表達式</param>
25 25         /// <param name="includes">需貪婪加載的屬性名稱</param>
26 26         /// <returns>IQueryable</returns>
27 27         IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes);
28 28 
29 29         /// <summary>
30 30         /// 是否存在滿足表達式的記錄
31 31         /// </summary>
32 32         /// <param name="predicate">過濾條件表達式</param>
33 33         /// <returns>Boolean</returns>
34 34         bool Contains(Expression<Func<T, bool>> predicate);
35 35 
36 36         /// <summary>
37 37         /// 按照數據庫主鍵查詢特定的實例
38 38         /// </summary>
39 39         /// <param name="keys">主鍵列表</param>
40 40         /// <returns>T</returns>
41 41         T Single(params object[] keys);
42 42 
43 43         /// <summary>
44 44         /// 按照指定表達式查詢特定的實例
45 45         /// </summary>
46 46         /// <param name="predicate">過濾條件表達式</param>
47 47         /// <returns>T</returns>
48 48         T FirstOrDefault(Expression<Func<T, bool>> predicate);
49 49 
50 50         /// <summary>
51 51         /// 插入一條記錄
52 52         /// </summary>
53 53         /// <param name="t">新實例</param>
54 54         /// <param name="submitImmediately">是否直接提交。默認false。</param>
55 55         /// <returns>T</returns>
56 56         T Create(T t, bool submitImmediately = false);
57 57 
58 58         /// <summary>
59 59         /// 刪除一行記錄
60 60         /// </summary>
61 61         /// <param name="t">要刪除的實例</param>
62 62         /// <param name="submitImmediately">是否直接提交。默認false。</param>
63 63         void Delete(T t, bool submitImmediately = false);
64 64 
65 65         /// <summary>
66 66         /// 刪除滿足表達式的記錄
67 67         /// </summary>
68 68         /// <param name="predicate">過濾條件表達式</param>
69 69         /// <param name="submitImmediately">是否直接提交。默認false。</param>
70 70         void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false);
71 71 
72 72         /// <summary>
73 73         /// 更新一條記錄
74 74         /// </summary>
75 75         /// <param name="t">要更新的實例</param>
76 76         /// <param name="submitImmediately">是否直接提交。默認false。</param>
77 77         void Update(T t, bool submitImmediately = false);
78 78 
79 79         /// <summary>
80 80         /// 獲取當前實例的主鍵
81 81         /// </summary>
82 82         /// <param name="t">實例</param>
83 83         /// <returns>Object</returns>
84 84         object GetKeyValue(T t);
85 85     }
View Code

  接下來是實現這個接口,真正去處理數據查詢和操作的時候了。

  1  1     /// <summary>
  2   2     /// 倉儲類型定義
  3   3     /// 采用泛型類定義
  4   4     /// </summary>
  5   5     /// <typeparam name="T">泛型參數</typeparam>
  6   6     public class Repository<T> : IRepository<T> where T : class
  7   7     {
  8   8         /// <summary>
  9   9         /// 數據庫上下文
 10  10         /// </summary>
 11  11         public UsersContext Context { get; private set; }
 12  12 
 13  13         /// <summary>
 14  14         /// 當前表記錄集合
 15  15         /// </summary>
 16  16         protected DbSet<T> DbSet
 17  17         {
 18  18             get
 19  19             {
 20  20                 return Context.Set<T>();
 21  21             }
 22  22         }
 23  23 
 24  24         /// <summary>
 25  25         /// 構造器
 26  26         /// </summary>
 27  27         public Repository()
 28  28         {
 29  29             Context = new UsersContext();
 30  30         }
 31  31 
 32  32         /// <summary>
 33  33         /// 構造器
 34  34         /// </summary>
 35  35         /// <param name="connectionString">連接字符串(名稱)</param>
 36  36         public Repository(string connectionString)
 37  37         {
 38  38             Context = new UsersContext(connectionString);
 39  39         }
 40  40 
 41  41         /// <summary>
 42  42         /// 構造器
 43  43         /// </summary>
 44  44         /// <param name="context">數據庫上下文</param>
 45  45         public Repository(UsersContext context)
 46  46         {
 47  47             Context = context;
 48  48         }
 49  49         
 50  50         /// <summary>
 51  51         /// 析構器
 52  52         /// </summary>
 53  53         public void Dispose()
 54  54         {
 55  55             if (Context != null)
 56  56                 Context.Dispose();
 57  57         }
 58  58 
 59  59         /// <summary>
 60  60         /// 返回當前表的所有記錄
 61  61         /// </summary>
 62  62         /// <returns></returns>
 63  63         public IQueryable<T> Entries()
 64  64         {
 65  65             return DbSet.AsQueryable();
 66  66         }
 67  67 
 68  68         /// <summary>
 69  69         /// 通過過濾條件進行查詢
 70  70         /// </summary>
 71  71         /// <param name="predicate">過濾條件表達式</param>
 72  72         /// <returns>T</returns>
 73  73         public IQueryable<T> Filter(Expression<Func<T, bool>> predicate)
 74  74         {
 75  75             return DbSet.Where(predicate);
 76  76         }
 77  77 
 78  78         /// <summary>
 79  79         /// 通過過濾條件進行查詢
 80  80         /// </summary>
 81  81         /// <param name="predicate">過濾條件表達式</param>
 82  82         /// <param name="includes">需貪婪加載的屬性名稱</param>
 83  83         /// <returns>IQueryable</returns>
 84  84         public IQueryable<T> Filter(Expression<Func<T, bool>> predicate, params string[] includes)
 85  85         {
 86  86             var query = DbSet.Where(predicate);
 87  87             if (includes != null)
 88  88             {
 89  89                 foreach (var item in includes)
 90  90                 {
 91  91                     query = query.Include(item);
 92  92                 }
 93  93             }
 94  94             return query;
 95  95         }
 96  96 
 97  97         /// <summary>
 98  98         /// 是否存在滿足表達式的記錄
 99  99         /// </summary>
100 100         /// <param name="predicate">過濾條件表達式</param>
101 101         /// <returns>Boolean</returns>
102 102         public bool Contains(Expression<Func<T, bool>> predicate)
103 103         {
104 104             return DbSet.Count(predicate) > 0;
105 105         }
106 106 
107 107         /// <summary>
108 108         /// 按照數據庫主鍵查詢特定的實例
109 109         /// </summary>
110 110         /// <param name="keys">主鍵列表</param>
111 111         /// <returns>T</returns>
112 112         public T Single(params object[] keys)
113 113         {
114 114             return DbSet.Find(keys);
115 115         }
116 116 
117 117         /// <summary>
118 118         /// 按照指定表達式查詢特定的實例
119 119         /// </summary>
120 120         /// <param name="predicate">過濾條件表達式</param>
121 121         /// <returns>T</returns>
122 122         public T FirstOrDefault(Expression<Func<T, bool>> predicate)
123 123         {
124 124             return DbSet.FirstOrDefault(predicate);
125 125         }
126 126 
127 127         /// <summary>
128 128         /// 插入一條記錄
129 129         /// </summary>
130 130         /// <param name="t">新實例</param>
131 131         /// <param name="submitImmediately">是否直接提交。默認false。</param>
132 132         /// <returns>T</returns>
133 133         public T Create(T t, bool submitImmediately = false)
134 134         {
135 135             var newEntry = DbSet.Add(t);
136 136             if (submitImmediately)
137 137                 Context.SaveChanges();
138 138             return newEntry;
139 139         }
140 140 
141 141         /// <summary>
142 142         /// 刪除一行記錄
143 143         /// </summary>
144 144         /// <param name="t">要刪除的實例</param>
145 145         /// <param name="submitImmediately">是否直接提交。默認false。</param>
146 146         public void Delete(T t, bool submitImmediately = false)
147 147         {
148 148             DbSet.Remove(t);
149 149             if (submitImmediately)
150 150                 Context.SaveChanges();
151 151         }
152 152 
153 153         /// <summary>
154 154         /// 刪除滿足表達式的記錄
155 155         /// </summary>
156 156         /// <param name="predicate">過濾條件表達式</param>
157 157         /// <param name="submitImmediately">是否直接提交。默認false。</param>
158 158         public void Delete(Expression<Func<T, bool>> predicate, bool submitImmediately = false)
159 159         {
160 160             try
161 161             {
162 162                 Context.Configuration.AutoDetectChangesEnabled = false; //關閉數據庫上下文的自動更新跟蹤功能,可提高批量操作的性能
163 163 
164 164                 var objects = Filter(predicate);
165 165                 foreach (var obj in objects)
166 166                     DbSet.Remove(obj);
167 167                 if (submitImmediately)
168 168                     Context.SaveChanges();
169 169             }
170 170             finally
171 171             {
172 172                 Context.Configuration.AutoDetectChangesEnabled = true; //完成批量操作后,打開數據庫上下文的自動更新跟蹤功能
173 173             }
174 174         }
175 175 
176 176         /// <summary>
177 177         /// 更新一條記錄
178 178         /// </summary>
179 179         /// <param name="t">要更新的實例</param>
180 180         /// <param name="submitImmediately">是否直接提交。默認false。</param>
181 181         public void Update(T t, bool submitImmediately = false)
182 182         {
183 183             var key = GetKeyValue(t);
184 184 
185 185             var originalEntity = DbSet.Find(key);
186 186 
187 187             Context.Entry(originalEntity).CurrentValues.SetValues(t);
188 188 
189 189             if (submitImmediately)
190 190                 Context.SaveChanges();
191 191         }
192 192 
193 193         /// <summary>
194 194         /// 獲取當前實例的主鍵
195 195         /// </summary>
196 196         /// <param name="t">實例</param>
197 197         /// <returns>Object</returns>
198 198         public object GetKeyValue(T t)
199 199         {
200 200             var key =
201 201                 typeof(T).GetProperties().FirstOrDefault(
202 202                     p => p.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), true).Length != 0);
203 203             return (key != null) ? key.GetValue(t, null) : null;
204 204         }
205 205     }
View Code

  倉儲定義完成。它包含了基本的增、刪、改、查功能。無需過多的修飾,作為倉儲單元,它的任務就是這么四個操作而已。

  接下來就是我們的Unit of work的定義了。

  1 namespace Framework.Repositories
  2 {
  3     /// <summary>
  4     /// 工作單元接口定義
  5     /// </summary>
  6     public interface IUnitOfWork: IDisposable
  7     {
  8         /// <summary>
  9         /// 獲取數據庫上下文
 10         /// </summary>
 11         UsersContext DbContext { get; }
 12 
 13         /// <summary>
 14         /// 執行自定義SQL語句。
 15         /// 該方法提供了一個直接操作數據庫表的實現。
 16         /// </summary>
 17         /// <param name="commandText">SQL語句</param>
 18         /// <param name="parameters">參數列表</param>
 19         /// <returns>Integer</returns>
 20         int ExecuteSqlCommand(string commandText, params object[] parameters);
 21         
 22         /// <summary>
 23         /// 提交事務
 24         /// </summary>
 25         /// <returns>Integer</returns>
 26         int Commit();
 27 
 28         /// <summary>
 29         /// 獲取指定類型的倉儲實例
 30         /// </summary>
 31         /// <typeparam name="T">泛型參數</typeparam>
 32         /// <returns>IRepository</returns>
 33         IRepository<T> Repositry<T>() where T : class;
 34     }
 35 
 36     /// <summary>
 37     /// 工作單元實現定義
 38     /// </summary>
 39     public class UnitOfWork : IUnitOfWork
 40     {
 41         /// <summary>
 42         /// 用戶數據庫上下文
 43         /// </summary>
 44         private UsersContext dbContext = null;
 45 
 46         /// <summary>
 47         /// 獲取數據庫上下文
 48         /// </summary>
 49         public UsersContext DbContext { get { return dbContext; } }
 50 
 51         /// <summary>
 52         /// 構造器
 53         /// </summary>
 54         public UnitOfWork()
 55         {
 56             dbContext = new UsersContext();
 57         }
 58 
 59         /// <summary>
 60         /// 構造器
 61         /// </summary>
 62         /// <param name="context">數據庫上下文</param>
 63         public UnitOfWork(UsersContext context)
 64         {
 65             dbContext = context;
 66         }
 67 
 68         /// <summary>
 69         /// 析構器
 70         /// </summary>
 71         public void Dispose()
 72         {
 73             if (dbContext != null)
 74                 dbContext.Dispose();
 75             GC.SuppressFinalize(this);
 76         }
 77 
 78         /// <summary>
 79         /// 獲取指定類型的倉儲實例
 80         /// </summary>
 81         /// <typeparam name="T">泛型參數</typeparam>
 82         /// <returns>IRepository</returns>
 83         public IRepository<T> Repositry<T>() where T : class
 84         {
 85             return new Repository<T>(DbContext);
 86         }
 87 
 88         /// <summary>
 89         /// 提交事務
 90         /// </summary>
 91         /// <returns>Integer</returns>
 92         public int Commit()
 93         {
 94             return dbContext.SaveChanges();
 95         }
 96 
 97         /// <summary>
 98         /// 執行自定義SQL語句。
 99         /// 該方法提供了一個直接操作數據庫表的實現。
100         /// </summary>
101         /// <param name="commandText">SQL語句</param>
102         /// <param name="parameters">參數列表</param>
103         /// <returns>Integer</returns>
104         public int ExecuteSqlCommand(string commandText, params object[] parameters)
105         {
106             return dbContext.Database.ExecuteSqlCommand(commandText, parameters);
107         }
108     } 
109 }
View Code 

  OK,基礎類型的定義已經完成,看看我們如何使用它吧。先模擬一個場景:當前要添加一個用戶,同時在添加用戶的時候,要吧該用戶增加到指定的部門列表(UserDepartment)下。

 1         public void AddNewUser(string userName, string department)
 2         {
 3             using (IUnitOfWork unit = new UnitOfWork())
 4             {
 5                 //獲取用戶類型的倉儲
 6                 var usrRep = unit.Repositry<UserProfile>();
 7                 //創建新用戶
 8                 var User = new UserProfile { UserName = userName };
 9 
10                 //將用戶信息添加到數據庫。
11                 //注意:我們沒有使用Create接口的第二個參數,即表示第二個參數默認為false,這表示當前操作暫時不提交到數據庫。
12                 //如果使用 usrRep.Create(User, true), 則表示直接提交當前記錄到數據庫。
13                 //假如有興趣,可以嘗試第二個參數為true,執行該句之后人為拋出一個異常,看看數據庫是如何發生變化的。
14                 usrRep.Create(User);
15                 
16                 //throw new Exception("");
17 
18                 //獲取部門類型的倉儲
19                 var depRep = unit.Repositry<Department>();
20                 //根據部門名稱獲取部門信息
21                 var Department = depRep.FirstOrDefault(p => p.Name.Equals(department));
22                 //將當前用戶添加到該部門內
23                 Department.DepartmentUsers.Add(new UserDepartment { UserId = User.UserId, DepartmentId = Department.Id });
24 
25                 //提交前面所有的操作。這個才是最關鍵的。沒有這句,一切都是瞎忙活!!!
26                 unit.Commit();
27             }
28         }
View Code

  當然,通常我們不會這么去做。我們會在UserProfile的定義中添加一個Department集合,同樣會在Department中添加一個UserProfile集合,構建用戶與部門的多對多關系(EntityFramework的多種數據映射關系以后我們會提到,網上相應的資料也很多),這樣就可以很容易的只是用用戶倉儲實例進行操作,只需一個usrRep.Create(User, true)操作就可以完成上面的業務。不過我們只是為了說明Unitofwork如何工作,大家不必太較真。

  好了,今天就此結束。希望我能堅持不懈,也希望大家能一起見證我們努力的結果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM