通用仓库模式 MVC






4.79/5 (11投票s)
通用仓库模式 MVC
良好的架构是任何项目的核心。开发人员一直在寻找能够减少重复代码并分离数据访问和业务逻辑的优秀架构。因此,我们将使用 ASP.NET MVC 和 Entity Framework 来创建我们的通用存储库。如果您不了解 Entity Framework,请 点击此处 了解 Entity Framework。因此,在开始存储库模式之前,我们首先需要了解什么是存储库模式?为什么我们需要它?
存储库和单元工作模式
简单来说,存储库意味着您的数据访问和业务逻辑层之间的抽象,这对于单元测试或测试驱动开发 (TDD) 将非常有用。通过使用存储库,您的系统将更加松耦合。
在编程术语中,我们需要为每个存储库或实体创建一个接口。例如,Student
实体有一个接口 (IStudentInterface
),其中声明了所有方法,如 Insert
、Update
、Delete
、Get
,另一个是继承自 IStudentInterface
的类 (StudentRepository
),StudentRepository
将实现接口中声明的所有方法。当我们在控制器中实例化存储库时,我们将使用接口,这样控制器就可以接受实现存储库接口的任何对象的引用。当控制器在 Web 服务器下运行时,它会接收一个与 Entity Framework 一起工作的存储库。
当控制器在单元测试类下运行时,它会接收一个与存储在易于操作以进行测试的数据(例如内存集合)一起工作的存储库。因此,我们可以在单元测试中提供一个模拟存储库。
如果您想查看完整的实现,请参考以下链接
缺点
每次我们都要为每个实体创建存储库,这会导致代码冗余。
Using the Code
现在我们只需要一个数据访问类,它可以接受任何实体并执行所需的操作,例如 CRUD。在研究了大量文章、理论和示例代码后,我发现了一个非常好的通用存储库模式的参考。
我的代码很大程度上基于Huy Nguyen的博客。请参考以下链接
- https://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/
- https://huyrua.wordpress.com/2012/09/16/entity-framework-poco-repository-and-specification-pattern-upgraded-to-ef-5/
我更改了一些代码,并将其添加到新的类库项目中,同时添加了一些每个项目所需的通用函数。现在我可以在任何项目中使用这个库。以下是文件夹结构。
Mayur.DAL
– 包含通用存储库和通用函数的类库项目。
- Core – 文件夹
- GlobalCommonHelper.cs – 一个
abstract
类,其中包含每个项目所需的所有通用函数
- GlobalCommonHelper.cs – 一个
- Repository – 文件夹
- IRepository.cs – 通用存储库的接口
- Repository.cs – 继承自
IRepository
的通用存储库类。所有方法都在其中实现。 - IUnitOfWork.cs – 单元工作类的接口。
- UnitOfWork.cs – 为 Entity Framework 实现
SaveChanges()
方法。当执行任何事务性数据库命令(如Insert
、Update
、Delete
)时,单元工作类非常重要,在调用Savechanges()
方法之前,实体框架不会提交到数据库。
Mayur.Web
– MVC 项目
- Controller – 文件夹
- HomeController.cs – Home控制器,包含
Index
、Create
、Edit
、Delete ActionResult
- HomeController.cs – Home控制器,包含
- Core – 文件夹
- CommonHelper.cs – 继承自Mayur.DAL.Core.GlobalCommonHelper.cs,包含所有与 MVC 项目相关的通用方法
- Model – 文件夹
- Student.cs –
Student Model
,代表student
表
- Student.cs –
- Views – 文件夹
- Index.chtml – 显示所有学生信息的 HTML
- Create.chtml – 创建新学生信息的 HTML
- Edit.cshtml – 更新学生信息的 HTML
- Delete.cshtml – 删除学生信息的 HTML
让我们简要了解一下Mayur.DAL中的每个文件
Repository 文件夹:此文件夹包含所有数据访问逻辑。有 4 个文件,2 个是接口,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 类
/// <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 接口
public interface IUnitOfWork : IDisposable
{
void SaveChanges();
}
4. UnitOfWork 类
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文件夹中,有一个abstract
类,其中包含每个项目所需的**所有**通用函数。如果您有任何需要在每个项目中都需要的**新**函数,请在评论中提出。
5. Mayur.DAL.Core.GlobalCommonHelper.cs 类
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
}
现在我们的通用存储库已准备好,并内置了通用函数。现在我们可以看到如何在控制器中使用它们。假设我们有一个Students
模型,其中包含studentID
、name
、rollNo
列。以下是HomeController
的代码。
- 在我们继续之前,我们正在使用 code-first 方法,以下是我们的
DbContext
类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) { } }
- 我们在数据访问层中创建了一个
abstract
类,其中包含全局通用函数,意味着几乎每个项目都需要该函数。该类是abstract
类,因此我们无法创建其实例。所以我们在 Web 项目中创建了新的CommonHelper.cs类,它继承自Abstract
类,我们可以在这里实现项目特定的通用函数。public class CommonHelper : GlobalCommonHelper { public int PageSize = 25; //Some common functions. Only Project-specific. }
- 现在我们可以看到如何在
HomeController
中使用存储库。HomeController
如下//Constructor public HomeController() { IRepository repository = new Repository(new MyFirstDbContex); CommonHelper helper = new CommonHelper(); }
- Index.cshtml 以表格形式显示所有
Students
。所以ActionResult
如下public ActionResult Index() { return View(repository.GetAll<Students>()); }
- Create.cshtml 创建新的
Students
记录,这意味着它在数据库中插入新记录。public ActionResult Create() { return View(); } [HttpPost] public ActionResult Create(Students studPost) { if(ModelState.IsValid) { repository.Add<Students>(studPost); repository.UnitOfWork.SaveChanges(); } }
- Edit.cshtml 表示编辑视图,我们可以在其中修改记录。
public ActionResult Edit(int id=0) { if(id==0) return HttpNotFound(); Students stud = repository.FindOne<Students>(x=>x.studentID == id); return View(stud); } [HttpPost] public ActionResult Edit(Students studPost) { if(ModelState.IsValid) { repository.Update<Students>(studPost); repository.UnitOfWork.SaveChanges(); } }
- 最后一个是
Delete
,如下[HttpPost] public ActionResult Delete(int id=0) { if(id==0) return HttpNotFound(); Students stud = repository.FindOne<Students>(x=>x.studentID == id); if(stud == null) return HttpNotFound(); repository.Delete<Students>(stud); repository.UnitOfWork.SaveChanges(); }
- Index.cshtml 以表格形式显示所有
我需要您更多的建议和改进。请评论。
修订
- 31/05/2015:文章发布
- 30/04/2016:根据 "Irene Troupansky" 的建议,更新了文章,修复了一些构造函数错误。