Portal.MVC —— nopcommerce的简化版

摘要:
门户。MVC概要项目基于MVC4+EF,具有角色、权限、用户中心和帐户等基本功能。我提到的开源项目nopcommerce是一个电子商务架构的MVC项目。我把它简化了。以前,主要是为了方便我建立一些小网站。如下:内核。将<IPermissionservice>()绑定到<Permissionservice<();有关Ninject的更详细介绍,请参阅我的博客:在MVC5.publicHttpContextBaseHttpContext中使用Ninject{get{returnnewHttpContextWrapper;}}HttpContextBase是一个抽象类,HttpContextWrapper是其派生成员。更多的解释可以在老赵的博客中找到:为什么是HttpContextBase而不是IHttpContext?关于这一部分有很多在线讨论。否使用一个仓库接口IRepository<T>,然后实现不同的服务。IRepository<T>:公共接口IRepositary<T>,其中T:BaseEntity{///<summary>///Getthebyid。///</summary>//<paramname=“id”>Theid。///<return>`0。˂/return>TGetById;///<summary>///插入特定性。//////实体。voidInsert;/////插入特定性=“实体”˃实体voidInsert;//////更新特定性。/////实体voidUpdate;//////删除特定性。//////实体voidDelete;//////获取表格。//////<value>表格///获取表格跟踪。/////<value>表格跟踪。IQueryableTableNoTracking{get;}}ViewCode使用EfRepository实现此接口publicclassEfRepository:IRepository,其中T:BaseEntity{#regionFieldsprivatedonlyIDbContext_context;privateIDbSet_entities;#endregionpublicEfRepository{if{thrownewArgumentNullException;}_context=上下文;}#regionpropertyprotectedvirtualIDbSet<T>实体{get{if{_Entities=_context.Set<T>();}返回实体??
Portal.MVC 简介
项目是基于MVC4+EF,带有角色,权限,用户中心及账户相关(登录,注册,修改密码,找回密码等)等基本功能。参考的开源项目 nopcommerce,这是一个电商架构的MVC项目,我对其进行了简化,之前主要是方便我自己搭建一些小的网站。包含前台和后台。
界面浏览
1.首页。这是前天晚上临时做出来的Logo和页面。不是真实案例,大家可以结合实际项目再修改。
Portal.MVC —— nopcommerce的简化版第1张
2.登录框
Portal.MVC —— nopcommerce的简化版第2张
2.注册框
Portal.MVC —— nopcommerce的简化版第3张
3.邮箱密码找回,这个地方要用还需要配置邮箱服务器。
Portal.MVC —— nopcommerce的简化版第4张
4.用户中心
Portal.MVC —— nopcommerce的简化版第5张
 4.后台管理,使用的Matrix Admin 模板。这是一个很好用的模板,是衍生于Bootstrap。
 Portal.MVC —— nopcommerce的简化版第6张
 用户管理,这里excel导出用的是NPOI。
 Portal.MVC —— nopcommerce的简化版第7张
 不知道有没有提起园友的兴趣,下面我讲一下代码部分。
功能介绍
我没有做分太多的层级,这样比较直观,核心的部分在Niqiu.Core类库里面.分四个部分
 Portal.MVC —— nopcommerce的简化版第8张
  • Domain:包含主要的领域模型,比如User,UserRole,PermissionRecord等
  • Helpers:包含一些帮助类,比如xml,email
  • Mapping:数据映射
  • Services:服务部分的接口和实现

而网站部分重要的有一些可以复用的Attributes,AccountController等,比如UserLastActivityIpAttribute,会记录用户的Ip并更新最后访问时间。

下面介绍下2个我认为重要点的部分

Ninject依赖注入:

Nop中使用的是Autofac,并构建了一个强大的EnginContext管理所有的依赖注入项,在这个项目中我拿掉了这一部分,换成Ninject来完成IOC的工作。并不涉及这两个组件的优劣问题,而且我认为前者的功能更要强悍一些。Ninject是在NinjectWebCommon类中注册的.它在App_Start文件夹下。 如下:

kernel.Bind<IPermissionservice>().To<Permissionservice>();

Ninject更详细的介绍请看我的博客:Ninject在MVC5中的使用。在不能用构造函数的地方,可以用属性注入。

  [Inject]
   public IWorkContext WorkContext { get; set; }

  [Inject]
  public IUserService UserService { get; set; }

而有的Service需要用到HttpContext,这对象没有接口,是无法注册的,但可以通过HttpContextWrapper获得。

 public HttpContextBase HttpContext
        {
            get { return new HttpContextWrapper(System.Web.HttpContext.Current); }
        }
HttpContextBase 是一个抽象类,HttpContextWrapper是其派生成员。这两者都是.Net3.5之后才出现。

更多的解释大家可以看老赵的博客:为什么是HttpContextBase而不是IHttpContext

领域设计

 这个部分网上讨论的比较多,Nop是采用的单个仓库接口IRepository<T>,然后实现不同的服务。定义IDbContext,注入数据库对象。

 IRepository<T>:

Portal.MVC —— nopcommerce的简化版第9张Portal.MVC —— nopcommerce的简化版第10张
public interface IRepository<T> where T:BaseEntity
   {
       /// <summary>
       /// Gets the by id.
       /// </summary>
       /// <param name="id">The id.</param>
       /// <returns>`0.</returns>
       T GetById(object id);
       /// <summary>
       /// Inserts the specified entity.
       /// </summary>
       /// <param name="entity">The entity.</param>
       void Insert(T entity);
       /// <summary>
       /// Inserts the specified entities.
       /// </summary>
       /// <param name="entities">The entities.</param>
       void Insert(IEnumerable<T> entities);
       /// <summary>
       /// Updates the specified entity.
       /// </summary>
       /// <param name="entity">The entity.</param>
       void Update(T entity);
       /// <summary>
       /// Deletes the specified entity.
       /// </summary>
       /// <param name="entity">The entity.</param>
       void Delete(T entity);
       /// <summary>
       /// Gets the table.
       /// </summary>
       /// <value>The table.</value>
       IQueryable<T> Table { get; }
       /// <summary>
       /// Gets the tables no tracking.
       /// </summary>
       /// <value>The tables no tracking.</value>
       IQueryable<T> TableNoTracking { get; }
   }
View Code

用EfRepository<T>实现这个接口

Portal.MVC —— nopcommerce的简化版第9张Portal.MVC —— nopcommerce的简化版第12张
public class EfRepository<T>:IRepository<T> where T:BaseEntity
   {
       #region Fields

       private readonly IDbContext _context ;

       private IDbSet<T> _entities;

       #endregion
       public EfRepository(IDbContext context)
       {
           if (context == null)
           {
               throw new ArgumentNullException("context");
           }
           _context = context;
       }
       #region property

       protected virtual IDbSet<T> Entities
       {
           get
           {
               if (_entities == null)
               {
                   _entities = _context.Set<T>();
               }
               return _entities ?? (_entities = _context.Set<T>()); 
                  // _entities ?? _entities = db.Set<T>();
           }
       }

       /// <summary>
       /// Gets a table
       /// </summary>
       public virtual IQueryable<T> Table
       {
           get
           {
               return Entities;
           }
       }

       public IQueryable<T> TableNoTracking
       {
           get
           {
               return Entities.AsNoTracking();
               
           }
       }


       #endregion

     

       public virtual T GetById(object id)
       {
           return Entities.Find(id);
       }

       public void Insert(T entity)
       {
           try
           {
               if(entity==null) throw new ArgumentException("entity");
               Entities.Add(entity);
               _context.SaveChanges();
           }
           catch (DbEntityValidationException dbEx)
           {
               GetException(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)
                   Entities.Add(entity);

               _context.SaveChanges();
           }
           catch (DbEntityValidationException dbEx)
           {
               GetException(dbEx);
           }
       }

       /// <summary>
       /// Update entity
       /// </summary>
       /// <param name="entity">Entity</param>
       public virtual void Update(T entity)
       {
           try
           {
               if (entity == null)
                   throw new ArgumentNullException("entity");
               _context.SaveChanges();
           }
           catch (DbEntityValidationException dbEx)
           {
               GetException(dbEx);
           }
       }

       /// <summary>
       /// Delete entity
       /// </summary>
       /// <param name="entity">Entity</param>
       public virtual void Delete(T entity)
       {
           try
           {
               if (entity == null)
                   throw new ArgumentNullException("entity");

               Entities.Remove(entity);

               _context.SaveChanges();
           }
           catch (DbEntityValidationException dbEx)
           {
               GetException(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)
                   Entities.Remove(entity);
               _context.SaveChanges();
           }
           catch (DbEntityValidationException dbEx)
           {
               GetException(dbEx);
           }
       }

       private static void GetException(DbEntityValidationException 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;
       }
   }
View Code

而其中的IDbContext是自定义的数据接口

Portal.MVC —— nopcommerce的简化版第9张Portal.MVC —— nopcommerce的简化版第14张
 public interface IDbContext
    {
        IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;

        int SaveChanges();

        /// <summary>
        /// 执行存储过程,并返回对象列表
        /// </summary>
        /// <typeparam name="TEntity">The type of the T entity.</typeparam>
        /// <param name="commandText">The command text.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns>IList{``0}.</returns>
        IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
            where TEntity : BaseEntity, new();
        /// <summary>
        /// 查询Sql语句
        /// </summary>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters);

        /// <summary>
        /// 执行sql 是否启用事务
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="doNotEnsureTransaction"></param>
        /// <param name="timeout"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null,
            params object[] parameters);
    }
View Code

然后注入:

  kernel.Bind<IDbContext>().To<PortalDb>().InSingletonScope();

对于和模型相关的Service内部都是注入的IRepository<T>,比如UserService。

      private readonly IRepository<User> _useRepository;
       private readonly IRepository<UserRole> _userRoleRepository;
       private readonly  ICacheManager _cacheManager ;

       public UserService(IRepository<User> useRepository,IRepository<UserRole> userRoleRepository,ICacheManager cacheManager)
       {
           _useRepository = useRepository;
           _userRoleRepository = userRoleRepository;
           _cacheManager = cacheManager;
       }

这样相互之间就比较干净。只依赖接口。

而数据模型都会继承BaseEntity这个对象。

 public class User : BaseEntity
    {
  //...
   }

数据映射相关的部分在Mapping中,比如UserMap

 public class UserMap : PortalEntityTypeConfiguration<Domain.User.User>
    {
        public UserMap()
        {
            ToTable("Users");
            HasKey(n => n.Id);
            Property(n => n.Username).HasMaxLength(100);
            Property(n => n.Email).HasMaxLength(500);
            Ignore(n => n.PasswordFormat);
            HasMany(c => c.UserRoles).WithMany().Map(m => m.ToTable("User_UserRole_Mapping"));
        }
    }

在PortalDb的OnModelCreating方法中加入。这样就会影响到数据库的设计。

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
     
            modelBuilder.Configurations.Add(new UserMap());
}

Nop的做法高级一些。反射找出所有的PortalEntityTypeConfiguration。一次性加入。这里大家可以自己去试

  var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
            .Where(type => !String.IsNullOrEmpty(type.Namespace))
            .Where(type => type.BaseType != null && type.BaseType.IsGenericType &&
                type.BaseType.GetGenericTypeDefinition() == typeof(PortalEntityTypeConfiguration<>));
            foreach (var type in typesToRegister)
            {
                dynamic configurationInstance = Activator.CreateInstance(type);
                modelBuilder.Configurations.Add(configurationInstance);
            }

权限管理:

默认设定了三种角色,Administrators,Admins,Employeer,他们分别配置了不同的权限,权限指定是在StandardPermissionProvider这个类中完成的,表示一个角色拥有哪些权限,Administrators拥有所有权限

new DefaultPermissionRecord
      {
        UserRoleSystemName   = SystemUserRoleNames.Admin,
        PermissionRecords = new []
         {
            AccessAdminPanel,
            SearchOrder,
            ManageUsers,
          }
       },

而权限验证的时候,在响应的Action上面加上AdminAuthorize和权限名称即可。

 [AdminAuthorize("ManageUsers")]
   public ActionResult Index()
    {
    }

而在其内部是通过PermissionService来验证的:

 public virtual bool HasAdminAccess(AuthorizationContext filterContext)
        {
            bool result = PermissionService.Authorize(Permission);
            return result;
        }

后台只有给用户设置角色的页面,我没做新增角色并分配权限的页面。不过这个实现起来也很简单了。如果你是更换数据库,这个时候设计到权限的初始化。调用下面的方法即可:

_permissionService.InstallPermissions(new StandardPermissionProvider());

当然前提是你先注入这个_permissionService。更多的代码细节大家可以去看源码。安装完成之后会有以下默认权限

Portal.MVC —— nopcommerce的简化版第15张

 以上是关于工程的大体说明,欢迎大家拍砖。下面下载地址。数据文件需要附加。

 1:github地址:https://github.com/stoneniqiu/Portal.MVC 

 2:百度云:https://pan.baidu.com/s/1juc5Mo20sTW0I5uJyaNBbw

 数据库是Sql2008,附加不上,也可以自动生成。记得给自己添加用户。 默认用户名:stoneniqiu 密码 admin

关于分享
首先还是希望这个工程对大家有帮助,分享的过程是对过去知识一个梳理,存档入库,这种感觉就像是盛了一杯水倒掉了,进而我需要找新的水装满这个水杯;最后的最后还是宣传一下我们的读书群。我们加了很多技术群,但终归寂寥。但这个群是会持续分享读书心得和电子书的。人生路长,你需要一个持续的精神粮食来源。下面是我们第四期书山有路的投票结果,《数学之美》现在正在进行。群号:452450927
 
Portal.MVC —— nopcommerce的简化版第16张

免责声明:文章转载自《Portal.MVC —— nopcommerce的简化版》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Redis 存储图片 [base64/url/path]vs[object]iOS 代码规范下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

破解SQLYog30天试用方法

删除此项,SQLyog重新启动后将重新生成注册表。使用寿命为30天。...

.gitignore模板

Github/gitgnore·Github列出了一些有用的。gitnore模板。例如,这是针对visual studio的。将“FallbackLocation”留空,这样VS将生成sdf到临时文件夹。...

kvm虚拟机快照备份

kvm虚拟机默认使用原始图像格式,该格式具有最佳性能和最快速度。它的缺点是不支持一些新功能,例如映像、zlib磁盘压缩、AES加密等。接下来,启动kvm虚拟机快照备份的过程。查看虚拟机映像快照的版本查看当前虚拟机映像snapshot的版本。您可以看到它是最新的快照版本。...

数据不平衡的相关

大多数常见的机器学习算法不能很好地处理不平衡的数据集。例如,搜索引擎的点击预测(点击页面往往占很小的比例)、电子商务中的产品推荐(正在购买的推荐产品的比例很低)、信用卡欺诈检测、网络攻击识别、癌症检测等。处理数据不平衡的方法主要有以下几种。2.数据级别2.1重新采样2.1.1欠采样(下采样)欠采样通过减少丰富类的大小来平衡数据集。它试图通过增加稀有样本的数量...

C#探秘系列(十)WPF:打开文件选择器选择文件并保存

//此为点击按钮的监听事件,点击按钮弹出文件选择器privatevoidimageButton_Click(objectsender,RoutedEventArgse){vardialog=newOpenFileDialog();dialog.Filter=".jpg|*.jpg|.png|*.png|.jpeg|*.jpeg";if(dialog.Show...

JQuery 导入导出 Excel

正在做一个小项目,从数据库中查询数据放在HTMLTable中.现在想要从这个table中导出数据来.另外用户需要选择导出的列.使用jQuery的导出插件可以完成这个需求.jQueryPlugintoExportHTMLTables例子:导入插件:[javascript]viewplaincopy˂/scr...