在讲解之前,先来看看解决方案的架构:
1、在Nop.Core下的Domain里建立一个实体Category;
2、在Nop.Data下的Mapping\Catatog\下建立一个数据表映射CategoryMap:
using System.Data.Entity.ModelConfiguration;
using Nop.Core.Domain.Catalog;
namespace Nop.Data.Mapping.Catalog
{
public partial class CategoryMap : EntityTypeConfiguration<Category>
{
public CategoryMap()
{
this.ToTable("Category");
this.HasKey(c => c.Id);
this.Property(c => c.Name).IsRequired().HasMaxLength(400);
this.Property(c => c.Description).IsMaxLength();
this.Property(c => c.MetaKeywords).HasMaxLength(400);
this.Property(c => c.MetaDescription);
this.Property(c => c.MetaTitle).HasMaxLength(400);
this.Property(c => c.SeName).HasMaxLength(200);
this.Property(c => c.PriceRanges).HasMaxLength(400);
this.Property(c => c.PageSizeOptions).HasMaxLength(200);
}
}
}
3、在Nop.Data下建EF上下文接口IDbContext和对象NopObjectContext:
IDbContext:
using System.Collections.Generic;
using System.Data.Entity;
using Nop.Core;
namespace Nop.Data
{
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;
int SaveChanges();
IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
where TEntity : BaseEntity, new();
}
}
NopObjectContext:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Reflection;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Data.Mapping.Catalog;
//using Nop.Data.Mapping.Localization;
namespace Nop.Data
{
/// <summary>
/// Object context
/// </summary>
public class NopObjectContext : DbContext, IDbContext
{
public NopObjectContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
//((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true;
}
//public DbSet<Category> Category { get; set; }
/// <summary>
/// 自动加载所有的数据表映射类并映射到数据库表 (这个函数是重点,这里就是替代DbSet<T>属性)
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//dynamically load all configuration
System.Type configType = typeof(CategoryMap); //any of your configuration classes here
var typesToRegister = Assembly.GetAssembly(configType).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());
base.OnModelCreating(modelBuilder);
}
/// <summary>
/// 将一个实体附加到Context上和返回已附加这个实体(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.Where(x => x.Id == entity.Id).FirstOrDefault();
if (alreadyAttached == null)
{
//attach new entity
Set<TEntity>().Attach(entity);
return entity;
}
else
{
//entity is already loaded.
return alreadyAttached;
}
}
public string CreateDatabaseScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
}
/// <summary>
/// context附加实体
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <returns></returns>
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
/// <summary>
/// 执行存储过程
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
//HACK: Entity Framework Code First doesn't support doesn't support output parameters
//That's why we have to manually create command and execute it.
//just wait until EF Code First starts support them
//
//More info: http://weblogs.asp.net/dwahlin/archive/2011/09/23/using-entity-framework-code-first-with-stored-procedures-that-have-output-parameters.aspx
bool hasOutputParameters = false;
if (parameters != null)
{
foreach (var p in parameters)
{
var outputP = p as DbParameter;
if (outputP == null)
continue;
if (outputP.Direction == ParameterDirection.InputOutput ||
outputP.Direction == ParameterDirection.Output)
hasOutputParameters = true;
}
}
var context = ((IObjectContextAdapter)(this)).ObjectContext;
if (!hasOutputParameters)
{
//no output parameters
var result = this.Database.SqlQuery<TEntity>(commandText, parameters).ToList();
for (int i = 0; i < result.Count; i++)
result[i] = AttachEntityToContext(result[i]);
return result;
//var result = context.ExecuteStoreQuery<TEntity>(commandText, parameters).ToList();
//foreach (var entity in result)
// Set<TEntity>().Attach(entity);
//return result;
}
else
{
//var connection = context.Connection;
var connection = this.Database.Connection;
//Don't close the connection after command execution
//open the connection for use
if (connection.State == ConnectionState.Closed)
connection.Open();
//create a command object
using (var cmd = connection.CreateCommand())
{
//command to execute
cmd.CommandText = commandText;
cmd.CommandType = CommandType.StoredProcedure;
// move parameters to command object
if (parameters != null)
foreach (var p in parameters)
cmd.Parameters.Add(p);
//database call
var reader = cmd.ExecuteReader();
//return reader.DataReaderToObjectList<TEntity>();
var result = context.Translate<TEntity>(reader).ToList();
for (int i = 0; i < result.Count; i++)
result[i] = AttachEntityToContext(result[i]);
//close up the reader, we're done saving results
reader.Close();
return result;
}
}
}
}
}
4、在Nop.Core下的Data里建立一个接口IRepository,代码如下:
using System.Linq;
namespace Nop.Core.Data
{
/// <summary>
/// Repository
/// </summary>
public partial interface IRepository<T> where T : BaseEntity
{
T GetById(object id);
void Insert(T entity);
void Update(T entity);
void Delete(T entity);
IQueryable<T> Table { get; }
}
}
5、在Nop.Core下的Data里建立一个实体EfRepository.cs,代码如下:
using System;
using System.Data.Entity;
using System.Linq;
using Nop.Core;
using Nop.Core.Data;
namespace Nop.Data
{
/// <summary>
/// Entity Framework repository
/// </summary>
public partial class EfRepository<T> : IRepository<T> where T : BaseEntity
{
private readonly DbContext _context;
private IDbSet<T> _entities;
/// <summary>
/// Ctor
/// </summary>
/// <param name="context">Object context</param>
public EfRepository(DbContext context)
{
this._context = context;
}
public T GetById(object id)
{
return this.Entities.Find(id);
}
public void Insert(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity");
this.Entities.Add(entity);
this._context.SaveChanges();
}
catch (Exception dbEx)
{
var msg = string.Empty;
//foreach (var validationErrors in dbEx.EntityValidationErrors)
// foreach (var validationError in validationErrors.ValidationErrors)
// msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
var fail = new Exception(msg, dbEx);
//Debug.WriteLine(fail.Message, fail);
throw fail;
}
}
public void Update(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity");
this._context.SaveChanges();
}
catch (Exception dbEx)
{
var msg = string.Empty;
//foreach (var validationErrors in dbEx.EntityValidationErrors)
// foreach (var validationError in validationErrors.ValidationErrors)
// msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
var fail = new Exception(msg, dbEx);
//Debug.WriteLine(fail.Message, fail);
throw fail;
}
}
public void Delete(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity");
this.Entities.Remove(entity);
this._context.SaveChanges();
}
catch (Exception dbEx)
{
var msg = string.Empty;
// foreach (var validationErrors in dbEx.EntityValidationErrors)
// foreach (var validationError in validationErrors.ValidationErrors)
// msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
var fail = new Exception(msg, dbEx);
//Debug.WriteLine(fail.Message, fail);
throw fail;
}
}
public virtual IQueryable<T> Table
{
get
{
return this.Entities;
}
}
private IDbSet<T> Entities
{
get
{
if (_entities == null)
_entities = _context.Set<T>();
return _entities;
}
}
//TODO implement IDisposable interface
}
}
6、在Nop.Services中建立ICategoryService和CategoryService:
ICategoryService:
using System.Collections.Generic;
using Nop.Core;
using Nop.Core.Domain.Catalog;
namespace Nop.Services.Catalog
{
/// <summary>
/// Category service interface
/// </summary>
public partial interface ICategoryService
{
/// <summary>
/// Gets all categories
/// </summary>
/// <param name="showHidden">A value indicating whether to show hidden records</param>
/// <returns>Categories</returns>
IList<Category> GetAllCategories(bool showHidden = false);
}
}
CategoryService:
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using Nop.Core.Data;
using Nop.Core.Domain.Catalog;
using Nop.Data;
namespace Nop.Services.Catalog
{
/// <summary>
/// Category service
/// </summary>
public partial class CategoryService : DbModelBuilder,ICategoryService
{
#region Constants
//private const string CATEGORIES_BY_ID_KEY = "Nop.category.id-{0}";
#endregion
#region Fields
private readonly IRepository<Category> _categoryRepository;
//private readonly DbProviderFactory _dbProviderFactory;
#endregion
#region Ctor
public CategoryService()
{
var ConnectionString = "Data Source=192.168.16.2;Initial Catalog=nopCommerce;Integrated Security=False;Persist Security Info=False;User ID=sa;Password=123456;MultipleActiveResultSets=True";
DbContext dbContext = new NopObjectContext(ConnectionString);
this._categoryRepository = new EfRepository<Category>(dbContext);
}
#endregion
#region Methods
/// <summary>
/// Gets all categories
/// </summary>
/// <param name="showHidden">A value indicating whether to show hidden records</param>
/// <returns>Categories</returns>
public virtual IList<Category> GetAllCategories(bool showHidden = false)
{
var query = from c in _categoryRepository.Table
orderby c.ParentCategoryId, c.DisplayOrder
where (showHidden || c.Published) &&
!c.Deleted
select c;
var unsortedCategories = query.ToList();
return unsortedCategories;
}
#endregion
}
}
![EF4.1 企业架构模式 自动映射数据表 - qiuguangchun - sandea的个人主页 EF4.1 企业架构模式 自动映射数据表 - qiuguangchun - sandea的个人主页](http://img3.ph.126.net/D1aAbcw8uEUamNOlkr4WRQ==/6597338047493177513.jpg)
1、在Nop.Core下的Domain里建立一个实体Category;
2、在Nop.Data下的Mapping\Catatog\下建立一个数据表映射CategoryMap:
using System.Data.Entity.ModelConfiguration;
using Nop.Core.Domain.Catalog;
namespace Nop.Data.Mapping.Catalog
{
public partial class CategoryMap : EntityTypeConfiguration<Category>
{
public CategoryMap()
{
this.ToTable("Category");
this.HasKey(c => c.Id);
this.Property(c => c.Name).IsRequired().HasMaxLength(400);
this.Property(c => c.Description).IsMaxLength();
this.Property(c => c.MetaKeywords).HasMaxLength(400);
this.Property(c => c.MetaDescription);
this.Property(c => c.MetaTitle).HasMaxLength(400);
this.Property(c => c.SeName).HasMaxLength(200);
this.Property(c => c.PriceRanges).HasMaxLength(400);
this.Property(c => c.PageSizeOptions).HasMaxLength(200);
}
}
}
3、在Nop.Data下建EF上下文接口IDbContext和对象NopObjectContext:
IDbContext:
using System.Collections.Generic;
using System.Data.Entity;
using Nop.Core;
namespace Nop.Data
{
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;
int SaveChanges();
IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
where TEntity : BaseEntity, new();
}
}
NopObjectContext:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Reflection;
using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Data.Mapping.Catalog;
//using Nop.Data.Mapping.Localization;
namespace Nop.Data
{
/// <summary>
/// Object context
/// </summary>
public class NopObjectContext : DbContext, IDbContext
{
public NopObjectContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
//((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true;
}
//public DbSet<Category> Category { get; set; }
/// <summary>
/// 自动加载所有的数据表映射类并映射到数据库表 (这个函数是重点,这里就是替代DbSet<T>属性)
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//dynamically load all configuration
System.Type configType = typeof(CategoryMap); //any of your configuration classes here
var typesToRegister = Assembly.GetAssembly(configType).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());
base.OnModelCreating(modelBuilder);
}
/// <summary>
/// 将一个实体附加到Context上和返回已附加这个实体(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.Where(x => x.Id == entity.Id).FirstOrDefault();
if (alreadyAttached == null)
{
//attach new entity
Set<TEntity>().Attach(entity);
return entity;
}
else
{
//entity is already loaded.
return alreadyAttached;
}
}
public string CreateDatabaseScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
}
/// <summary>
/// context附加实体
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <returns></returns>
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
/// <summary>
/// 执行存储过程
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="commandText"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
//HACK: Entity Framework Code First doesn't support doesn't support output parameters
//That's why we have to manually create command and execute it.
//just wait until EF Code First starts support them
//
//More info: http://weblogs.asp.net/dwahlin/archive/2011/09/23/using-entity-framework-code-first-with-stored-procedures-that-have-output-parameters.aspx
bool hasOutputParameters = false;
if (parameters != null)
{
foreach (var p in parameters)
{
var outputP = p as DbParameter;
if (outputP == null)
continue;
if (outputP.Direction == ParameterDirection.InputOutput ||
outputP.Direction == ParameterDirection.Output)
hasOutputParameters = true;
}
}
var context = ((IObjectContextAdapter)(this)).ObjectContext;
if (!hasOutputParameters)
{
//no output parameters
var result = this.Database.SqlQuery<TEntity>(commandText, parameters).ToList();
for (int i = 0; i < result.Count; i++)
result[i] = AttachEntityToContext(result[i]);
return result;
//var result = context.ExecuteStoreQuery<TEntity>(commandText, parameters).ToList();
//foreach (var entity in result)
// Set<TEntity>().Attach(entity);
//return result;
}
else
{
//var connection = context.Connection;
var connection = this.Database.Connection;
//Don't close the connection after command execution
//open the connection for use
if (connection.State == ConnectionState.Closed)
connection.Open();
//create a command object
using (var cmd = connection.CreateCommand())
{
//command to execute
cmd.CommandText = commandText;
cmd.CommandType = CommandType.StoredProcedure;
// move parameters to command object
if (parameters != null)
foreach (var p in parameters)
cmd.Parameters.Add(p);
//database call
var reader = cmd.ExecuteReader();
//return reader.DataReaderToObjectList<TEntity>();
var result = context.Translate<TEntity>(reader).ToList();
for (int i = 0; i < result.Count; i++)
result[i] = AttachEntityToContext(result[i]);
//close up the reader, we're done saving results
reader.Close();
return result;
}
}
}
}
}
4、在Nop.Core下的Data里建立一个接口IRepository,代码如下:
using System.Linq;
namespace Nop.Core.Data
{
/// <summary>
/// Repository
/// </summary>
public partial interface IRepository<T> where T : BaseEntity
{
T GetById(object id);
void Insert(T entity);
void Update(T entity);
void Delete(T entity);
IQueryable<T> Table { get; }
}
}
5、在Nop.Core下的Data里建立一个实体EfRepository.cs,代码如下:
using System;
using System.Data.Entity;
using System.Linq;
using Nop.Core;
using Nop.Core.Data;
namespace Nop.Data
{
/// <summary>
/// Entity Framework repository
/// </summary>
public partial class EfRepository<T> : IRepository<T> where T : BaseEntity
{
private readonly DbContext _context;
private IDbSet<T> _entities;
/// <summary>
/// Ctor
/// </summary>
/// <param name="context">Object context</param>
public EfRepository(DbContext context)
{
this._context = context;
}
public T GetById(object id)
{
return this.Entities.Find(id);
}
public void Insert(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity");
this.Entities.Add(entity);
this._context.SaveChanges();
}
catch (Exception dbEx)
{
var msg = string.Empty;
//foreach (var validationErrors in dbEx.EntityValidationErrors)
// foreach (var validationError in validationErrors.ValidationErrors)
// msg += string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage) + Environment.NewLine;
var fail = new Exception(msg, dbEx);
//Debug.WriteLine(fail.Message, fail);
throw fail;
}
}
public void Update(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity");
this._context.SaveChanges();
}
catch (Exception dbEx)
{
var msg = string.Empty;
//foreach (var validationErrors in dbEx.EntityValidationErrors)
// foreach (var validationError in validationErrors.ValidationErrors)
// msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
var fail = new Exception(msg, dbEx);
//Debug.WriteLine(fail.Message, fail);
throw fail;
}
}
public void Delete(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity");
this.Entities.Remove(entity);
this._context.SaveChanges();
}
catch (Exception dbEx)
{
var msg = string.Empty;
// foreach (var validationErrors in dbEx.EntityValidationErrors)
// foreach (var validationError in validationErrors.ValidationErrors)
// msg += Environment.NewLine + string.Format("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
var fail = new Exception(msg, dbEx);
//Debug.WriteLine(fail.Message, fail);
throw fail;
}
}
public virtual IQueryable<T> Table
{
get
{
return this.Entities;
}
}
private IDbSet<T> Entities
{
get
{
if (_entities == null)
_entities = _context.Set<T>();
return _entities;
}
}
//TODO implement IDisposable interface
}
}
ICategoryService:
using System.Collections.Generic;
using Nop.Core;
using Nop.Core.Domain.Catalog;
namespace Nop.Services.Catalog
{
/// <summary>
/// Category service interface
/// </summary>
public partial interface ICategoryService
{
/// <summary>
/// Gets all categories
/// </summary>
/// <param name="showHidden">A value indicating whether to show hidden records</param>
/// <returns>Categories</returns>
IList<Category> GetAllCategories(bool showHidden = false);
}
}
CategoryService:
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using Nop.Core.Data;
using Nop.Core.Domain.Catalog;
using Nop.Data;
namespace Nop.Services.Catalog
{
/// <summary>
/// Category service
/// </summary>
public partial class CategoryService : DbModelBuilder,ICategoryService
{
#region Constants
//private const string CATEGORIES_BY_ID_KEY = "Nop.category.id-{0}";
#endregion
#region Fields
private readonly IRepository<Category> _categoryRepository;
//private readonly DbProviderFactory _dbProviderFactory;
#endregion
#region Ctor
public CategoryService()
{
var ConnectionString = "Data Source=192.168.16.2;Initial Catalog=nopCommerce;Integrated Security=False;Persist Security Info=False;User ID=sa;Password=123456;MultipleActiveResultSets=True";
DbContext dbContext = new NopObjectContext(ConnectionString);
this._categoryRepository = new EfRepository<Category>(dbContext);
}
#endregion
#region Methods
/// <summary>
/// Gets all categories
/// </summary>
/// <param name="showHidden">A value indicating whether to show hidden records</param>
/// <returns>Categories</returns>
public virtual IList<Category> GetAllCategories(bool showHidden = false)
{
var query = from c in _categoryRepository.Table
orderby c.ParentCategoryId, c.DisplayOrder
where (showHidden || c.Published) &&
!c.Deleted
select c;
var unsortedCategories = query.ToList();
return unsortedCategories;
}
#endregion
}
}