而網站部分重要的有一些能夠複用的Attributes,AccountController等,好比UserLastActivityIpAttribute,會記錄用戶的Ip並更新最後訪問時間。html
下面介紹下2個我認爲重要點的部分git
Nop中使用的是Autofac,並構建了一個強大的EnginContext管理全部的依賴注入項,在這個項目中我拿掉了這一部分,換成Ninject來完成IOC的工做。並不涉及這兩個組件的優劣問題,並且我認爲前者的功能更要強悍一些。Ninject是在NinjectWebCommon類中註冊的.它在App_Start文件夾下。 以下:github
kernel.Bind<IPermissionservice>().To<Permissionservice>();
Ninject更詳細的介紹請看個人博客:Ninject在MVC5中的使用。在不能用構造函數的地方,能夠用屬性注入。sql
[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>:app
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; } }
用EfRepository<T>實現這個接口ide
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; } }
而其中的IDbContext是自定義的數據接口函數
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); }
而後注入:
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。更多的代碼細節你們能夠去看源碼。安裝完成以後會有如下默認權限
以上是關於工程的大致說明,歡迎你們拍磚。下面下載地址。數據文件須要附加。
1:github地址:https://github.com/stoneniqiu/Portal.MVC
2:百度雲:https://pan.baidu.com/s/1juc5Mo20sTW0I5uJyaNBbw
數據庫是Sql2008,附加不上,也能夠自動生成。記得給本身添加用戶。 默認用戶名:stoneniqiu 密碼 admin