(译文)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 接口
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

浙公网安备 33010602011771号