nopcommerce中文网

nopcommerce是国外asp.net领域一个高质量的b2c开源项目,基于EntityFramework和MVC开发,QQ群1:75272942(2000人超级群,已满) QQ群2:640322459

导航 - 搜索

教你一招 - nopcommerce如何启用redis缓存?

从nopcommerce3.7开始就支持redis缓存了,但是默认没有启用,启用步骤如下:
1、首先安装redis服务器(如果没有的话),参考网址 http://www.redis.net.cn/tutorial/3503.html
2、然后修改web.config如下:
<RedisCaching Enabled="true" ConnectionString="localhost:6379,allowAdmin=true" />
注意要启用allowAdmin=true,否则后台清理缓存的时候会报错。
3、刷新页面就可以了,在Redis Desktop Manager里面看下缓存数据吧。


分享是一种美。版权所有,转载请注明出处 http://www.nopchina.net/

教你一招 - 如何在nopCommerce里面使用 Entity Framework (EF) Code-First Migrations(英)

I've seen a lot of nopCommerce forums users asking how they can use Entity Framework (EF) Code-First Migrations to customize nopCommerce, add new fields and entites to the core. I actually use a lot of EF Migrations myself when doing nopCommerce customization projects, and I must say it helps a lot in the development.

Today, I'll share with you how you can do that in nopCommerce project! I'll be using nopCommerce 3.20 as an example, but you can easily apply the concept to other vesions!

Setting Up EF Migrations in nopCommerce Solution

The first thing you want to do is to enable migrations in your nopCommerce solution. So fire up nopCommerce in Visual Studio, look at Nop.Web project, and open Web.config. You need to add a connection string to your development database. Note that adding the connection string in Web.config doesn't affect how nopCommerce works, since nopCommerce doesn't look for connection string in Web.config.

EF Migrations in nopCommerce - Setup Web.config

Then, open NopObjectContext.cs in Nop.Data. Add a new constructor that points to the name of the connection string that you've just added in previous step. NOTE: replace Pro-nopCommerce with the name of your connection string.

EF Migrations in nopCommerce - Setup NopObjectContext.cs

The next step is to actually enable migrations in the project. If you have not already done so, bring up Package Manager Console. In the "Default Project" drop-down list, select Nop.Data as the project. Please also make sure Nop.Web is selected as the StartUp Project. Now, enter the command "enable-migrations" in Package Manager Console, and hit Enter! Visual Studio will generate a file named "Configurations.cs"; you can safely ignore it, but you need to keep it.

EF Migrations in nopCommerce - Enable migrations

The last step in EF Migrations setup is done by entering the command "add-migration InitialMigration -IgnoreChanges" in Package Manager Console. "InitialMigration" (highligted yellow) is the name you want to give the the current migration, and the "IgnoreChanges" handle is to tell EF Migrations that you want to leave out the current database as-is: that means you want EF to ignore all the existing tables so that no script is generated for the existing tables.

EF Migrations in nopCommerce - Initial Migration

As a result, you'll see a new .cs file generated by Visual Studio that correspond to the migration - InitialMigration - you've just added. If you look at the file, it essentially is a blank file, due to the fact that we used the -IgnoreChanges handle in previous step.

To actually save this migration over to the database, run the command "update-database" in Package Manager Console. Your database is now ready for actual EF Migration tasks!

EF Migrations in nopCommerce - Update Database

Adding a Migration and Updating the Database

Now, suppose we want to link up Blog and Products, where a Product can have multiple BlogPost that talk about the Product itself. We'll need a one-to-many relationship between Product and BlogPost.

In essence, what we need is a new ProductId field in BlogPost, and a ICollection<BlogPost> property in Product. The following screenshot sums up the update we should add to both Product.cs and BlogPost.cs (in Nop.Core project).

EF Migrations in nopCommerce - BlogPost.cs

EF Migrations in nopCommerce - Product.cs

Now you have the properties ready on your domain model, you also need to setup the configuration and tell Entity Framework how you want to format the parameter, for example the relationship, and whether a field is optional/required. Open BlogPostMap.cs in Nop.Data, and enter the following lines, where we tell EF to that Product (or ProductId) is an optional property (meaning it can have NULL as value), and a Product can have many BlogPost, and to use ProductId as the foreign key between BlogPost andProduct.

EF Migrations in nopCommerce - BlogPostMap

We now have enough information to instruct Entity Framework to generate the migration, so again, bring up Package Manager Console, and enter the command "add-migration AddProductToBlogPost".

EF Migrations in nopCommerce - Add Product To Blog Post

This command causes Visual Studio to generate a .cs file that looks like the following:

EF Migrations in nopCommerce - Generated Migration CS file

To actually generate the SQL script and update the database, run again the command "update-database" in Package Manager Console. Now, to verify that the database is updated correctly, open up BlogPost table in the database, and check that the new field, foreign key and etc are indeed added.

EF Migrations in nopCommerce - Verifying Database

Conclusion

Entity Framework Code-first Migration can be a very handy tool if you customize nopCommerce a lot. Like this you don't need to touch the database when you want to add new fields, tables and etc to the database. Everything can be done from the code!

Learn up the technique, and have fun coding!

翻译自:http://www.pronopcommerce.com/using-entity-framework-ef-code-first-migrations-in-nopcommerce-for-fast-customizations

教你一招 - Misc类型插件的妙用(附带插件源码)

熟悉nopcommerce插件的朋友应该知道里面有一种Misc类型的插件,比如Nop.Plugin.Misc.WebServices和Nop.Plugin.Misc.FacebookShop,继承自接口IMiscPlugin,主要用来完成其他插件不能完成的工作,今天无意中发现这种插件还有个妙用,就是可以替换现有的页面,看来nopcommerce真的很博大。

例如我们进入后台促销管理 - 折扣管理,你会看到这个页面:



这个功能做的不太友好,没有搜索功能,如果数据比较多的话找起来会很吃力,怎么办?一般情况下我们会想到直接修改代码来增加搜索功能,但是这样会破坏nopcommerce的源程序,做nopcommerce二次开发的前提是尽可能的不修改它的架构,而是用插件和模板来实现自己的需求,这样方便以后升级。

然后我们会想到做个页面来替换这个页面从而达到我们的目的,这时候就会用到Misc类型的插件了,实际上我们就是在这个插件里面做了一个带搜索的折扣管理的页面,然后重要的是修改RouteProvider.cs里面的路由,如下:


这段代码的意思很明显就是移除默认的路由然后添加新的路由从而指向我们添加的这个页面,插件完成后后台安装再进入折扣管理页面你会看到如图所示的页面:


好了,其实还有很多的用途,盆友们自己研究吧!

源码下载:Nop.Plugin.Misc.DiscountAdminHelper.zip (170.9KB)

分享是一种美。版权所有,转载请注明出处 http://www.nopchina.net/

教你一招 - 如何给nopcommerce做一套自己的主题

nopcommerce拥有一套不错的模板机制,可以让你快速的做一套属于自己的主题。\Presentation\Nop.Web下面有个Themes文件夹,这里面就是放主题的地方,每个主题对应一个文件夹,每个主题文件夹下面都有一个theme.config文件,这个文件定义了该主题的一些基本信息。Content文件夹放的样式、图片、脚本等资源文件,Views文件夹放的是试图,nopcommerce会默认先读取这个里面的view,如果找不到就去根目录下面的Views里面找,我的作法是直接把根目录下面的Views文件夹复制过来,需要哪个就修改哪个,然后就是修改样式了,nopcommerce有一列、两列、三列显示,你可以参考一些国内的商城系统(注:你可以使用google浏览器或者火狐浏览器能很方便的看到每一块的具体样式,能很快的抄过来。。。),这里就不多说了。

这里分享了几个简单的主题:查看

教你一招 - 如何给nopcommerce增加新闻类别模块

nopcommerce的新闻模块一直都没有新闻类别,但是很多情况下都会使用到类别,怎么办呢,自己动手吧。

1、首先创建新闻类别表,脚本如下:

CREATE TABLE [dbo].[NewsCategory](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Name] [nvarchar](400) NOT NULL,
	[Description] [nvarchar](max) NOT NULL,
	[ParentCategoryId] [int] NOT NULL,
	[Published] [bit] NOT NULL,
	[Deleted] [bit] NOT NULL,
	[DisplayOrder] [int] NOT NULL,
	[CreatedOn] [datetime] NOT NULL,
	[UpdatedOn] [datetime] NOT NULL,
 CONSTRAINT [PK_NewsCategory] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO



2、\Libraries\Nop.Core\Domain\News下面增加实体类,代码如下:

using System;
using System.Collections.Generic;
using Nop.Core.Domain.Localization;

namespace Nop.Core.Domain.News
{
    /// <summary>
    /// Represents a news category
    /// </summary>
    public partial class NewsCategory : BaseEntity
    {
        /// <summary>
        /// Gets or sets the name
        /// </summary>
        public virtual string Name { get; set; }

        /// <summary>
        /// Gets or sets the description
        /// </summary>
        public virtual string Description { get; set; }

        /// <summary>
        /// Gets or sets the parent category identifier
        /// </summary>
        public virtual int ParentCategoryId { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the entity is published
        /// </summary>
        public virtual bool Published { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the entity has been deleted
        /// </summary>
        public virtual bool Deleted { get; set; }

        /// <summary>
        /// Gets or sets the display order
        /// </summary>
        public virtual int DisplayOrder { get; set; }

        /// <summary>
        /// Gets or sets the date and time of instance creation
        /// </summary>
        public virtual DateTime CreatedOn { get; set; }

        /// <summary>
        /// Gets or sets the date and time of instance update
        /// </summary>
        public virtual DateTime UpdatedOn { get; set; }
    }
}



3、\Libraries\Nop.Data\Mapping\News下面增加新闻类别映射类,因为nopcommerce使用Entity Framework (EF) Code-First方法,
允许你在nopcommerce代码中定义实体 (所有的核心实体类都在Nop.Core中定义),所以必须在此添加映射类,代码如下:

using System.Data.Entity.ModelConfiguration;
using Nop.Core.Domain.News;

namespace Nop.Data.Mapping.News
{
    public partial class NewsCategoryMap : EntityTypeConfiguration<NewsCategory>
    {
        public NewsCategoryMap()
        {
            this.ToTable("NewsCategory");
            this.HasKey(bp => bp.Id);
            this.Property(bp => bp.Name).IsRequired().IsMaxLength();
            this.Property(bp => bp.Description).IsRequired().IsMaxLength();
        }
    }
}



4、\Libraries\Nop.Services\News下面的INewsService.cs和NewsService.cs增加新闻类别相关操作。
INewsService.cs整理后代码如下:

using System;
using System.Collections.Generic;
using Nop.Core;
using Nop.Core.Domain.News;

namespace Nop.Services.News
{
    /// <summary>
    /// News service interface
    /// </summary>
    public partial interface INewsService
    {
        #region news

        /// <summary>
        /// Deletes a news
        /// </summary>
        /// <param name="newsItem">News item</param>
        void DeleteNews(NewsItem newsItem);

        /// <summary>
        /// Gets a news
        /// </summary>
        /// <param name="newsId">The news identifier</param>
        /// <returns>News</returns>
        NewsItem GetNewsById(int newsId);

        /// <summary>
        /// Gets all news
        /// </summary>
        /// <param name="languageId">Language identifier; 0 if you want to get all records</param>
        /// <param name="dateFrom">Filter by created date; null if you want to get all records</param>
        /// <param name="dateTo">Filter by created date; null if you want to get all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>News items</returns>
        IPagedList<NewsItem> GetAllNews(int languageId,
            DateTime? dateFrom, DateTime? dateTo, int pageIndex, int pageSize, bool showHidden = false);

        /// <summary>
        /// Inserts a news item
        /// </summary>
        /// <param name="news">News item</param>
        void InsertNews(NewsItem news);

        /// <summary>
        /// Updates the news item
        /// </summary>
        /// <param name="news">News item</param>
        void UpdateNews(NewsItem news);
        #endregion

        #region news category

        /// <summary>
        /// Deletes a news category
        /// </summary>
        /// <param name="category">News Category item</param>
        void DeleteNewsCategory(NewsCategory category);

        /// <summary>
        /// Gets a news category
        /// </summary>
        /// <param name="categoryId">The news category identifier</param>
        /// <returns>NewsCategory</returns>
        NewsCategory GetNewsCategoryById(int categoryId);

        /// <summary>
        /// Gets all news category
        /// </summary>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>News Category items</returns>
        IPagedList<NewsCategory> GetAllNewsCategory(int pageIndex, int pageSize, bool showHidden = false);

        /// <summary>
        /// Gets all news category
        /// </summary>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>News Category items</returns>
        IList<NewsCategory> GetAllNewsCategory(bool showHidden = false);

        /// <summary>
        /// Inserts a news category item
        /// </summary>
        /// <param name="category">News Category item</param>
        void InsertNewsCategory(NewsCategory category);

        /// <summary>
        /// Updates the news category item
        /// </summary>
        /// <param name="category">News Category item</param>
        void UpdateNewsCategory(NewsCategory category);

        #endregion
    }
}


NewsService.cs整理后代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Data;
using Nop.Core.Domain.News;
using Nop.Core.Events;

namespace Nop.Services.News
{
    /// <summary>
    /// News service
    /// </summary>
    public partial class NewsService : INewsService
    {
        #region Constants
        private const string NEWS_BY_ID_KEY = "Nop.news.id-{0}";
        private const string NEWS_PATTERN_KEY = "Nop.news.";
        private const string NEWSCATEGORY_BY_ID_KEY = "Nop.newscategory.id-{0}";
        private const string NEWSCATEGORY_PATTERN_KEY = "Nop.newscategory.";
        #endregion

        #region Fields

        private readonly IRepository<NewsItem> _newsItemRepository;
        private readonly IRepository<NewsCategory> _newsCategoryRepository;
        private readonly ICacheManager _cacheManager;
        private readonly IEventPublisher _eventPublisher;

        #endregion

        #region Ctor

        public NewsService(IRepository<NewsItem> newsItemRepository, IRepository<NewsCategory> newsCategoryRepository, ICacheManager cacheManager, IEventPublisher eventPublisher)
        {
            _newsItemRepository = newsItemRepository;
            _newsCategoryRepository = newsCategoryRepository;
            _cacheManager = cacheManager;
            _eventPublisher = eventPublisher;
        }

        #endregion

        #region Methods

        #region news
        
        /// <summary>
        /// Deletes a news
        /// </summary>
        /// <param name="newsItem">News item</param>
        public virtual void DeleteNews(NewsItem newsItem)
        {
            if (newsItem == null)
                throw new ArgumentNullException("newsItem");

            _newsItemRepository.Delete(newsItem);

            _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityDeleted(newsItem);
        }

        /// <summary>
        /// Gets a news
        /// </summary>
        /// <param name="newsId">The news identifier</param>
        /// <returns>News</returns>
        public virtual NewsItem GetNewsById(int newsId)
        {
            if (newsId == 0)
                return null;

            string key = string.Format(NEWS_BY_ID_KEY, newsId);
            return _cacheManager.Get(key, () =>
            {
                var n = _newsItemRepository.GetById(newsId);
                return n;
            });
        }

        /// <summary>
        /// Gets all news
        /// </summary>
        /// <param name="languageId">Language identifier; 0 if you want to get all records</param>
        /// <param name="dateFrom">Filter by created date; null if you want to get all records</param>
        /// <param name="dateTo">Filter by created date; null if you want to get all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>News items</returns>
        public virtual IPagedList<NewsItem> GetAllNews(int languageId,
            DateTime? dateFrom, DateTime? dateTo, int pageIndex, int pageSize, bool showHidden = false)
        {
            var query = _newsItemRepository.Table;
            if (dateFrom.HasValue)
                query = query.Where(n => dateFrom.Value <= n.CreatedOnUtc);
            if (dateTo.HasValue)
                query = query.Where(n => dateTo.Value >= n.CreatedOnUtc);
            if (languageId > 0)
                query = query.Where(n => languageId == n.LanguageId);
            if (!showHidden)
                query = query.Where(n => n.Published);
            query = query.OrderByDescending(b => b.CreatedOnUtc);

            var news = new PagedList<NewsItem>(query, pageIndex, pageSize);
            return news;
        }
        /// <summary>
        /// Inserts a news item
        /// </summary>
        /// <param name="news">News item</param>
        public virtual void InsertNews(NewsItem news)
        {
            if (news == null)
                throw new ArgumentNullException("news");

            _newsItemRepository.Insert(news);

            _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityInserted(news);
        }

        /// <summary>
        /// Updates the news item
        /// </summary>
        /// <param name="news">News item</param>
        public virtual void UpdateNews(NewsItem news)
        {
            if (news == null)
                throw new ArgumentNullException("news");

            _newsItemRepository.Update(news);

            _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityUpdated(news);
        }

        #endregion

        #region news category

        /// <summary>
        /// Deletes a news category
        /// </summary>
        /// <param name="category">News Category item</param>
        public virtual void DeleteNewsCategory(NewsCategory category)
        {
            if (category == null)
                throw new ArgumentNullException("category");

            _newsCategoryRepository.Delete(category);

            _cacheManager.RemoveByPattern(NEWSCATEGORY_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityDeleted(category);
        }

        /// <summary>
        /// Gets a news category
        /// </summary>
        /// <param name="categoryId">The news category identifier</param>
        /// <returns>NewsCategory</returns>
        public virtual NewsCategory GetNewsCategoryById(int categoryId)
        {
            if (categoryId == 0)
                return null;

            string key = string.Format(NEWSCATEGORY_BY_ID_KEY, categoryId);
            return _cacheManager.Get(key, () =>
            {
                var n = _newsCategoryRepository.GetById(categoryId);
                return n;
            });
        }

        /// <summary>
        /// Gets all news category
        /// </summary>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>News Category items</returns>
        public virtual IPagedList<NewsCategory> GetAllNewsCategory(int pageIndex, int pageSize, bool showHidden = false)
        {
            var categories = GetAllNewsCategory(showHidden);
            return new PagedList<NewsCategory>(categories, pageIndex, pageSize);
        }

        /// <summary>
        /// Gets all news category
        /// </summary>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>News Category items</returns>
        public virtual IList<NewsCategory> GetAllNewsCategory(bool showHidden = false)
        {
            var query = _newsCategoryRepository.Table;
            if (!showHidden)
                query = query.Where(n => n.Published);
            query = query.OrderBy(b => b.DisplayOrder);

            var unsortedCategories = query.ToList();

            //sort categories
            var sortedCategories = unsortedCategories.SortCategoriesForTree(0);
            return sortedCategories;
        }

        /// <summary>
        /// Inserts a news category item
        /// </summary>
        /// <param name="category">News Category item</param>
        public virtual void InsertNewsCategory(NewsCategory category)
        {
            if (category == null)
                throw new ArgumentNullException("category");

            _newsCategoryRepository.Insert(category);

            _cacheManager.RemoveByPattern(NEWSCATEGORY_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityInserted(category);
        }

        /// <summary>
        /// Updates the news category item
        /// </summary>
        /// <param name="category">News Category item</param>
        public virtual void UpdateNewsCategory(NewsCategory category)
        {
            if (category == null)
                throw new ArgumentNullException("category");

            _newsCategoryRepository.Update(category);

            _cacheManager.RemoveByPattern(NEWSCATEGORY_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityUpdated(category);
        }

        #endregion

        #endregion
    }
}



另外需要增加NewsExtensions.cs,主要用于新闻类别的排序(对无限极分类的排序等等),代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using Nop.Core.Domain.News;

namespace Nop.Services.News
{
    /// <summary>
    /// Extensions
    /// </summary>
    public static class NewsExtensions
    {
        /// <summary>
        /// Sort news categories for tree representation
        /// </summary>
        /// <param name="source">Source</param>
        /// <param name="parentId">Parent category identifier</param>
        /// <returns>Sorted categories</returns>
        public static IList<NewsCategory> SortCategoriesForTree(this IList<NewsCategory> source, int parentId)
        {
            var result = new List<NewsCategory>();

            var temp = source.ToList().FindAll(c => c.ParentCategoryId == parentId);
            foreach (var cat in temp)
            {
                result.Add(cat);
                result.AddRange(SortCategoriesForTree(source, cat.Id));
            }
            return result;
        }

        public static string GetCategoryNameWithPrefix(this NewsCategory category, INewsService newsService)
        {
            string result = string.Empty;

            while (category != null)
            {
                if (String.IsNullOrEmpty(result))
                    result = category.Name;
                else
                    result = "--" + result;
                category = newsService.GetNewsCategoryById(category.ParentCategoryId);
            }
            return result;
        }

        public static string GetCategoryBreadCrumb(this NewsCategory category, INewsService newsService)
        {
            string result = string.Empty;

            while (category != null && !category.Deleted)
            {
                if (String.IsNullOrEmpty(result))
                    result = category.Name;
                else
                    result = category.Name + " >> " + result;

                category = newsService.GetNewsCategoryById(category.ParentCategoryId);

            }
            return result;
        }

    }
}



5、到此底层的数据操作已经基本完成,开始修改后台管理部分。
Nop.Admin\Models\News\增加新闻类别页面所需要的ViewModel,代码如下:

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using FluentValidation.Attributes;
using Nop.Admin.Validators.News;
using Nop.Web.Framework;
using Nop.Web.Framework.Mvc;

namespace Nop.Admin.Models.News
{
    [Validator(typeof(NewsCategoryValidator))]
    public class NewsCategoryModel : BaseNopEntityModel
    {
        public NewsCategoryModel()
        {
            AvailableCategories = new List<SelectListItem>();
        }

        [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.Name")]
        public string Name { get; set; }

        [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.Description")]
        [AllowHtml]
        public string Description { get; set; }

        [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.ParentName")]
        public int ParentCategoryId { get; set; }

        [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.ParentName")]
        public string ParentCategoryName { get; set; }

        [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.Published")]
        public bool Published { get; set; }

        [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.DisplayOrder")]
        public int DisplayOrder { get; set; }

        [NopResourceDisplayName("Admin.ContentManagement.News.NewsCategoryItems.Fields.CreatedOn")]
        public DateTime CreatedOn { get; set; }

        public IList<SelectListItem> AvailableCategories { get; set; }
    }
}



Nop.Admin\Validators\News\增加类别的验证类NewsCategoryValidator.cs,主要用于后台添加时候的非空字数限制等等,代码如下

using FluentValidation;
using Nop.Admin.Models.News;
using Nop.Services.Localization;

namespace Nop.Admin.Validators.News
{
    public class NewsCategoryValidator : AbstractValidator<NewsCategoryModel>
    {
        public NewsCategoryValidator(ILocalizationService localizationService)
        {
            RuleFor(x => x.Name)
                .NotNull()
                .WithMessage(localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Fields.Name.Required"));

            RuleFor(x => x.Description)
                .NotNull()
                .WithMessage(localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Fields.Description.Required"));
        }
    }
}



修改Nop.Admin/MappingExtensions.cs文件,增加新闻类别相关内容,这几个方法主要是NewsCategoryNewsCategoryModel之间的相互转换,代码如下:

//news category items
        public static NewsCategoryModel ToModel(this NewsCategory entity)
        {
            return Mapper.Map<NewsCategory, NewsCategoryModel>(entity);
        }

        public static NewsCategory ToEntity(this NewsCategoryModel model)
        {
            return Mapper.Map<NewsCategoryModel, NewsCategory>(model);
        }

        public static NewsCategory ToEntity(this NewsCategoryModel model, NewsCategory destination)
        {
            return Mapper.Map(model, destination);
        }



修改Nop.Admin/Infrastructure/AutoMapperStartupTask.cs文件,增加相关映射规则,如果不加的话会出错的哦,
AutoMapper是基于对象到对象约定的映射工具,常用于(但并不仅限制于)把复杂的对象模型转为DTO,一般用于ViewModel模式和跨服务范畴。
AutoMapper给用户提供了便捷的配置API,就像使用约定来完成自动映射那样。
代码如下:

Mapper.CreateMap<NewsCategory, NewsCategoryModel>()
                .ForMember(dest => dest.AvailableCategories, mo => mo.Ignore())
                .ForMember(dest => dest.ParentCategoryName, mo => mo.Ignore())
                .ForMember(dest => dest.CreatedOn, mo => mo.Ignore());
            Mapper.CreateMap<NewsCategoryModel, NewsCategory>()
                .ForMember(dest => dest.CreatedOn, mo => mo.Ignore());

最后修改Nop.Admin\Controllers\NewsController.cs,增加新闻类别的增删改查操作,如下:

#region News category items
        public ActionResult CategoryList()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews))
                return AccessDeniedView();

            var news = _newsService.GetAllNewsCategory(0, _adminAreaSettings.GridPageSize, true);
            var gridModel = new GridModel<NewsCategoryModel>
            {
                Data = news.Select(x =>
                {
                    var m = x.ToModel();
                    m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOn, DateTimeKind.Utc);
                    m.Name = x.GetCategoryBreadCrumb(_newsService);
                    m.Published = x.Published;
                    m.DisplayOrder = x.DisplayOrder;
                    return m;
                }),
                Total = news.TotalCount
            };
            return View(gridModel);
        }

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult CategoryList(GridCommand command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews))
                return AccessDeniedView();

            var news = _newsService.GetAllNewsCategory(0, _adminAreaSettings.GridPageSize, true);
            var gridModel = new GridModel<NewsCategoryModel>
            {
                Data = news.Select(x =>
                {
                    var m = x.ToModel();
                    m.CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOn, DateTimeKind.Utc);
                    m.Name = x.GetCategoryBreadCrumb(_newsService);
                    m.Published = x.Published;
                    m.DisplayOrder = x.DisplayOrder;
                    return m;
                }),
                Total = news.TotalCount
            };
            return new JsonResult
            {
                Data = gridModel
            };
        }

        public ActionResult CategoryCreate()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews))
                return AccessDeniedView();

            var model = new NewsCategoryModel();
            //default values
            model.Published = true;

            //categories
            model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var c in _newsService.GetAllNewsCategory(true))
                model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_newsService), Value = c.Id.ToString() });

            return View(model);
        }
        [HttpPost, FormValueExists("save", "save-continue", "continueEditing")]
        public ActionResult CategoryCreate(NewsCategoryModel model, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews))
                return AccessDeniedView();

            if (ModelState.IsValid)
            {
                var newsItem = model.ToEntity();
                newsItem.CreatedOn = DateTime.UtcNow;
                newsItem.UpdatedOn = DateTime.UtcNow;
                _newsService.InsertNewsCategory(newsItem);

                SuccessNotification(_localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Added"));
                return continueEditing ? RedirectToAction("CategoryEdit", new { id = newsItem.Id }) : RedirectToAction("CategoryList");
            }

            return View(model);
        }

        public ActionResult CategoryEdit(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews))
                return AccessDeniedView();

            var newsItem = _newsService.GetNewsCategoryById(id);
            if (newsItem == null)
                //No news item found with the specified id
                return RedirectToAction("CategoryList");

            var model = newsItem.ToModel();
            //categories
            model.AvailableCategories.Add(new SelectListItem() { Text = _localizationService.GetResource("Admin.Common.All"), Value = "0" });
            foreach (var c in _newsService.GetAllNewsCategory(true))
                model.AvailableCategories.Add(new SelectListItem() { Text = c.GetCategoryNameWithPrefix(_newsService), Value = c.Id.ToString() });

            return View(model);
        }

        [HttpPost, FormValueExists("save", "save-continue", "continueEditing")]
        public ActionResult CategoryEdit(NewsCategoryModel model, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews))
                return AccessDeniedView();

            var newsItem = _newsService.GetNewsCategoryById(model.Id);
            if (newsItem == null)
                //No news item found with the specified id
                return RedirectToAction("CategoryList");

            if (ModelState.IsValid)
            {
                newsItem = model.ToEntity(newsItem);
                newsItem.UpdatedOn = DateTime.UtcNow;
                _newsService.UpdateNewsCategory(newsItem);

                SuccessNotification(_localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Updated"));
                return continueEditing ? RedirectToAction("CategoryEdit", new { id = newsItem.Id }) : RedirectToAction("List");
            }

            return View(model);
        }

        [HttpPost, ActionName("DeleteCategory")]
        public ActionResult DeleteCategoryConfirmed(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageNews))
                return AccessDeniedView();

            var newsItem = _newsService.GetNewsCategoryById(id);
            if (newsItem == null)
                //No news item found with the specified id
                return RedirectToAction("CategoryList");

            _newsService.DeleteNewsCategory(newsItem);

            SuccessNotification(_localizationService.GetResource("Admin.ContentManagement.News.NewsCategoryItems.Deleted"));
            return RedirectToAction("CategoryList");
        }

        #endregion


6、接下来就是页面View了,代码太多,这里不再具体列出,不熟悉MVC机制的可以先补充一下MVC相关知识。
稍后整理提供下载。

分享是一种美。版权所有,转载请注明出处 http://www.nopchina.net/

教你一招 - 如何安装nopcommerce2.5

1、确认服务器安装有iis、.net framework4、mvc3、sql server。

2、下载官方no source版压缩包,如果是学习的话也可以下载with source code版。 下载地址

nopchina - 教你一招系列

3、把下载的压缩包解压到指定目录,在iis里面配置好,注意asp.net版本选择和MVC的设置。

4、配置好以后,尝试打开http://xxx/install 第一次打开会进入安装向导页面,创建数据库、管理员、模拟数据等等,如图所示:

nopchina - 教你一招系列

5、按照上图提示输入完整后,点击Install按钮,如果一切正常的话便开始安装,安装成功后就自动跳转到首页。

注:第一次接触mvc版可能不知道链接字符串在哪修改?  在这个文件找找看:\Presentation\Nop.Web\App_Data\Settings.txt。

分享是一种美。版权所有,转载请注明出处 http://www.nopchina.net/

教你一招 - 如何使用nopcommerce主题(v2.5)

 从网上下载了主题包如何使用,看下面的步骤:

1、把下载的模板主题zip文件解压到网站“\Presentation\Nop.Web\Themes”文件夹下。下载模板

2、进入管理后台,打开配置 - 设置 - 综合设置,如图所示:

 nopchina - 教你一招系列

3、在上面的中选择对应的模板名称,然后点击保存。

4、刷新前台看看效果吧:

nopchina - 教你一招系列

分享是一种美。版权所有,转载请注明出处 http://www.nopchina.net/

教你一招 - 如何使用中文包

 下载了中文包不会用怎么办,看下面的步骤:

1、下载语言包,解压得到xml文件,如果没有的话点击这里下载 中文包

2、进入管理中心,打开Configuration - Languages页面,点击Add new,如图所示:

nopchina - 教你一招系列

3、输入上面内容后,点击

4、进入语言编辑页面,点击Import resources,如图所示:

nopchina - 教你一招系列

5、选择刚才下载的语言包,然后点击Import resources即可导入。

6、然后在右上角语言选择中选择中文即可看到效果,如图所示:

nopchina - 教你一招系列

7、最后来张效果图:

nopchina - 教你一招系列

分享是一种美。版权所有,转载请注明出处 http://www.nopchina.net/