(译文)MVC通用仓储类
Generic Repository Pattern MVC
Generic Repository Pattern MVC
原文链接:http://www.codeproject.com/Articles/1095323/Generic-Repository-Pattern-MVC
良好的架构师任何项目的核心,开发人员一直在寻找一个牛X的架构,它能减少重复代码,分离数据访问与业务逻辑。因此,我们需要在MVC中使用EF创建一个泛型仓储类。如果不了解EF,去这里学习。在开始之前,我们需要了解什么是仓储模式,为什么使用仓储模式。
仓储模式和工作单元
简言之,仓储模式意味着,数据访问层与业务逻辑层之间的抽象,这是非常有利于单元测试或TDD。通过使用仓储模式,你的系统会更加松散耦合。
在开发过程中,我们通常为每个仓储或实体创建一个接口。例如,我们为Student这个实体创建一个接口约束(IStudentInterface),定义所有的CRUD操作,同时创建另一个类(StudentRepository)实现接口中所定义的所有方法。当我们在控制器中实例化仓储类的时候,我们将使用实现了对应接口的类的引用。当控制器在运行的时候,它将调用在EF基础上工作的仓储类。
当对控制器进行单元测试的时候,我们可以操作仓储类的具体数据实现,例如内存数据集。这样我们可以使用伪数据进行单元测试。
如果你想了解详细的实现,可以参照如下链接:
不利因素
每次我们都需要为实体创建仓储类,导致代码冗余
代码实现
现在我们仅仅需要一个数据访问类,介绍一些实体和执行必要的操作,例如CRUD.在学习了很多文章、理论和示例代码后,我获得了一个很好的通用的仓储模式的实现。
我的代码在很大程度上基于Huy Nguyen的博客。请参阅以下链接
•entity-framework-4-poco-repository-and-specification-pattern
•entity-framework-poco-repository-and-specification-pattern-upgraded-to-ef-5
我修改了很多实现代码同时添加了一些在项目中常用的代码实现。现在我能使用这个类库在任何项目。下面是文件结构:
Mayur.DAL – 通用仓储和公共方法类库
-
Core – 文件夹
- GlobalCommonHelper.cs – 一个为每个项目提供大部分公共方法的抽象类
-
Repository – 文件夹
- IRepository.cs – 通用仓储接口
- Repository.cs – 通用仓储实现类,继承与仓储接口
- IUnitOfWork.cs – 工作单元接口.
- UnitOfWork.cs – 实现了EF的SaveChanges()方法。工作单元类保证我们在对数据库执行事务性的插入、更新、删除操作时,直到我们执行Savechanges()方法以后EF才会提交所做的修改。
Mayur.Web – MVC Web项目
-
Controller – 文件夹
- HomeController.cs – 包含CRUD动作的控制器
-
Core – 文件夹
- CommonHelper.cs – 继承于 Mayur.DAL.Core.GlobalCommonHelper.cs which 包含MVC项目中相关的公共方法。
-
Model – 文件夹
- Student.cs – 实体类
-
Views – Folder
- Index.chtml – 不用说了吧都
- Create.chtml – Create new student html
- Edit.cshtml – Update student info html
- Delete.cshtml – Delete student info html
让我们简单了解下DAL中每个文件的作用:
Repository 文件夹: 在这个文件夹,包含所有的数据访问逻辑。有4个文件, 2个接口文件,两个接口的实现类
1. IRepository 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | public interface IRepository : IDisposable { /// <summary> /// Gets the unit of work. /// </summary> /// <value>The unit of work.</value> IUnitOfWork UnitOfWork { get ; } /// <summary> /// Gets entity by key. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="keyValue">The key value.</param> /// <returns></returns> TEntity GetByKey<TEntity>( object keyValue) where TEntity : class ; /// <summary> /// Gets the query. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <returns></returns> IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class ; /// <summary> /// Gets the query. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="predicate">The predicate.</param> /// <returns></returns> IQueryable<TEntity> GetQuery<TEntity> (Expression<Func<TEntity, bool >> predicate) where TEntity : class ; /// <summary> /// Gets all. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <returns></returns> IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class ; /// <summary> /// Gets the specified order by. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <typeparam name="TOrderBy">The type of the order by.</typeparam> /// <param name="orderBy">The order by.</param> /// <param name="pageIndex">Index of the page.</param> /// <param name="pageSize">Size of the page.</param> /// <param name="sortOrder">The sort order.</param> /// <returns></returns> IEnumerable<TEntity> Get<TEntity, TOrderBy>(Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class ; /// <summary> /// Gets the specified criteria. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <typeparam name="TOrderBy">The type of the order by.</typeparam> /// <param name="criteria">The criteria.</param> /// <param name="orderBy">The order by.</param> /// <param name="pageIndex">Index of the page.</param> /// <param name="pageSize">Size of the page.</param> /// <param name="sortOrder">The sort order.</param> /// <returns></returns> IEnumerable<TEntity> Get<TEntity, TOrderBy>(Expression<Func<TEntity, bool >> criteria, Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class ; /// <summary> /// Gets one entity based on matching criteria /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="criteria">The criteria.</param> /// <returns></returns> TEntity Single<TEntity>(Expression<Func<TEntity, bool >> criteria) where TEntity : class ; /// <summary> /// Firsts the specified predicate. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="predicate">The predicate.</param> /// <returns></returns> TEntity First<TEntity>(Expression<Func<TEntity, bool >> predicate) where TEntity : class ; /// <summary> /// Finds entities based on provided criteria. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="criteria">The criteria.</param> /// <returns></returns> IEnumerable<TEntity> Find<TEntity> (Expression<Func<TEntity, bool >> criteria) where TEntity : class ; /// <summary> /// Finds one entity based on provided criteria. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="criteria">The criteria.</param> /// <returns></returns> TEntity FindOne<TEntity>(Expression<Func<TEntity, bool >> criteria) where TEntity : class ; /// <summary> /// Counts the specified entities. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <returns></returns> int Count<TEntity>() where TEntity : class ; /// <summary> /// Counts entities with the specified criteria. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="criteria">The criteria.</param> /// <returns></returns> int Count<TEntity>(Expression<Func<TEntity, bool >> criteria) where TEntity : class ; /// <summary> /// Adds the specified entity. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="entity">The entity.</param> void Add<TEntity>(TEntity entity) where TEntity : class ; /// <summary> /// Attaches the specified entity. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="entity">The entity.</param> void Attach<TEntity>(TEntity entity) where TEntity : class ; /// <summary> /// Updates changes of the existing entity. /// The caller must later call SaveChanges() /// on the repository explicitly to save the entity to database /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="entity">The entity.</param> void Update<TEntity>(TEntity entity) where TEntity : class ; /// <summary> /// Deletes the specified entity. /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="entity">The entity.</param> void Delete<TEntity>(TEntity entity) where TEntity : class ; /// <summary> /// Deletes one or many entities matching the specified criteria /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="criteria">The criteria.</param> void Delete<TEntity>(Expression<Func<TEntity, bool >> criteria) where TEntity : class ; /// <summary> /// Deletes entities which satisfy specificatiion /// </summary> /// <typeparam name="TEntity">The type of the entity.</typeparam> /// <param name="criteria">The criteria.</param> //void Delete<TEntity> (ISpecification<TEntity> criteria) where TEntity : class ; } |
2. Repository Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | /// <summary> /// Generic repository Class /// </summary> public partial class Repository : IRepository, IDisposable { //Private Variables private bool bDisposed; private DbContext context; private IUnitOfWork unitOfWork; #region Contructor Logic /// <summary> /// Initializes a new instance of the /// <see cref="Repository<TEntity>"/> class. /// </summary> public Repository() { } /// <summary> /// Initializes a new instance of the /// <see cref="Repository<TEntity>" /> class. /// </summary> /// <param name="context">The context.</param> public Repository(DbContext contextObj) { if (contextObj == null ) throw new ArgumentNullException( "context" ); this .context = contextObj; } public Repository(ObjectContext contextObj) { if (contextObj == null ) throw new ArgumentNullException( "context" ); context = new DbContext(contextObj, true ); } public void Dispose() { Close(); } #endregion #region Properties //DbContext Property protected DbContext DbContext { get { if (context == null ) throw new ArgumentNullException( "context" ); return context; } } //Unit of Work Property public IUnitOfWork UnitOfWork { get { if (unitOfWork == null ) { unitOfWork = new UnitOfWork(DbContext); } return unitOfWork; } } #endregion #region Data Display Methods //Helper Method tp create Query [IQuerable] public TEntity GetByKey<TEntity>( object keyValue) where TEntity : class { EntityKey key = GetEntityKey<TEntity>(keyValue); object originalItem; if (((IObjectContextAdapter)DbContext). ObjectContext.TryGetObjectByKey(key, out originalItem)) { return (TEntity)originalItem; } return default (TEntity); } public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class { string entityName = GetEntityName<TEntity>(); return ((IObjectContextAdapter)DbContext). ObjectContext.CreateQuery<TEntity>(entityName); } public IQueryable<TEntity> GetQuery<TEntity> (Expression<Func<TEntity, bool >> predicate) where TEntity : class { return GetQuery<TEntity>().Where(predicate); } //All Readonly Display or fetch data methods. public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class { return GetQuery<TEntity>().AsEnumerable(); } public IEnumerable<TEntity> Get<TEntity, TOrderBy> (Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class { if (sortOrder == SortOrder.Ascending) { return GetQuery<TEntity>() .OrderBy(orderBy) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .AsEnumerable(); } return GetQuery<TEntity>() .OrderByDescending(orderBy) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .AsEnumerable(); } public IEnumerable<TEntity> Get<TEntity, TOrderBy>(Expression<Func<TEntity, bool >> criteria, Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class { if (sortOrder == SortOrder.Ascending) { return GetQuery(criteria). OrderBy(orderBy). Skip((pageIndex - 1) * pageSize). Take(pageSize) .AsEnumerable(); } return GetQuery(criteria) .OrderByDescending(orderBy) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .AsEnumerable(); } public TEntity Single<TEntity> (Expression<Func<TEntity, bool >> criteria) where TEntity : class { return GetQuery<TEntity>().Single<TEntity>(criteria); } public TEntity First<TEntity> (Expression<Func<TEntity, bool >> predicate) where TEntity : class { return GetQuery<TEntity>().First(predicate); } public IEnumerable<TEntity> Find<TEntity> (Expression<Func<TEntity, bool >> criteria) where TEntity : class { return GetQuery<TEntity>().Where(criteria); } public TEntity FindOne<TEntity> (Expression<Func<TEntity, bool >> criteria) where TEntity : class { return GetQuery<TEntity>().Where(criteria).FirstOrDefault(); } public int Count<TEntity>() where TEntity : class { return GetQuery<TEntity>().Count(); } public int Count<TEntity> (Expression<Func<TEntity, bool >> criteria) where TEntity : class { return GetQuery<TEntity>().Count(criteria); } #endregion #region Data Transactional Methods public void Add<TEntity>(TEntity entity) where TEntity : class { if (entity == null ) { throw new ArgumentNullException( "entity" ); } DbContext.Set<TEntity>().Add(entity); } public void Attach<TEntity>(TEntity entity) where TEntity : class { if (entity == null ) { throw new ArgumentNullException( "entity" ); } DbContext.Set<TEntity>().Attach(entity); } public void Update<TEntity>(TEntity entity) where TEntity : class { string fqen = GetEntityName<TEntity>(); object originalItem; EntityKey key = ((IObjectContextAdapter)DbContext).ObjectContext.CreateEntityKey(fqen, entity); if (((IObjectContextAdapter)DbContext).ObjectContext.TryGetObjectByKey (key, out originalItem)) { ((IObjectContextAdapter)DbContext).ObjectContext.ApplyCurrentValues (key.EntitySetName, entity); } } public void Delete<TEntity>(TEntity entity) where TEntity : class { if (entity == null ) { throw new ArgumentNullException( "entity" ); } DbContext.Set<TEntity>().Remove(entity); } public void Delete<TEntity>(Expression<Func<TEntity, bool >> criteria) where TEntity : class { IEnumerable<TEntity> records = Find(criteria); foreach (TEntity record in records) { Delete(record); } } #endregion #region Internal Processing Private Methods private EntityKey GetEntityKey<TEntity>( object keyValue) where TEntity : class { string entitySetName = GetEntityName<TEntity>(); ObjectSet<TEntity> objectSet = ((IObjectContextAdapter)DbContext).ObjectContext.CreateObjectSet<TEntity>(); string keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString(); var entityKey = new EntityKey (entitySetName, new [] { new EntityKeyMember(keyPropertyName, keyValue) }); return entityKey; } private string GetEntityName<TEntity>() where TEntity : class { // Thanks to Kamyar Paykhan - // http://huyrua.wordpress.com/2011/04/13/ // entity-framework-4-poco-repository-and-specification-pattern-upgraded-to-ef-4-1/ // #comment-688 string entitySetName = ((IObjectContextAdapter)DbContext).ObjectContext .MetadataWorkspace .GetEntityContainer(((IObjectContextAdapter)DbContext). ObjectContext.DefaultContainerName, DataSpace.CSpace) .BaseEntitySets.Where(bes => bes.ElementType.Name == typeof (TEntity).Name).First().Name; return string .Format( "{0}.{1}" , ((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName, entitySetName); } private string RemoveAccent( string txt) { byte [] bytes = System.Text.Encoding.GetEncoding( "Cyrillic" ).GetBytes(txt); return System.Text.Encoding.ASCII.GetString(bytes); } private bool IsValidTag( string tag, string tags) { string [] allowedTags = tags.Split( ',' ); if (tag.IndexOf( "javascript" ) >= 0) return false ; if (tag.IndexOf( "vbscript" ) >= 0) return false ; if (tag.IndexOf( "onclick" ) >= 0) return false ; var endchars = new char [] { ' ' , '>' , '/' , '\t' }; int pos = tag.IndexOfAny(endchars, 1); if (pos > 0) tag = tag.Substring(0, pos); if (tag[0] == '/' ) tag = tag.Substring(1); foreach ( string aTag in allowedTags) { if (tag == aTag) return true ; } return false ; } #endregion #region Disposing Methods protected void Dispose( bool bDisposing) { if (!bDisposed) { if (bDisposing) { if ( null != context) { context.Dispose(); } } bDisposed = true ; } } public void Close() { Dispose( true ); GC.SuppressFinalize( this ); } #endregion } } |
3. IUnitOfWork Interface
1 2 3 4 | public interface IUnitOfWork : IDisposable { void SaveChanges(); } |
4. UnitOfWork Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | internal class UnitOfWork : IUnitOfWork { private readonly DbContext _dbContext; public UnitOfWork(DbContext context) { _dbContext = context; } public void SaveChanges() { ((IObjectContextAdapter)_dbContext).ObjectContext.SaveChanges(); } #region Implementation of IDisposable private bool _disposed; /// <summary> /// Performs application-defined tasks associated with freeing, /// releasing, or resetting unmanaged resources. /// </summary> public void Dispose() { Dispose( true ); GC.SuppressFinalize( this ); } /// <summary> /// Disposes off the managed and unmanaged resources used. /// </summary> /// <param name="disposing"></param> private void Dispose( bool disposing) { if (!disposing) return ; if (_disposed) return ; _disposed = true ; } #endregion } |
在Mayur.DAL.Core文件夹中,还有一个抽象类,包含了一些项目中常用到的公共方法,如果你也有一些新的方法函数是我们在项目中需要的,请在评论中提出建议(原文这么说的,在我这评论我也不介意)。
5. Mayur.DAL.Core.GlobalCommonHelper.cs Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | abstract public class GlobalCommonHelper { #region General Methods /// <summary> /// Take any string and encrypt it using SHA1 then /// return the encrypted data /// </summary> /// <param name="data">input text you will enterd to encrypt it</param> /// <returns>return the encrypted text as hexadecimal string</returns> public string GetSHA1HashData( string data) { //create new instance of md5 SHA1 sha1 = SHA1.Create(); //convert the input text to array of bytes byte [] hashData = sha1.ComputeHash(Encoding.Default.GetBytes(data)); //create new instance of StringBuilder to save hashed data StringBuilder returnValue = new StringBuilder(); //loop for each byte and add it to StringBuilder for ( int i = 0; i < hashData.Length; i++) { returnValue.Append(hashData[i].ToString()); } // return hexadecimal string return returnValue.ToString(); } /// <summary> /// Creates a slug url from string . /// </summary> /// <param name="phrase"></param> /// <returns></returns> public string GetSlugURLFromString( string phrase) { string str = RemoveAccent(phrase).ToLower(); // invalid chars str = Regex.Replace(str, @"[^a-z0-9\s-]" , "" ); // convert multiple spaces into one space str = Regex.Replace(str, @"\s+" , " " ).Trim(); // cut and trim str = str.Substring(0, str.Length <= 45 ? str.Length : 45).Trim(); str = Regex.Replace(str, @"\s" , "-" ); // hyphens return str; } /// <summary> /// Delete file by specified path. /// </summary> /// <param name="path">path of file.</param> public void DeleteTargetFile( string path) { if (File.Exists(path)) { File.SetAttributes(path, FileAttributes.Normal); File.Delete(path); } } /// <summary> /// Sent email to target email address with attachment. /// </summary> /// <param name="toEmail">Email addresses of /// one or multiple receipients semi colon (;) separated values.</param> /// <param name="subject">Email subject</param> /// <param name="body">Email body</param> /// <returns>True | False</returns> public bool SendEmailToTarget( string toEmail, string subject, string body) { bool success = false ; try { SmtpClient SmtpServer = new SmtpClient(); MailMessage mail = new MailMessage(); SmtpServer.Credentials = new NetworkCredential( Convert.ToString(ConfigurationManager.AppSettings[ "fromEmail" ]), Convert.ToString(ConfigurationManager.AppSettings[ "fromPassword" ])); SmtpServer.Host = Convert.ToString (ConfigurationManager.AppSettings[ "hostName" ]); SmtpServer.Port = Convert.ToInt32 (ConfigurationManager.AppSettings[ "portNumber" ]); if (Convert.ToBoolean (ConfigurationManager.AppSettings[ "isEnableSSL" ]) == true ) SmtpServer.EnableSsl = true ; mail.From = new MailAddress(Convert.ToString (ConfigurationManager.AppSettings[ "senderName" ])); string [] multiEmails = toEmail.Split( ';' ); foreach ( string email in multiEmails) { mail.To.Add(email); } mail.Subject = subject; mail.IsBodyHtml = true ; mail.Body = body; SmtpServer.Send(mail); mail.Dispose(); success = true ; } catch (Exception) { success = false ; } return success; } /// <summary> /// Sent email to target email address with attachment. /// </summary> /// <param name="toEmail">Email addresses of /// one or multiple receipients semi colon (;) separated values.</param> /// <param name="subject">Email subject</param> /// <param name="body">Email body</param> /// <param name="body">Email attachment file path</param> /// <returns>True | False</returns> public bool SendEmailToTarget( string toEmail, string subject, string body, string attachmentPath) { bool success = false ; try { SmtpClient SmtpServer = new SmtpClient(); MailMessage mail = new MailMessage(); SmtpServer.Credentials = new NetworkCredential( Convert.ToString(ConfigurationManager.AppSettings[ "fromEmail" ]), Convert.ToString(ConfigurationManager.AppSettings[ "fromPassword" ])); SmtpServer.Host = Convert.ToString (ConfigurationManager.AppSettings[ "hostName" ]); SmtpServer.Port = Convert.ToInt32 (ConfigurationManager.AppSettings[ "portNumber" ]); if (Convert.ToBoolean(ConfigurationManager.AppSettings[ "isEnableSSL" ]) == true ) SmtpServer.EnableSsl = true ; mail.From = new MailAddress(Convert.ToString (ConfigurationManager.AppSettings[ "senderName" ])); string [] multiEmails = toEmail.Split( ';' ); foreach ( string email in multiEmails) { mail.To.Add(email); } Attachment attachment; attachment = new System.Net.Mail.Attachment(attachmentPath); mail.Attachments.Add(attachment); mail.Subject = subject; mail.IsBodyHtml = true ; mail.Body = body; SmtpServer.Send(mail); mail.Dispose(); success = true ; } catch (Exception) { success = false ; } return success; } /// <summary> /// Strips tags /// </summary> /// <param name="text">Text</param> /// <returns>Formatted text</returns> public string RemoveHtmlFromString( string text) { if (String.IsNullOrEmpty(text)) return string .Empty; text = Regex.Replace(text, @"(>)(\r|\n)*(<)" , "><" ); text = Regex.Replace(text, "(<[^>]*>)([^<]*)" , "$2" ); text = Regex.Replace(text, "(&#x?[0-9]{2,4};|" |&| |<|>|€|©|®|‰|‡|†|‹| ›|„|”|“|‚|’|‘|—| –||||| | | |˜| ˆ|Ÿ|š|Š) ", " @"); return text; } /// <summary> /// Verifies that a string is in valid e-mail format /// </summary> /// <param name=" email">Email to verify</param> /// <returns>true if the string is a valid e-mail address and false if it's not</returns> public bool IsValidEmail( string email) { if (String.IsNullOrEmpty(email)) return false ; email = email.Trim(); var result = Regex.IsMatch(email, "^(?:[\\w\\!\\#\\$\\%\\&\\ '\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+\\.)*[\\w\\!\\#\\$\\%\\&\\ '\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+@(?:(?:(?:[a-zA-Z0-9] (?:[a-zA-Z0-9\\-](?!\\.)){0,61}[a-zA-Z0-9]?\\.)+[a-zA-Z0-9] (?:[a-zA-Z0-9\\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\\[(?:(?:[01]?\\d{1,2}|2[0-4] \\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\]))$", RegexOptions.IgnoreCase); return result; } /// <summary> /// Returns Allowed HTML only. /// </summary> /// <param name="text">Text</param> /// <returns>Allowed HTML</returns> public string EnsureOnlyAllowedHtml( string text) { if (String.IsNullOrEmpty(text)) return string .Empty; const string allowedTags = "br,hr,b,i,u,a,div,ol,ul,li,blockquote,img,span,p,em," + "strong,font,pre,h1,h2,h3,h4,h5,h6,address,cite" ; var m = Regex.Matches(text, "<.*?>" , RegexOptions.IgnoreCase); for ( int i = m.Count - 1; i >= 0; i--) { string tag = text.Substring(m[i].Index + 1, m[i].Length - 1).Trim().ToLower(); if (!IsValidTag(tag, allowedTags)) { text = text.Remove(m[i].Index, m[i].Length); } } return text; } #endregion #region Internal Processing Private Methods private string RemoveAccent( string txt) { byte [] bytes = System.Text.Encoding.GetEncoding( "Cyrillic" ).GetBytes(txt); return System.Text.Encoding.ASCII.GetString(bytes); } private bool IsValidTag( string tag, string tags) { string [] allowedTags = tags.Split( ',' ); if (tag.IndexOf( "javascript" ) >= 0) return false ; if (tag.IndexOf( "vbscript" ) >= 0) return false ; if (tag.IndexOf( "onclick" ) >= 0) return false ; var endchars = new char [] { ' ' , '>' , '/' , '\t' }; int pos = tag.IndexOfAny(endchars, 1); if (pos > 0) tag = tag.Substring(0, pos); if (tag[0] == '/' ) tag = tag.Substring(1); foreach ( string aTag in allowedTags) { if (tag == aTag) return true ; } return false ; } #endregion } |
现在通用仓储已经具备了公共的方法,现在来看下我们怎么在控制器中使用它。假设我们有一个Student实体,包含studentID, name, rollNo 等列,下面是控制器中的代码
1. 在继续之前,我们需要先完善数据上下文信息,以生成数据库和数据表 ,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public partial class MyFirstDbContext : DbContext { public MyFirstDbContext() : base ( "name=MyFirstDbContext" ) { Database.SetInitializer<MyFirstDbContext>( null ); } public virtual DbSet<Students> Students { get ; set ; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { } } |
2. 我在数据访问层创建了一个抽象的包含了每个项目中都要使用的公共方法的类。因为是一个抽象类,这意味着我们为它创建实例。所以我们需要创建一个新的类在Web项目中,这个类继承于GlobalCommonHelper类,命名为CommonHelper,我们可以在这个类中实现一些项目独有的公共方法。
1 2 3 4 5 | public class CommonHelper : GlobalCommonHelper { public int PageSize = 25; //Some common functions. Only Project-specific. } |
3. 现在我们可以看下如何在我们的控制器中使用仓储类,看控制器中的代码:
1 2 3 4 5 6 | //Constructor public HomeController() { IRepository repository = new Repository( new MyFirstDbContex); CommonHelper helper = new CommonHelper(); } |
1: public class BooksController : Controller
2: {
3: private IRepository repository;
4: private CommonHelper helper;
5:
6: public BooksController()
7: {
8: repository=new Repository(new MyFirstDbContext());
9: helper=new CommonHelper();
10: }
11:
12: // GET: Books
13: public ActionResult Index()
14: {
15: var list = repository.GetAll<Book>();
16: return View(list);
17: }
18:
19: // GET: Books/Details/5
20: public ActionResult Details(int id=0)
21: {
22: if (id == 0)
23: {
24: return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
25: }
26:
27: Book book = repository.FindOne<Book>(b => b.Id == id);
28: if (book == null)
29: {
30: return HttpNotFound();
31: }
32: return View(book);
33: }
34:
35: // GET: Books/Create
36: public ActionResult Create()
37: {
38: return View();
39: }
40:
41: // POST: Books/Create
42: // 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
43: // 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
44: [HttpPost]
45: [ValidateAntiForgeryToken]
46: public ActionResult Create([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,SalePrice,PublicationDate,Introduction,AboutTheAuthors,Link")] Book book)
47: {
48: if (ModelState.IsValid)
49: {
50: repository.Add(book);
51: repository.UnitOfWork.SaveChanges();
52:
53: return RedirectToAction("Index");
54: }
55:
56: return View(book);
57: }
58:
59: // GET: Books/Edit/5
60: public ActionResult Edit(int id=0)
61: {
62: if (id == 0)
63: {
64: return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
65: }
66: Book book = repository.FindOne<Book>(x => x.Id == id);
67: if (book == null)
68: {
69: return HttpNotFound();
70: }
71: return View(book);
72: }
73:
74: // POST: Books/Edit/5
75: // 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
76: // 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
77: [HttpPost]
78: [ValidateAntiForgeryToken]
79: public ActionResult Edit([Bind(Include = "Id,Cover,BookName,Author,TranslatedName,Translator,Publisher,WordCount,Pages,ISBN,Price,SalePrice,PublicationDate,Introduction,AboutTheAuthors,Link")] Book book)
80: {
81: if (ModelState.IsValid)
82: {
83: repository.Update<Book>(book);
84: repository.UnitOfWork.SaveChanges();
85: //db.Entry(book).State = EntityState.Modified;
86: //db.SaveChanges();
87: return RedirectToAction("Index");
88: }
89: return View(book);
90: }
91:
92: // GET: Books/Delete/5
93: public ActionResult Delete(int id=0)
94: {
95: if (id == 0)
96: {
97: return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
98: }
99: Book book = repository.FindOne<Book>(b => b.Id == id);
100: if (book == null)
101: {
102: return HttpNotFound();
103: }
104: return View(book);
105: }
106:
107: // POST: Books/Delete/5
108: [HttpPost, ActionName("Delete")]
109: [ValidateAntiForgeryToken]
110: public ActionResult DeleteConfirmed(int id)
111: {
112: Book book = repository.FindOne<Book>(b => b.Id == id);
113: if (book == null)
114: {
115: return HttpNotFound();
116: }
117: repository.Delete<Book>(book);
118: repository.UnitOfWork.SaveChanges();
119: //db.Books.Remove(book);
120: //db.SaveChanges();
121: return RedirectToAction("Index");
122: }
123:
124: }
我需要更多来自你的建议和改进,请提给我吧。(原作)
译注
正好在学习仓储模式和工作单元,想整理一个通用的仓储类,作者的做参考。
英语不好有些翻译很生硬,还有的直接表意,省掉了作者一些话(他们写文章都很认真,像教小学生一样敦敦教诲)
版本
•31/05/2015: Article published
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
转载自:http://www.cnblogs.com/wit13142/p/5435368.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?