mvc3的实际应用时间还是不长,有些东西正在摸索当中,项目是多用户多模版店铺,以下为实际开发过程中的解决办法,感觉解决方案不是最好的,但是目前只能想到这些,希望园里的大牛们给点建议。
1.项目解决方案的目录结构。

Syw.Core主要放实体类及依赖注入的程序及插件和数据访问接口。
Syw.Data.SqlServer完全是一大堆sql,实现Syw.Core里的IData类。对orm我没深入使用过,还是觉得最大程度的控制我的sql比较放心,所有的集合都是List或者Ilist类型的。
Syw.Services业务处理层,类似以前的bll,这里用的是静态方法,没有走接口了,项目不是特别大,一个人开发,接口太多会很累。
Syw.Test单元测试用的,有时候调试返回结果还是很有用的。
Syw.Admin,后台管理,但是这个我参考的nopcommerce,生成的时候直接在网站更目录下生成Administrator文件夹,View和Content文件夹都在这里,但是dll文件是生成到Syw.Web这个主目录下的,这样我就不需要用二级域名来访问后台了,直接通过http://xxxx.com/admin就可以了。
Syw.Framework主要是一些基础帮助类。例如图片处理,邮件发送帮助类,字符串处理,自定义ui等等。
2.模板路径及结构


a1,a2,b1为不同风格的模板,不仅仅颜色不相同,排版方式也不相同。现在只有三个模板,通过这种方式选择模板。a1,a2为店铺类的,a3为服务类企业用的。
_LayoutStore.cshtml是店铺的主模板页。通过@Hmtl.Action来访问ChildAction加载页面,@RenderBody() 加载主页面。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <title>@ViewBag.Title -ooxx网</title> <meta name="keywords" content="@ViewBag.Title"> <meta name="description" content="@ViewBag.Title"> <script type="text/javascript" src="/Scripts/jquery-1.4.4.min.js"></script> <link href="/StoreThemes/a2/Content/style2.css" type="text/css" rel="stylesheet" /> </head> <body> <!--banner以上的头部--> @Html.Action("Header", "Store", new { store = ViewBag.Model.Store }) <!--banner以上的头部 结束--> <!--顶部banner和导航--><!--考虑传参数model--> @Html.Action("TopBanner", "Store", new { store = ViewBag.Model.Store, act = Url.RequestContext.RouteData.Values["action"].ToString().ToLower() }) <!--顶部banner和导航结束--> <div class="con"> @Html.Action("SideBar", "Store", new { store = ViewBag.Model.Store }) <div class="con-right"> <!-- include --> @RenderBody() <!-- include end --> </div> </div> <!-- 页脚 --> @Html.Action("Footer","Store",new { store = ViewBag.Model.Store }) <!-- 页脚 结束 --> </body> </html>
StoreController里的关于我们的Action
#region 关于我们 /// <summary> /// 店铺简介 /// </summary> /// <returns></returns> public ActionResult About(int id) { var model = new StoreModel(); model.Store = StoreService.GetStoreById(id); model.CurrentAction = RouteData.Values["action"].ToString().ToLower().ToLower(); ViewBag.Model = model; return View(model.Store.Theme.ViewPath + "About.cshtml", model); } #endregion
ChildAction如下,网站底部
[ChildActionOnly] public ActionResult Footer(StoreInfo store) { var model =new StoreModel(); model.Store = store; if (store.Theme != null) { return PartialView(store.Theme.ViewPath + "Footer.cshtml", model); } else { return PartialView(StoreService.GetStoreConfig().DefaultView + "Footer.cshtml", model); } }
后台选择模板

通过这种简单的方式实现了多用户店铺多模板的选择功能,自定义二级域名不在这里赘述了。
自我感觉还有很多不完善的地方,欢迎拍砖。
独立博客地址:http://www.jqpress.com/post/182.aspx
nopcommerce插件机制是相当优秀的,所以就分析一下然后拿来所用,集成到自己的网站架构里。写篇小文记录一下。不足和错误之处还望指正,nop版本2.5
1.Nop.Core.Plugins核心文件夹
文件目录:

这里面是Plugins的基类文件夹,实现插件机制的核心部分。
IPluginFinder.cs接口:
获取插件的信息接口,在ioc里的Nop.Web.Framework.DependencyRegistrar注册此接口。
系统启动的时候会加载到内存里。
//plugins
builder.RegisterType<PluginFinder>().As<IPluginFinder>().InstancePerHttpRequest();
IPlugin.cs:
插件的操作接口,主要有设置插件的属性信息,安装插件接口,卸载插件接口。
BasePlugins.cs 实现IPlugin.cs的方法。
PluginDescriptor.cs 插件的实体类,包含了插件的版本、描述,类型,文件名称,作者,等等一系列状态。
PluginFileParser。cs 包含对插件的实体操作方法,主要是写入插件的描述信息。
PluginFinder.cs 加载所有的插件,并获取它们的信息.
PluginManager.cs 插件管理的主类,看里面的注释,它的插件机制应该是参考的Umbraco这个cms的。
2.自定义插件

在这里我演示一个空的插件,本来是写抓取程序的,时间关系,没有做 了。插件名称,Nop.Plugin.Crawler
第一步:新建一个类库项目,而不是mvc应用项目。

第二步:在你的项目里添加需要的文件夹和文件:
Controllers,必选,控制器
Models,可选,如果你的项目很简单的话。
Views,可选但是一般情况下都需要有个界面的,放razor模板的.
Description.txt,这个必须要,插件的版本描述信息。内容如下
Group: Crawler
FriendlyName:Crawler
SystemName: Nop.Plugin.Crawler
Version: 1.00
SupportedVersions: 2.50
Author: nopCommerce team
DisplayOrder: 1
FileName: Nop.Plugin.Crawler.dll
在它上面点右键设置属性,如下图

Notes.txt ,这个是插件的备注信息,描述了插件的文件位置和一些注意事项。
RouteProvider.cs,路由文件,继承自Nop.Web.Framework.Mvc.Routes.IRouteProvider
View Code
using System.Web.Mvc; using System.Web.Routing; using Nop.Web.Framework.Mvc.Routes; namespace Nop.Plugin.Crawler { public partial class RouteProvider : IRouteProvider { public void RegisterRoutes(RouteCollection routes) { routes.MapRoute("Nop.Plugin.Crawler", "Plugins/Crawler/Index", new { controller = "Crawler", action = "Index" }, new[] { "Nop.Plugin.Crawler.Controllers" } ); } public int Priority { get { return 0; } } } }
web.config,.net配置文件。
CrawlerController.cs 在我的控制器里定义了一个返回简单页面的方法。
using System.Web.Mvc; using Nop.Web.Framework.Controllers; namespace Nop.Plugin.Crawler.Controllers { [AdminAuthorize] public class CrawlerController : Controller { public ActionResult Index() { return View("Nop.Plugin.Crawler.Views.Index"); } } }
大家注意了,返回的View里的view路径是插件里的视图路径,在view的Index.cshtml点右键设置的属性,cshtml是编译到dll里的

还有整个插件的属性,在插件项目上点右键设置输出属性

最后设置引用进来的dll文件属性。这样输出dll文件的时候不会把其他乱七八糟的文件也拷贝过来了。




在插件上点右键生只输出这三个文件
3.最后一步在Nop.Web的InstalledPlugins.txt里添加插件

插件名称不能重复,可以自定义。这里我的取的名字和插件项目名称相同没什么特殊要求。

后台插件预览就可以看到了。

偶尔翻到豆瓣里一篇对中国屌丝的批评,突然想到当年美国那个垮掉的一代,吸毒,淫乱,绝望的生存,而如今我们苦逼的80后自诩为屌丝的时候,也不想想每一个堕落的时代还是有牛逼的人存在,中国的大学,绝大部分在逃课,生殖冲动,暗无天日的游戏,苦逼学习的几乎是不入群,悲观消极的情绪像哈欠一样传染着每一个容易被感染的苦逼青年。毕业后即失业,人生没有方向,更无从理想了,傻逼的政府不知道怎么搞教育,造就了一群乌合之众的国家,纵使在这样的环境下,还是有人为理想奋斗着,每一天在充实,相信总有一天会有自己出头的。
我不吸毒,不喝酒,不打游戏,不搞乌烟瘴气的社会关系,在苦逼的it路上挨踢,虽然天资和能力都进不了微软google之类的公司,每天在自己既定的方向一步步前进。做着自己喜欢做的事情,认真做好每一件事情。摆脱虚伪的自我,既敢为了感情牺牲失业,又要做到掌握自己人生的方向,拒绝屌丝的自嘲,拒绝屌丝的人生。
当明白一切时,秋叶已泛黄,在收获的季节里播种,是期望下一个春天还是奢望冬天来临时那无几的颗实,纵使如此,朝闻道夕可死。
第一个代码文件
View Code
public class DomainRoute : Route
{
private Regex domainRegex;
private Regex pathRegex;
public string Domain { get; set; }
public DomainRoute(string domain, string url, RouteValueDictionary defaults)
: base(url, defaults, new MvcRouteHandler())
{
Domain = domain;
}
public DomainRoute(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler)
{
Domain = domain;
}
public DomainRoute(string domain, string url, object defaults)
: base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
{
Domain = domain;
}
public DomainRoute(string domain, string url, object defaults, IRouteHandler routeHandler)
: base(url, new RouteValueDictionary(defaults), routeHandler)
{
Domain = domain;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
// 构造 regex
domainRegex = CreateRegex(Domain);
pathRegex = CreateRegex(Url);
// 请求信息
string requestDomain = httpContext.Request.Headers["host"];
if (!string.IsNullOrEmpty(requestDomain))
{
if (requestDomain.IndexOf(":") > 0)
{
requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":"));
}
}
else
{
requestDomain = httpContext.Request.Url.Host;
}
string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
// 匹配域名和路由
Match domainMatch = domainRegex.Match(requestDomain);
Match pathMatch = pathRegex.Match(requestPath);
// 路由数据
RouteData data = null;
if (domainMatch.Success && pathMatch.Success)
{
data = new RouteData(this, RouteHandler);
// 添加默认选项
if (Defaults != null)
{
foreach (KeyValuePair<string, object> item in Defaults)
{
data.Values[item.Key] = item.Value;
}
}
// 匹配域名路由
for (int i = 1; i < domainMatch.Groups.Count; i++)
{
Group group = domainMatch.Groups[i];
if (group.Success)
{
string key = domainRegex.GroupNameFromNumber(i);
if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
{
if (!string.IsNullOrEmpty(group.Value))
{
data.Values[key] = group.Value;
}
}
}
}
// 匹配域名路径
for (int i = 1; i < pathMatch.Groups.Count; i++)
{
Group group = pathMatch.Groups[i];
if (group.Success)
{
string key = pathRegex.GroupNameFromNumber(i);
if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
{
if (!string.IsNullOrEmpty(group.Value))
{
data.Values[key] = group.Value;
}
}
}
}
}
return data;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return base.GetVirtualPath(requestContext, RemoveDomainTokens(values));
}
public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values)
{
// 获得主机名
string hostname = Domain;
foreach (KeyValuePair<string, object> pair in values)
{
hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString());
}
// Return 域名数据
return new DomainData
{
Protocol = "http",
HostName = hostname,
Fragment = ""
};
}
private Regex CreateRegex(string source)
{
// 替换
source = source.Replace("/", @"\/?");
source = source.Replace(".", @"\.?");
source = source.Replace("-", @"\-?");
source = source.Replace("{", @"(?<");
source = source.Replace("}", @">([a-zA-Z0-9_]*))");
return new Regex("^" + source + "$");
}
private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
{
Regex tokenRegex = new Regex(@"({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?");
Match tokenMatch = tokenRegex.Match(Domain);
for (int i = 0; i < tokenMatch.Groups.Count; i++)
{
Group group = tokenMatch.Groups[i];
if (group.Success)
{
string key = group.Value.Replace("{", "").Replace("}", "");
if (values.ContainsKey(key))
values.Remove(key);
}
}
return values;
}
}
第二个代码文件
public class DomainData
{
public string Protocol { get; set; }
public string HostName { get; set; }
public string Fragment { get; set; }
}
在global里注册实现
routes.Add("DomainRoute", new DomainRoute(
"{subdomain}.您的域名.com", // Domain with parameters
"{controller}/{action}/{id}", // URL with parameters
new { subdomain = "", controller = "Home", action = "Index", id = "" } // Parameter defaults
));
在HomeController的index 里获取域名值
//获取二级域名
var subdomain = RouteData.Values["subdomain"].ToString();
剩下的工作接着做
建议博客园成立中国的开源项目组织,园子里这么多大牛,更多技术爱好者和分享者,而且很多牛人也有自己的开源项目托管在googlecode,codeplex,sourceforge,github,或者自己的爱机里,如果博客园里能专门托管园子里大牛的开源项目不是更好吗,每年选取一两个优秀的项目应该就够了(在这里更正一下,不是源码托管站,是类似apache的孵化器组织)况且园子里也有很多优秀的专题小组,资源要利用啊。如果有技术类商业公司赞助博客园的开源项目,既宣传了企业又推动了博客园发展,大家各取所需不也是很好吗?
当然让dudu做这个决定是艰难的,服务器要钱,人员要钱,所以要园友们共同支持,能孵化出中国自己的商业开源项目,哪怕是梦也要梦一下。作为长年泡在园子里的园友更希望博客园成为中国最好的技术社区,比那些广告满天飞的所谓的中国最大的技术社区更有影响力,事情是一件一件做出来的,估计dudu从来也没闲着,但是提出这个意见还是希望dudu做一个艰难的决定。
做一件事情开始一定是艰难的,下面的批评都有道理,博客园不就是这样坚持下来的吗。但是相信dudu,也相信园友,很小的项目哪怕是个小blog,小cms,小工具,一粒种子很可能就会成为大树。况且中国暂时没发现有有影响力的开源组织,有不少个人开源项目站点和大量的垃圾源码站。
好吧,那还是让园友做这个艰难的决定了,同意的推荐一下,不同意的反对一下。
ps一下:有个人的开源或者准备开源的项目爆出来给dudu和园友看看


