原文链接:http://www.codeproject.com/Articles/1095323/Generic-Repository-Pattern-MVC

良好的架构师任何项目的核心,开发人员一直在寻找一个牛X的架构,它能减少重复代码,分离数据访问与业务逻辑。因此,我们需要在MVC中使用EF创建一个泛型仓储类。如果不了解EF,去这里学习。在开始之前,我们需要了解什么是仓储模式,为什么使用仓储模式。

仓储模式和工作单元

简言之,仓储模式意味着,数据访问层与业务逻辑层之间的抽象,这是非常有利于单元测试或TDD。通过使用仓储模式,你的系统会更加松散耦合。

在开发过程中,我们通常为每个仓储或实体创建一个接口。例如,我们为Student这个实体创建一个接口约束(IStudentInterface),定义所有的CRUD操作,同时创建另一个类(StudentRepository)实现接口中所定义的所有方法。当我们在控制器中实例化仓储类的时候,我们将使用实现了对应接口的类的引用。当控制器在运行的时候,它将调用在EF基础上工作的仓储类。

当对控制器进行单元测试的时候,我们可以操作仓储类的具体数据实现,例如内存数据集。这样我们可以使用伪数据进行单元测试。

如果你想了解详细的实现,可以参照如下链接:

getting started with ef 5 using mvc 4 / implementing the repository and unit of work patterns in an asp.net mvc application

不利因素

每次我们都需要为实体创建仓储类,导致代码冗余

代码实现

现在我们仅仅需要一个数据访问类,介绍一些实体和执行必要的操作,例如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 接口

     public interface IRepository : IDisposable
    {
    /// <summary>
    /// 获取工作单元
    /// </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 类

       1:     /// <summary>
       2:     ///     Generic repository Class
       3:     /// </summary>
       4:     public partial class Repository : IRepository, IDisposable
       5:     {
       6:         //Private Variables
       7:         private bool bDisposed;
       8:         private DbContext context;
       9:         private IUnitOfWork unitOfWork;
      10:   
      11:         #region Contructor Logic
      12:   
      13:         /// <summary>
      14:         /// Initializes a new instance of the
      15:         /// <see cref="Repository<TEntity>"/> class.
      16:         /// </summary>
      17:         public Repository()
      18:         {
      19:   
      20:         }
      21:   
      22:         /// <summary>
      23:         ///     Initializes a new instance of the
      24:         /// <see cref="Repository<TEntity>" /> class.
      25:         /// </summary>
      26:         /// <param name="context">The context.</param>
      27:         public Repository(DbContext contextObj)
      28:         {
      29:             if (contextObj == null)
      30:                 throw new ArgumentNullException("context");
      31:             this.context = contextObj;
      32:         }
      33:   
      34:         public Repository(ObjectContext contextObj)
      35:         {
      36:             if (this.context == null)
      37:                 throw new ArgumentNullException("context");
      38:             context = new DbContext(contextObj, true);
      39:         }
      40:   
      41:         public void Dispose()
      42:         {
      43:             Close();
      44:         }
      45:   
      46:         #endregion
      47:   
      48:         #region Properties
      49:   
      50:         //DbContext Property
      51:         protected DbContext DbContext
      52:         {
      53:             get
      54:             {
      55:                 if (context == null)
      56:                     throw new ArgumentNullException("context");
      57:   
      58:                 return context;
      59:             }
      60:         }
      61:   
      62:         //Unit of Work Property
      63:         public IUnitOfWork UnitOfWork
      64:         {
      65:             get
      66:             {
      67:                 if (unitOfWork == null)
      68:                 {
      69:                     unitOfWork = new UnitOfWork(DbContext);
      70:                 }
      71:                 return unitOfWork;
      72:             }
      73:         }
      74:   
      75:         #endregion
      76:   
      77:         #region Data Display Methods
      78:   
      79:         //Helper Method tp create Query [IQuerable]
      80:   
      81:         public TEntity GetByKey<TEntity>(object keyValue) where TEntity : class
      82:         {
      83:             EntityKey key = GetEntityKey<TEntity>(keyValue);
      84:   
      85:             object originalItem;
      86:             if (((IObjectContextAdapter)DbContext).
      87:             ObjectContext.TryGetObjectByKey(key, out originalItem))
      88:             {
      89:                 return (TEntity)originalItem;
      90:             }
      91:   
      92:             return default(TEntity);
      93:         }
      94:   
      95:         public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
      96:         {
      97:             string entityName = GetEntityName<TEntity>();
      98:             return ((IObjectContextAdapter)DbContext).
      99:             ObjectContext.CreateQuery<TEntity>(entityName);
     100:         }
     101:   
     102:         public IQueryable<TEntity> GetQuery<TEntity>
     103:         (Expression<Func<TEntity, bool>> predicate) where TEntity : class
     104:         {
     105:             return GetQuery<TEntity>().Where(predicate);
     106:         }
     107:   
     108:   
     109:         //All Readonly Display or fetch data methods.
     110:         public IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
     111:         {
     112:             return GetQuery<TEntity>().AsEnumerable();
     113:         }
     114:   
     115:         public IEnumerable<TEntity> Get<TEntity, TOrderBy>
     116:         (Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex,
     117:             int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
     118:         {
     119:             if (sortOrder == SortOrder.Ascending)
     120:             {
     121:                 return GetQuery<TEntity>()
     122:                     .OrderBy(orderBy)
     123:                     .Skip((pageIndex - 1) * pageSize)
     124:                     .Take(pageSize)
     125:                     .AsEnumerable();
     126:             }
     127:             return
     128:                 GetQuery<TEntity>()
     129:                     .OrderByDescending(orderBy)
     130:                     .Skip((pageIndex - 1) * pageSize)
     131:                     .Take(pageSize)
     132:                     .AsEnumerable();
     133:         }
     134:   
     135:         public IEnumerable<TEntity> Get<TEntity,
     136:         TOrderBy>(Expression<Func<TEntity, bool>> criteria,
     137:             Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize,
     138:             SortOrder sortOrder = SortOrder.Ascending) where TEntity : class
     139:         {
     140:             if (sortOrder == SortOrder.Ascending)
     141:             {
     142:                 return GetQuery(criteria).
     143:                     OrderBy(orderBy).
     144:                     Skip((pageIndex - 1) * pageSize).
     145:                     Take(pageSize)
     146:                     .AsEnumerable();
     147:             }
     148:             return
     149:                 GetQuery(criteria)
     150:                     .OrderByDescending(orderBy)
     151:                     .Skip((pageIndex - 1) * pageSize)
     152:                     .Take(pageSize)
     153:                     .AsEnumerable();
     154:         }
     155:   
     156:         public TEntity Single<TEntity>
     157:         (Expression<Func<TEntity, bool>> criteria) where TEntity : class
     158:         {
     159:             return GetQuery<TEntity>().Single<TEntity>(criteria);
     160:         }
     161:   
     162:         public TEntity First<TEntity>
     163:         (Expression<Func<TEntity, bool>> predicate) where TEntity : class
     164:         {
     165:             return GetQuery<TEntity>().First(predicate);
     166:         }
     167:   
     168:         public IEnumerable<TEntity> Find<TEntity>
     169:         (Expression<Func<TEntity, bool>> criteria) where TEntity : class
     170:         {
     171:             return GetQuery<TEntity>().Where(criteria);
     172:         }
     173:   
     174:         public TEntity FindOne<TEntity>
     175:         (Expression<Func<TEntity, bool>> criteria) where TEntity : class
     176:         {
     177:             return GetQuery<TEntity>().Where(criteria).FirstOrDefault();
     178:         }
     179:   
     180:         public int Count<TEntity>() where TEntity : class
     181:         {
     182:             return GetQuery<TEntity>().Count();
     183:         }
     184:   
     185:         public int Count<TEntity>
     186:         (Expression<Func<TEntity, bool>> criteria) where TEntity : class
     187:         {
     188:             return GetQuery<TEntity>().Count(criteria);
     189:         }
     190:   
     191:         #endregion
     192:   
     193:         #region Data Transactional Methods
     194:   
     195:         public void Add<TEntity>(TEntity entity) where TEntity : class
     196:         {
     197:             if (entity == null)
     198:             {
     199:                 throw new ArgumentNullException("entity");
     200:             }
     201:             DbContext.Set<TEntity>().Add(entity);
     202:         }
     203:   
     204:         public void Attach<TEntity>(TEntity entity) where TEntity : class
     205:         {
     206:             if (entity == null)
     207:             {
     208:                 throw new ArgumentNullException("entity");
     209:             }
     210:   
     211:             DbContext.Set<TEntity>().Attach(entity);
     212:         }
     213:   
     214:         public void Update<TEntity>(TEntity entity) where TEntity : class
     215:         {
     216:             string fqen = GetEntityName<TEntity>();
     217:   
     218:             object originalItem;
     219:             EntityKey key =
     220:             ((IObjectContextAdapter)DbContext).ObjectContext.CreateEntityKey(fqen, entity);
     221:             if (((IObjectContextAdapter)DbContext).ObjectContext.TryGetObjectByKey
     222:             (key, out originalItem))
     223:             {
     224:                 ((IObjectContextAdapter)DbContext).ObjectContext.ApplyCurrentValues
     225:                 (key.EntitySetName, entity);
     226:             }
     227:         }
     228:   
     229:         public void Delete<TEntity>(TEntity entity) where TEntity : class
     230:         {
     231:             if (entity == null)
     232:             {
     233:                 throw new ArgumentNullException("entity");
     234:             }
     235:             DbContext.Set<TEntity>().Remove(entity);
     236:         }
     237:   
     238:         public void Delete<TEntity>(Expression<Func<TEntity,
     239:         bool>> criteria) where TEntity : class
     240:         {
     241:             IEnumerable<TEntity> records = Find(criteria);
     242:   
     243:             foreach (TEntity record in records)
     244:             {
     245:                 Delete(record);
     246:             }
     247:         }
     248:   
     249:         #endregion
     250:   
     251:         #region Internal Processing Private Methods
     252:   
     253:         private EntityKey GetEntityKey<TEntity>(object keyValue) where TEntity : class
     254:         {
     255:             string entitySetName = GetEntityName<TEntity>();
     256:             ObjectSet<TEntity> objectSet =
     257:             ((IObjectContextAdapter)DbContext).ObjectContext.CreateObjectSet<TEntity>();
     258:             string keyPropertyName = objectSet.EntitySet.ElementType.KeyMembers[0].ToString();
     259:             var entityKey = new EntityKey
     260:             (entitySetName, new[] { new EntityKeyMember(keyPropertyName, keyValue) });
     261:             return entityKey;
     262:         }
     263:   
     264:         private string GetEntityName<TEntity>() where TEntity : class
     265:         {
     266:             // Thanks to Kamyar Paykhan -
     267:             // http://huyrua.wordpress.com/2011/04/13/
     268:             // entity-framework-4-poco-repository-and-specification-pattern-upgraded-to-ef-4-1/
     269:             // #comment-688
     270:             string entitySetName = ((IObjectContextAdapter)DbContext).ObjectContext
     271:                 .MetadataWorkspace
     272:                 .GetEntityContainer(((IObjectContextAdapter)DbContext).
     273:                     ObjectContext.DefaultContainerName,
     274:                     DataSpace.CSpace)
     275:                 .BaseEntitySets.Where(bes => bes.ElementType.Name == typeof(TEntity).Name).First().Name;
     276:             return string.Format("{0}.{1}",
     277:             ((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName,
     278:                 entitySetName);
     279:         }
     280:   
     281:         private string RemoveAccent(string txt)
     282:         {
     283:             byte[] bytes = System.Text.Encoding.GetEncoding("Cyrillic").GetBytes(txt);
     284:             return System.Text.Encoding.ASCII.GetString(bytes);
     285:         }
     286:   
     287:         private bool IsValidTag(string tag, string tags)
     288:         {
     289:             string[] allowedTags = tags.Split(',');
     290:             if (tag.IndexOf("javascript") >= 0) return false;
     291:             if (tag.IndexOf("vbscript") >= 0) return false;
     292:             if (tag.IndexOf("onclick") >= 0) return false;
     293:   
     294:             var endchars = new char[] { ' ', '>', '/', '\t' };
     295:   
     296:             int pos = tag.IndexOfAny(endchars, 1);
     297:             if (pos > 0) tag = tag.Substring(0, pos);
     298:             if (tag[0] == '/') tag = tag.Substring(1);
     299:   
     300:             foreach (string aTag in allowedTags)
     301:             {
     302:                 if (tag == aTag) return true;
     303:             }
     304:   
     305:             return false;
     306:         }
     307:   
     308:         #endregion
     309:   
     310:         #region Disposing Methods
     311:   
     312:         protected void Dispose(bool bDisposing)
     313:         {
     314:             if (!bDisposed)
     315:             {
     316:                 if (bDisposing)
     317:                 {
     318:                     if (null != context)
     319:                     {
     320:                         context.Dispose();
     321:                     }
     322:                 }
     323:                 bDisposed = true;
     324:             }
     325:         }
     326:   
     327:         public void Close()
     328:         {
     329:             Dispose(true);
     330:             GC.SuppressFinalize(this);
     331:         }
     332:   
     333:         #endregion
     334:     }
  3. IUnitOfWork 接口

       1:      public interface IUnitOfWork : IDisposable
       2:      {
       3:          void SaveChanges();
       4:      }
  4. UnitOfWork 类

       1:      internal class UnitOfWork : IUnitOfWork
       2:      {
       3:          private readonly DbContext _dbContext;
       4:   
       5:          public UnitOfWork(DbContext context)
       6:          {
       7:              _dbContext = context;
       8:          }
       9:   
      10:          public void SaveChanges()
      11:          {
      12:              ((IObjectContextAdapter)_dbContext).ObjectContext.SaveChanges();
      13:          }
      14:   
      15:          #region Implementation of IDisposable
      16:   
      17:          private bool _disposed;
      18:   
      19:          /// <summary>
      20:          ///     Performs application-defined tasks associated with freeing, 
      21:          ///     releasing, or resetting unmanaged resources.
      22:          /// </summary>
      23:          public void Dispose()
      24:          {
      25:              Dispose(true);
      26:              GC.SuppressFinalize(this);
      27:          }
      28:   
      29:          /// <summary>
      30:          ///     Disposes off the managed and unmanaged resources used.
      31:          /// </summary>
      32:          /// <param name="disposing"></param>
      33:          private void Dispose(bool disposing)
      34:          {
      35:              if (!disposing)
      36:                  return;
      37:   
      38:              if (_disposed)
      39:                  return;
      40:   
      41:              _disposed = true;
      42:          }
      43:   
      44:          #endregion
      45:      }

在Mayur.DAL.Core文件夹中,还有一个抽象类,包含了一些项目中常用到的公共方法,如果你也有一些新的方法函数是我们在项目中需要的,请在评论中提出建议(原文这么说的,在我这评论我也不介意)。

  1. GlobalCommonHelper.cs 类

       1:   abstract public class GlobalCommonHelper
       2:      {
       3:          #region General Methods
       4:   
       5:          /// <summary>
       6:          /// Take any string and encrypt it using SHA1 then
       7:          /// return the encrypted data
       8:          /// </summary>
       9:          /// <param name="data">input text you will enterd to encrypt it</param>
      10:          /// <returns>return the encrypted text as hexadecimal string</returns>
      11:          public string GetSHA1HashData(string data)
      12:          {
      13:              //create new instance of md5
      14:              SHA1 sha1 = SHA1.Create();
      15:   
      16:              //convert the input text to array of bytes
      17:              byte[] hashData = sha1.ComputeHash(Encoding.Default.GetBytes(data));
      18:   
      19:              //create new instance of StringBuilder to save hashed data
      20:              StringBuilder returnValue = new StringBuilder();
      21:   
      22:              //loop for each byte and add it to StringBuilder
      23:              for (int i = 0; i < hashData.Length; i++)
      24:              {
      25:                  returnValue.Append(hashData[i].ToString());
      26:              }
      27:   
      28:              // return hexadecimal string
      29:              return returnValue.ToString();
      30:          }
      31:   
      32:          /// <summary>
      33:          /// Creates a slug url from string .
      34:          /// </summary>
      35:          /// <param name="phrase"></param>
      36:          /// <returns></returns>
      37:          public string GetSlugURLFromString(string phrase)
      38:          {
      39:              string str = RemoveAccent(phrase).ToLower();
      40:              // invalid chars          
      41:              str = Regex.Replace(str, @"[^a-z0-9\s-]", "");
      42:              // convert multiple spaces into one space  
      43:              str = Regex.Replace(str, @"\s+", " ").Trim();
      44:              // cut and trim
      45:              str = str.Substring(0, str.Length <= 45 ? str.Length : 45).Trim();
      46:              str = Regex.Replace(str, @"\s", "-"); // hyphens  
      47:              return str;
      48:          }
      49:   
      50:          /// <summary>
      51:          /// Delete file by specified path.
      52:          /// </summary>
      53:          /// <param name="path">path of file.</param>
      54:          public void DeleteTargetFile(string path)
      55:          {
      56:              if (File.Exists(path))
      57:              {
      58:                  File.SetAttributes(path, FileAttributes.Normal);
      59:                  File.Delete(path);
      60:              }
      61:          }
      62:   
      63:          /// <summary>
      64:          /// Sent email to target email address with attachment.
      65:          /// </summary>
      66:          /// <param name="toEmail">Email addresses of 
      67:          /// one or multiple receipients semi colon (;) separated values.</param>
      68:          /// <param name="subject">Email subject</param>
      69:          /// <param name="body">Email body</param>
      70:          /// <returns>True | False</returns>
      71:          public bool SendEmailToTarget(string toEmail, string subject, string body)
      72:          {
      73:   
      74:              bool success = false;
      75:              try
      76:              {
      77:                  SmtpClient SmtpServer = new SmtpClient();
      78:                  MailMessage mail = new MailMessage();
      79:   
      80:                  SmtpServer.Credentials = new NetworkCredential(
      81:                      Convert.ToString(ConfigurationManager.AppSettings["fromEmail"]),
      82:                      Convert.ToString(ConfigurationManager.AppSettings["fromPassword"]));
      83:   
      84:                  SmtpServer.Host = Convert.ToString
      85:                  (ConfigurationManager.AppSettings["hostName"]);
      86:                  SmtpServer.Port = Convert.ToInt32
      87:                  (ConfigurationManager.AppSettings["portNumber"]);
      88:   
      89:                  if (Convert.ToBoolean
      90:                  (ConfigurationManager.AppSettings["isEnableSSL"]) == true)
      91:                      SmtpServer.EnableSsl = true;
      92:   
      93:                  mail.From = new MailAddress(Convert.ToString
      94:                  (ConfigurationManager.AppSettings["senderName"]));
      95:   
      96:                  string[] multiEmails = toEmail.Split(';');
      97:                  foreach (string email in multiEmails)
      98:                  {
      99:                      mail.To.Add(email);
     100:                  }
     101:   
     102:                  mail.Subject = subject;
     103:                  mail.IsBodyHtml = true;
     104:                  mail.Body = body;
     105:                  SmtpServer.Send(mail);
     106:                  mail.Dispose();
     107:                  success = true;
     108:              }
     109:              catch (Exception)
     110:              {
     111:                  success = false;
     112:              }
     113:              return success;
     114:          }
     115:   
     116:          /// <summary>
     117:          /// Sent email to target email address with attachment.
     118:          /// </summary>
     119:          /// <param name="toEmail">Email addresses of 
     120:          /// one or multiple receipients semi colon (;) separated values.</param>
     121:          /// <param name="subject">Email subject</param>
     122:          /// <param name="body">Email body</param>
     123:          /// <param name="body">Email attachment file path</param>
     124:          /// <returns>True | False</returns>
     125:          public bool SendEmailToTarget(string toEmail, string subject, string body, string attachmentPath)
     126:          {
     127:   
     128:              bool success = false;
     129:              try
     130:              {
     131:                  SmtpClient SmtpServer = new SmtpClient();
     132:                  MailMessage mail = new MailMessage();
     133:   
     134:                  SmtpServer.Credentials = new NetworkCredential(
     135:                      Convert.ToString(ConfigurationManager.AppSettings["fromEmail"]),
     136:                      Convert.ToString(ConfigurationManager.AppSettings["fromPassword"]));
     137:   
     138:                  SmtpServer.Host = Convert.ToString
     139:                  (ConfigurationManager.AppSettings["hostName"]);
     140:                  SmtpServer.Port = Convert.ToInt32
     141:                  (ConfigurationManager.AppSettings["portNumber"]);
     142:   
     143:                  if (Convert.ToBoolean(ConfigurationManager.AppSettings["isEnableSSL"]) == true)
     144:                      SmtpServer.EnableSsl = true;
     145:   
     146:                  mail.From = new MailAddress(Convert.ToString
     147:                  (ConfigurationManager.AppSettings["senderName"]));
     148:   
     149:                  string[] multiEmails = toEmail.Split(';');
     150:                  foreach (string email in multiEmails)
     151:                  {
     152:                      mail.To.Add(email);
     153:                  }
     154:   
     155:                  Attachment attachment;
     156:                  attachment = new System.Net.Mail.Attachment(attachmentPath);
     157:                  mail.Attachments.Add(attachment);
     158:   
     159:                  mail.Subject = subject;
     160:                  mail.IsBodyHtml = true;
     161:                  mail.Body = body;
     162:                  SmtpServer.Send(mail);
     163:                  mail.Dispose();
     164:                  success = true;
     165:              }
     166:              catch (Exception)
     167:              {
     168:                  success = false;
     169:              }
     170:              return success;
     171:          }
     172:   
     173:          /// <summary>
     174:          /// Strips tags
     175:          /// </summary>
     176:          /// <param name="text">Text</param>
     177:          /// <returns>Formatted text</returns>
     178:          public string RemoveHtmlFromString(string text)
     179:          {
     180:              if (String.IsNullOrEmpty(text))
     181:              {
     182:                  return string.Empty;
     183:              }
     184:              else{
     185:                  
     186:   
     187:              text = Regex.Replace(text, @"(>)(\r|\n)*(<)", "><");
     188:              text = Regex.Replace(text, "(<[^>]*>)([^<]*)", "$2");
     189:              text = Regex.Replace(text, "(&#x?[0-9]{2,4};||&| |<|>|&euro;|&copy;|&reg;|&permil;|&Dagger;|&dagger;|&lsaquo;|&rsaquo;|&bdquo;|&rdquo;|&ldquo;|&sbquo;|&rsquo;|&lsquo;|&mdash;|&ndash;|&rlm;|&lrm;|&zwj;|&zwnj;|&thinsp;|&emsp;|&ensp;|&tilde;|&circ;|&Yuml;|&scaron;|&Scaron;)", "@");
     190:              return text;
     191:  }
     192:          }
     193:   
     194:          /// <summary>
     195:          /// Verifies that a string is in valid e-mail format
     196:          /// </summary>
     197:          /// <param name="email">Email to verify</param>
     198:          /// <returns>true if the string is a valid e-mail address and false if it's not</returns>
     199:          public bool IsValidEmail(string email)
     200:          {
     201:              if (String.IsNullOrEmpty(email))
     202:                  return false;
     203:   
     204:              email = email.Trim();
     205:              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);
     206:              return result;
     207:          }
     208:   
     209:          /// <summary>
     210:          /// Returns Allowed HTML only.
     211:          /// </summary>
     212:          /// <param name="text">Text</param>
     213:          /// <returns>Allowed HTML</returns>
     214:          public string EnsureOnlyAllowedHtml(string text)
     215:          {
     216:              if (String.IsNullOrEmpty(text))
     217:                  return string.Empty;
     218:   
     219:              const string allowedTags = "br,hr,b,i,u,a,div,ol,ul,li,blockquote,img,span,p,em," +
     220:                                          "strong,font,pre,h1,h2,h3,h4,h5,h6,address,cite";
     221:   
     222:              var m = Regex.Matches(text, "<.*?>", RegexOptions.IgnoreCase);
     223:              for (int i = m.Count - 1; i >= 0; i--)
     224:              {
     225:                  string tag = text.Substring(m[i].Index + 1, m[i].Length - 1).Trim().ToLower();
     226:   
     227:                  if (!IsValidTag(tag, allowedTags))
     228:                  {
     229:                      text = text.Remove(m[i].Index, m[i].Length);
     230:                  }
     231:              }
     232:   
     233:              return text;
     234:          }
     235:   
     236:          #endregion
     237:   
     238:          #region Internal Processing Private Methods
     239:   
     240:          private string RemoveAccent(string txt)
     241:          {
     242:              byte[] bytes = System.Text.Encoding.GetEncoding("Cyrillic").GetBytes(txt);
     243:              return System.Text.Encoding.ASCII.GetString(bytes);
     244:          }
     245:   
     246:          private bool IsValidTag(string tag, string tags)
     247:          {
     248:              string[] allowedTags = tags.Split(',');
     249:              if (tag.IndexOf("javascript") >= 0) return false;
     250:              if (tag.IndexOf("vbscript") >= 0) return false;
     251:              if (tag.IndexOf("onclick") >= 0) return false;
     252:   
     253:              var endchars = new char[] { ' ', '>', '/', '\t' };
     254:   
     255:              int pos = tag.IndexOfAny(endchars, 1);
     256:              if (pos > 0) tag = tag.Substring(0, pos);
     257:              if (tag[0] == '/') tag = tag.Substring(1);
     258:   
     259:              foreach (string aTag in allowedTags)
     260:              {
     261:                  if (tag == aTag) return true;
     262:              }
     263:   
     264:              return false;
     265:          }
     266:   
     267:          #endregion
     268:   
     269:      }

现在通用仓储已经具备了公共的方法,现在来看下我们怎么在控制器中使用它。假设我们有一个Student实体,包含studentID, name, rollNo 等列,下面是控制器中的代码

  1. 在继续之前,我们需要先完善数据上下文信息,以生成数据库和数据表 ,如下:

       1:      public class MyFirstDbContext:DbContext
       2:      {
       3:          public MyFirstDbContext()
       4:              : base("name=MyFirstDbContext")
       5:          {
       6:              Database.SetInitializer<MyFirstDbContext>(null);
       7:          }
       8:   
       9:          public virtual DbSet<Book> Books { get; set; }
      10:   
      11:          protected override void OnModelCreating(DbModelBuilder modelBuilder)
      12:          {
      13:   
      14:          }
      15:   
      16:      }
  2. 我在数据访问层创建了一个抽象的包含了每个项目中都要使用的公共方法的类。因为是一个抽象类,这意味着我们为它创建实例。所以我们需要创建一个新的类在Web项目中,这个类继承于GlobalCommonHelper类,命名为CommonHelper,我们可以在这个类中实现一些项目独有的公共方法。

       1:      public class CommonHelper : GlobalCommonHelper
       2:      {
       3:          public int PageSize = 25;
       4:          //Some common functions. Only Project-specific.     
       5:      }
  3. 现在我们可以看下如何在我们的控制器中使用仓储类,看控制器中的代码:

       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)

MVC通用仓储类的更多相关文章

  1. (译文)MVC通用仓储类

    Generic Repository Pattern MVC Generic Repository Pattern MVC 原文链接:http://www.codeproject.com/Articl ...

  2. 用MVC5+EF6+WebApi 做一个考试功能(六) 仓储模式 打造EF通用仓储类

    前言 年底工作比较忙,年度总结还没写,项目要上线,回老家过年各种准备.尤其是给长辈给侄子侄女准备礼物头都大了. 原来想年前先出一版能用的,我看有点悬了,尽量先把大体功能弄出来,扔掉一些,保证能考试,然 ...

  3. MVC5+EF6 入门完整教程十一:细说MVC中仓储模式的应用

    摘要: 第一阶段1~10篇已经覆盖了MVC开发必要的基本知识. 第二阶段11-20篇将会侧重于专题的讲解,一篇文章解决一个实际问题. 根据园友的反馈, 本篇文章将会先对呼声最高的仓储模式进行讲解. 文 ...

  4. MVC5+EF6 入门完整教程11--细说MVC中仓储模式的应用

    摘要: 第一阶段1~10篇已经覆盖了MVC开发必要的基本知识. 第二阶段11-20篇将会侧重于专题的讲解,一篇文章解决一个实际问题. 根据园友的反馈, 本篇文章将会先对呼声最高的仓储模式进行讲解. 文 ...

  5. 细说MVC中仓储模式的应用

    文章提纲 概述要点 理论基础 详细步骤 总结 概述要点 设计模式的产生,就是在对开发过程进行不断的抽象. 我们先看一下之前访问数据的典型过程. 在Controller中定义一个Context, 例如: ...

  6. [.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类

    [.net 面向对象程序设计进阶] (13) 序列化(Serialization)(五) Json 序列化利器 Newtonsoft.Json 及 通用Json类 本节导读: 关于JSON序列化,不能 ...

  7. [.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口 IXmlSerializable 实现XML序列化 及 通用XML类

    [.net 面向对象程序设计进阶] (11) 序列化(Serialization)(三) 通过接口 IXmlSerializable 实现XML序列化 及 通用XML类 本节导读:本节主要介绍通过序列 ...

  8. 通用窗口类 Inventory Pro 2.1.2 Demo1(下续篇 ),物品消耗扇形显示功能

    本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...

  9. 通用窗口类 Inventory Pro 2.1.2 Demo1(下)

    本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如No ...

随机推荐

  1. Pie Chart _Study

    1.Select a theme 2.Experiment with visual customization 3.Creat a script 4.Name should be as defined ...

  2. HihoCoder第一周与POJ3974:最长回文字串

    这个题目是hihoCoder第一周的题目,自己打算从第一周开始做起,不知道能追上多少,更不知道这一篇写完,下一篇会是什么时候... 题意很简单. 输入: abababa aaaabaa acacdas ...

  3. Maven添加Tomcat插件实现热部署

    Maven热部署,顾名思义就是可以不影响项目在服务器中的运行情况,可以实现项目代码的更新,减少启动,编译时间,达到快速开发的目的,也不需要手动拷贝war包到远程项目,可以直接将项目以及war包部署到远 ...

  4. CSAPP读书笔记--第八章 异常控制流

    第八章 异常控制流 2017-11-14 概述 控制转移序列叫做控制流.目前为止,我们学过两种改变控制流的方式: 1)跳转和分支: 2)调用和返回. 但是上面的方法只能控制程序本身,发生以下系统状态的 ...

  5. mysql limit查询入门

  6. javascript中退出语句break,continue和return 比较

    在 break,continue和return 三个关键字中, break,continue是一起的,return 是函数返回语句,但是返回的同时也将函数停止. 首先:break和continue两个 ...

  7. git仓库拆分

    例如: # 这就是那个大仓库 big-project $ git clone git@github.com:tom/big-project.git $ cd big-project # 把所有 `co ...

  8. 在远程服务器上执行本地的shell脚本

    在远程服务器上执行本地的shell脚本 [root@localhost zzx]# sh echoip.sh 192.168.67.131[root@localhost zzx]# ssh root@ ...

  9. Solve Error: Could not find the certificate xxxx.com. at ServerlessCustomDomain.<anonymous>

    When runs "serverless create_domain", we may get the following error: Could not find the c ...

  10. 十二、JavaScript之变量申明

    一.代码如下 二.运行效果如下 <!DOCTYPE html> <html> <meta http-equiv="Content-Type" cont ...