NopCommerce-EntityFramework开发:主要是Controller-Service-Repository的开发方式

操作数据库,主要对象是BaseEntity,IDbContext和NopObjectContext,IRepository和EfRepository,NopEntityTypeConfiguration(可以使用EntityFramework提供的EntityTypeConfiguration不使用NopCommerce拓展的NopEntityTypeConfiguration)

以下代码GitBub地址:https://github.com/heshengli/nop

BaseEntity,实体基类

/// <summary>
/// Base class for entities
/// </summary>
public abstract partial class BaseEntity
{
/// <summary>
/// Gets or sets the entity identifier
/// </summary>
public int Id { get; set; } public override bool Equals(object obj)
{
return Equals(obj as BaseEntity);
} private static bool IsTransient(BaseEntity obj)
{
return obj != null && Equals(obj.Id, default(int));
} private Type GetUnproxiedType()
{
return GetType();
} public virtual bool Equals(BaseEntity other)
{
if (other == null)
return false; if (ReferenceEquals(this, other))
return true; if (!IsTransient(this) &&
!IsTransient(other) &&
Equals(Id, other.Id))
{
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
} return false;
} public override int GetHashCode()
{
if (Equals(Id, default(int)))
return base.GetHashCode();
return Id.GetHashCode();
} public static bool operator ==(BaseEntity x, BaseEntity y)
{
return Equals(x, y);
} public static bool operator !=(BaseEntity x, BaseEntity y)
{
return !(x == y);
}
}

IDbContext,数据上下文接口

public interface IDbContext
{
/// <summary>
/// Get DbSet
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <returns>DbSet</returns>
IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity; /// <summary>
/// Save changes
/// </summary>
/// <returns></returns>
int SaveChanges(); /// <summary>
/// Execute stores procedure and load a list of entities at the end
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="commandText">Command text</param>
/// <param name="parameters">Parameters</param>
/// <returns>Entities</returns>
IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
where TEntity : BaseEntity, new(); /// <summary>
/// Creates a raw SQL query that will return elements of the given generic type. The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
/// </summary>
/// <typeparam name="TElement">The type of object returned by the query.</typeparam>
/// <param name="sql">The SQL query string.</param>
/// <param name="parameters">The parameters to apply to the SQL query string.</param>
/// <returns>Result</returns>
IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters); /// <summary>
/// Executes the given DDL/DML command against the database.
/// </summary>
/// <param name="sql">The command string</param>
/// <param name="doNotEnsureTransaction">false - the transaction creation is not ensured; true - the transaction creation is ensured.</param>
/// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
/// <param name="parameters">The parameters to apply to the command string.</param>
/// <returns>The result returned by the database after executing the command.</returns>
int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters); /// <summary>
/// Detach an entity
/// </summary>
/// <param name="entity">Entity</param>
void Detach(object entity); /// <summary>
/// Gets or sets a value indicating whether proxy creation setting is enabled (used in EF)
/// </summary>
bool ProxyCreationEnabled { get; set; } /// <summary>
/// Gets or sets a value indicating whether auto detect changes setting is enabled (used in EF)
/// </summary>
bool AutoDetectChangesEnabled { get; set; }
}

IRepository,基础操作接口

/// <summary>
/// Repository
/// </summary>
public partial interface IRepository<T> where T : BaseEntity
{
/// <summary>
/// Get entity by identifier
/// </summary>
/// <param name="id">Identifier</param>
/// <returns>Entity</returns>
T GetById(object id); /// <summary>
/// Insert entity
/// </summary>
/// <param name="entity">Entity</param>
void Insert(T entity); /// <summary>
/// Insert entities
/// </summary>
/// <param name="entities">Entities</param>
void Insert(IEnumerable<T> entities); /// <summary>
/// Update entity
/// </summary>
/// <param name="entity">Entity</param>
void Update(T entity); /// <summary>
/// Update entities
/// </summary>
/// <param name="entities">Entities</param>
void Update(IEnumerable<T> entities); /// <summary>
/// Delete entity
/// </summary>
/// <param name="entity">Entity</param>
void Delete(T entity); /// <summary>
/// Delete entities
/// </summary>
/// <param name="entities">Entities</param>
void Delete(IEnumerable<T> entities); /// <summary>
/// Delete entity by id
/// </summary>
/// <param name="id"></param>
void Delete(int id); /// <summary>
/// Gets a table
/// </summary>
IQueryable<T> Table { get; } /// <summary>
/// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
/// </summary>
IQueryable<T> TableNoTracking { get; }
}

NopObjectContext,继承DbContext, IDbContext,具体实现

/// <summary>
/// Object context
/// </summary>
public class NopObjectContext : DbContext, IDbContext
{
#region Ctor public NopObjectContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
//是否开启懒加载
//((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true;
} #endregion #region Utilities protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//dynamically load all configuration
//System.Type configType = typeof(LanguageMap); //any of your configuration classes here
//var typesToRegister = Assembly.GetAssembly(configType).GetTypes() var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => type.BaseType != null && type.BaseType.IsGenericType &&
type.BaseType.GetGenericTypeDefinition() == typeof(NopEntityTypeConfiguration<>)); //ef自带对象
//var typesToRegister2 = Assembly.GetExecutingAssembly().GetTypes()
//.Where(type => !String.IsNullOrEmpty(type.Namespace))
//.Where(type => type.BaseType != null && type.BaseType.IsGenericType &&
//type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); foreach (var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
//...or do it manually below. For example,
//modelBuilder.Configurations.Add(new LanguageMap()); //移除复数表名的约定
//modelBuilder.Conventions.Remove<PluralizingEntitySetNameConvention>(); //检测数据库是否存在
//Database.SetInitializer<ObjectContext>(null); base.OnModelCreating(modelBuilder);
} /// <summary>
/// Attach an entity to the context or return an already attached entity (if it was already attached)
/// </summary>
/// <typeparam name="TEntity">TEntity</typeparam>
/// <param name="entity">Entity</param>
/// <returns>Attached entity</returns>
protected virtual TEntity AttachEntityToContext<TEntity>(TEntity entity) where TEntity : BaseEntity, new()
{
//little hack here until Entity Framework really supports stored procedures
//otherwise, navigation properties of loaded entities are not loaded until an entity is attached to the context
var alreadyAttached = Set<TEntity>().Local.FirstOrDefault(x => x.Id == entity.Id);
if (alreadyAttached == null)
{
//attach new entity
Set<TEntity>().Attach(entity);
return entity;
} //entity is already loaded
return alreadyAttached;
} #endregion #region Methods /// <summary>
/// Create database script
/// </summary>
/// <returns>SQL to generate database</returns>
public string CreateDatabaseScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
} /// <summary>
/// Get DbSet
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <returns>DbSet</returns>
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
} /// <summary>
/// Execute stores procedure and load a list of entities at the end
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="commandText">Command text</param>
/// <param name="parameters">Parameters</param>
/// <returns>Entities</returns>
public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
//add parameters to command
if (parameters != null && parameters.Length > 0)
{
for (int i = 0; i <= parameters.Length - 1; i++)
{
var p = parameters[i] as DbParameter;
if (p == null)
throw new Exception("Not support parameter type"); commandText += i == 0 ? " " : ", "; commandText += "@" + p.ParameterName;
if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output)
{
//output parameter
commandText += " output";
}
}
} var result = this.Database.SqlQuery<TEntity>(commandText, parameters).ToList(); //performance hack applied as described here - http://www.nopcommerce.com/boards/t/25483/fix-very-important-speed-improvement.aspx
bool acd = this.Configuration.AutoDetectChangesEnabled;
try
{
this.Configuration.AutoDetectChangesEnabled = false; for (int i = 0; i < result.Count; i++)
result[i] = AttachEntityToContext(result[i]);
}
finally
{
this.Configuration.AutoDetectChangesEnabled = acd;
} return result;
} /// <summary>
/// Creates a raw SQL query that will return elements of the given generic type. The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
/// </summary>
/// <typeparam name="TElement">The type of object returned by the query.</typeparam>
/// <param name="sql">The SQL query string.</param>
/// <param name="parameters">The parameters to apply to the SQL query string.</param>
/// <returns>Result</returns>
public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
{
return this.Database.SqlQuery<TElement>(sql, parameters);
} /// <summary>
/// Executes the given DDL/DML command against the database.
/// </summary>
/// <param name="sql">The command string</param>
/// <param name="doNotEnsureTransaction">false - the transaction creation is not ensured; true - the transaction creation is ensured.</param>
/// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
/// <param name="parameters">The parameters to apply to the command string.</param>
/// <returns>The result returned by the database after executing the command.</returns>
public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters)
{
int? previousTimeout = null;
if (timeout.HasValue)
{
//store previous timeout
previousTimeout = ((IObjectContextAdapter)this).ObjectContext.CommandTimeout;
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = timeout;
} var transactionalBehavior = doNotEnsureTransaction
? TransactionalBehavior.DoNotEnsureTransaction
: TransactionalBehavior.EnsureTransaction;
var result = this.Database.ExecuteSqlCommand(transactionalBehavior, sql, parameters); if (timeout.HasValue)
{
//Set previous timeout back
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = previousTimeout;
} //return result
return result;
} /// <summary>
/// Detach an entity
/// </summary>
/// <param name="entity">Entity</param>
public void Detach(object entity)
{
if (entity == null)
throw new ArgumentNullException("entity"); ((IObjectContextAdapter)this).ObjectContext.Detach(entity);
} #endregion #region Properties /// <summary>
/// Gets or sets a value indicating whether proxy creation setting is enabled (used in EF)
/// </summary>
public virtual bool ProxyCreationEnabled
{
get
{
return this.Configuration.ProxyCreationEnabled;
}
set
{
this.Configuration.ProxyCreationEnabled = value;
}
} /// <summary>
/// Gets or sets a value indicating whether auto detect changes setting is enabled (used in EF)
/// </summary>
public virtual bool AutoDetectChangesEnabled
{
get
{
return this.Configuration.AutoDetectChangesEnabled;
}
set
{
this.Configuration.AutoDetectChangesEnabled = value;
}
} #endregion
}

EfRepository,IRepository接口的具体实现

/// <summary>
/// Entity Framework repository
/// </summary>
public partial class EfRepository<T> : IRepository<T> where T : BaseEntity
{
#region Fields private readonly IDbContext _context;
private IDbSet<T> _entities; #endregion #region Ctor /// <summary>
/// Ctor
/// </summary>
/// <param name="context">Object context</param>
public EfRepository(IDbContext context)
{
this._context = context;
} #endregion #region Utilities /// <summary>
/// Get full error
/// </summary>
/// <param name="exc">Exception</param>
/// <returns>Error</returns>
protected string GetFullErrorText(DbEntityValidationException exc)
{
var msg = string.Empty;
foreach (var validationErrors in exc.EntityValidationErrors)
foreach (var error in validationErrors.ValidationErrors)
msg += string.Format("Property: {0} Error: {1}", error.PropertyName, error.ErrorMessage) + Environment.NewLine;
return msg;
} #endregion #region Methods /// <summary>
/// Get entity by identifier
/// </summary>
/// <param name="id">Identifier</param>
/// <returns>Entity</returns>
public virtual T GetById(object id)
{
//see some suggested performance optimization (not tested)
//http://stackoverflow.com/questions/11686225/dbset-find-method-ridiculously-slow-compared-to-singleordefault-on-id/11688189#comment34876113_11688189
return this.Entities.Find(id);
} /// <summary>
/// Insert entity
/// </summary>
/// <param name="entity">Entity</param>
public virtual void Insert(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity"); this.Entities.Add(entity); this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
throw new Exception(GetFullErrorText(dbEx), dbEx);
}
} /// <summary>
/// Insert entities
/// </summary>
/// <param name="entities">Entities</param>
public virtual void Insert(IEnumerable<T> entities)
{
try
{
if (entities == null)
throw new ArgumentNullException("entities"); foreach (var entity in entities)
this.Entities.Add(entity); this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
throw new Exception(GetFullErrorText(dbEx), dbEx);
}
} /// <summary>
/// Update entity
/// </summary>
/// <param name="entity">Entity</param>
public virtual void Update(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity"); this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
throw new Exception(GetFullErrorText(dbEx), dbEx);
}
} /// <summary>
/// Update entities
/// </summary>
/// <param name="entities">Entities</param>
public virtual void Update(IEnumerable<T> entities)
{
try
{
if (entities == null)
throw new ArgumentNullException("entities"); this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
throw new Exception(GetFullErrorText(dbEx), dbEx);
}
} /// <summary>
/// Delete entity
/// </summary>
/// <param name="entity">Entity</param>
public virtual void Delete(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity"); this.Entities.Remove(entity); this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
throw new Exception(GetFullErrorText(dbEx), dbEx);
}
} /// <summary>
/// Delete entities
/// </summary>
/// <param name="entities">Entities</param>
public virtual void Delete(IEnumerable<T> entities)
{
try
{
if (entities == null)
throw new ArgumentNullException("entities"); foreach (var entity in entities)
this.Entities.Remove(entity); this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
throw new Exception(GetFullErrorText(dbEx), dbEx);
}
} public void Delete(int id)
{
try
{
T entity = this.Entities.Find(id);
if (entity == null)
throw new ArgumentNullException("entities");
this.Entities.Remove(entity); this._context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
throw new Exception(GetFullErrorText(dbEx), dbEx);
}
} #endregion #region Properties /// <summary>
/// Gets a table
/// </summary>
public virtual IQueryable<T> Table
{
get
{
return this.Entities;
}
} /// <summary>
/// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
/// </summary>
public virtual IQueryable<T> TableNoTracking
{
get
{
return this.Entities.AsNoTracking();
}
} /// <summary>
/// Entities
/// </summary>
protected virtual IDbSet<T> Entities
{
get
{
if (_entities == null)
_entities = _context.Set<T>();
return _entities;
}
} #endregion
}
NopEntityTypeConfiguration,可拓展的抽象类
public abstract class NopEntityTypeConfiguration<T> : EntityTypeConfiguration<T> where T : class
{
protected NopEntityTypeConfiguration()
{
PostInitialize();
} /// <summary>
/// Developers can override this method in custom partial classes
/// in order to add some custom initialization code to constructors
/// </summary>
protected virtual void PostInitialize()
{ }
}

简单demo:

实体基础代码

public class Student : BaseEntity
{
public string Name { get; set; } public string CustomProperty { get; set; }
} /// <summary>
/// 实体映射表nop拓展
/// </summary>
public partial class StudentMap : NopEntityTypeConfiguration<Student>
{
public StudentMap()
{
this.ToTable("Student");
this.HasKey(pa => pa.Id);
this.Property(pa => pa.Name).IsRequired();
}
} /// <summary>
/// 原生ef,需要调整NopObjectContext的代码
/// </summary>
//public partial class StudentMap2 : EntityTypeConfiguration<Student>
//{
// public StudentMap2()
// {
// this.ToTable("Student");
// this.HasKey(pa => pa.Id);
// this.Property(pa => pa.Name).IsRequired();
// }
//} public interface IStudentService
{
void Insert(Student stu);
void Update(Student stu);
void Delete(int id);
IEnumerable<Student> GetList();
} public class StudentService : IStudentService
{
private readonly IRepository<Student> _iStudentRepository;
private readonly IDbContext _iDbContext;
public StudentService()
{
_iDbContext = new NopObjectContext("App");
_iStudentRepository = new EfRepository<Student>(_iDbContext); } //Ioc Autofac 调整
//public StudentService(IRepository<Student> iStudentRepositpory,IDbContext iDbContext)
//{
// _iDbContext = iDbContext;
// _iStudentRepository = iStudentRepositpory;
//} public void Delete(int id)
{
_iStudentRepository.Delete(id);
} public IEnumerable<Student> GetList()
{
return _iStudentRepository.Table.AsEnumerable();
} public void Insert(Student stu)
{
_iStudentRepository.Insert(stu);
} public void Update(Student stu)
{
_iStudentRepository.Update(stu);
}
}

调用代码(使用了简单的consoleApp)

class Program
{
private static readonly IStudentService _iStudentService =
new StudentService(); static void Main(string[] args)
{
Student stu = new Student() {
Name="李四",
CustomProperty="自定义属性2"
};
_iStudentService.Insert(stu);
Console.WriteLine(stu.Name+"");
Console.ReadKey();
}
}

最新文章

  1. 配置apue的头文件apue.h和unp的头文件anp.h
  2. ZeroMQ接口函数之 :zmq_pgm – &#216;MQ 使用PGM 进行可靠的多路传输
  3. asp.net mvc 5 web api 关于Requested resource does not support options 问题
  4. Linq的一些很方便的方法
  5. FlashFXP|FTP
  6. Linux_日志管理介绍(一)
  7. JAVASE02-Unit01: API文档 、 字符串基本操作
  8. 使用my exclipse对数据库进行操作(2)
  9. 利用PPT的WebBroswer控件助力系统汇报演示
  10. C语言杂谈(三)存储类别
  11. iOS开发 落地消息多的处理办法(仅供参考)
  12. Linux,实时获取磁盘空间
  13. 面试题:给定一个长度为N的数组,其中每个元素的取值范围都是1到N。判断数组中是否有重复的数字
  14. Java如何调取创蓝253短信验证码
  15. jenkins自动化工具使用教程(转)
  16. pymongo中的连接操作:Connection()与MongoClient()
  17. Docker镜像命令
  18. shell重定向命令执行顺序
  19. hbase源码带注释版本,放在这里,方便大家下载吧
  20. python中command执行shell命令脚本方法

热门文章

  1. Effective C++ .17 函数调用时的资源管理
  2. order by MetadataToken解决反射字段顺序问题
  3. canvas合成图片 圣诞节新技能戴帽
  4. 28_Future模式1
  5. WPF控件相对位置解析
  6. SQL 截取字符
  7. 【Leetcode】【Medium】Find Peak Element
  8. shell单引号双引号详解
  9. ZT自贴吧 说说你是怎么和恋人确定恋爱关系的?
  10. 使用jMeter构造逻辑上有依赖关系的一系列并发请求