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基础上工作的仓储类。

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

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

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>
/// 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

/// <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

public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}

4. UnitOfWork Class

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

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. 在继续之前,我们需要先完善数据上下文信息,以生成数据库和数据表 ,如下:

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,我们可以在这个类中实现一些项目独有的公共方法。

public class CommonHelper : GlobalCommonHelper
{
public int PageSize = 25;
//Some common functions. Only Project-specific.
}

3. 现在我们可以看下如何在我们的控制器中使用仓储类,看控制器中的代码:

//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

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

  1. MVC通用仓储类

    原文链接:http://www.codeproject.com/Articles/1095323/Generic-Repository-Pattern-MVC 良好的架构师任何项目的核心,开发人员一直 ...

  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. SQL Server中的RAND函数的介绍和区间随机数值函数的实现

        工作中会遇到SQL Server模拟数据生成以及数值列值(如整型.日期和时间数据类型)随机填充等等任务,这些任务中都要使用到随机数.鉴于此,本文将对SQL Server中随机数的使用简单做个总 ...

  2. 烂泥:rsync配置文件详解

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 对于rsync服务器来说,最重要和复杂的就是它的配置了.rsync服务器的配置文件为/etc/rsyncd.conf,其控制认证.访问.日志记录等等. ...

  3. Linux文件和目录

    access() //检查是否调用进程有Access这个文件的权限,如果文件是一个符号链接,会将它解引用,成功返回0,失败返回-1设errno #include <unistd.h> in ...

  4. Mac下开启FTPserver

    开启命令 sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist   关闭命令 sudo -s launchctl unlo ...

  5. 父容器根据子容器高度自适应:设置父容器 height:100%;overflow:hidden;

    父容器根据子容器高度自适应:设置父容器  height:100%;overflow:hidden;

  6. linux监控命令nc用法

    一.nc命令检测端口的用法 # nc -v -w 10 %IP% -z %PORT% -v 显示指令执行过程. -w <超时秒数> 设置等待连线的时间. -u 表示使用UDP协议 -z 使 ...

  7. 06章 Struts2国际化

    1:什么是国际化? 国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式.它要求从产品中抽离所有的与语言,国家/地区和文化相关的元素.换言之,应用程序的功 ...

  8. JAVA单例

    单例模式: 1 public class Person{ 2 public static Person per//定义一个静态变量,用来储存当前类的对象 3 private Person()//构造方 ...

  9. Maya 与 Matlab 数据互联插件使用教程

    实验室做网格处理方面的算法,写界面很麻烦,所以有了利用maya和matlab进行数据连通的念头,于是有了这个插件. 这个插件可以把maya的网格数据导入matlab之中,完成计算之后重新返回maya. ...

  10. 机器学习实战--logistic回归

    #encoding:utf-8 from numpy import * def loadDataSet(): #加载数据 dataMat = []; labelMat = [] fr = open(' ...