john's tech blog

hope is coming


  • 首页

  • 标签

  • 归档

orchard中Controller是如何找到的

发表于 2015-09-30 | 更新于 2019-05-09

Orchard 控制器解析1

CompositionStrategy类

CompositionStrategy中跟控制器相关的就是Compose函数。看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 var allRoutes = new List<RouteDescriptor>();
var controllers = BuildBlueprint(features, IsController, BuildController, excludedTypes);

//通过Feature设置AreaName
private static ControllerBlueprint BuildController(Type type, Feature feature) {
var areaName = feature.Descriptor.Extension.Id;

var controllerName = type.Name;
if (controllerName.EndsWith("Controller"))
controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);

return new ControllerBlueprint {
Type = type,
Feature = feature,
AreaName = areaName,//AreaName在这里加上 便于后面 ControllFactory中去寻找
ControllerName = controllerName,
};

}

ShellContainerFactory类:

然后再通过ShellContainerFactory 加入orchard容器中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
foreach (var item in blueprint.Controllers) {
//这里很关键是通过AreaName和ControllerName注入
var serviceKeyName = (item.AreaName + "/" + item.ControllerName).ToLowerInvariant();
var serviceKeyType = item.Type;
RegisterType(builder, item)
.EnableDynamicProxy(dynamicProxyContext)
.Keyed<IController>(serviceKeyName)//这里供OrchardControllerFactory解析
.Keyed<IController>(serviceKeyType)
.WithMetadata("ControllerType", item.Type)
.InstancePerDependency()
.OnActivating(e => {
// necessary to inject custom filters dynamically
// see FilterResolvingActionInvoker
//需要动态注入filter
var controller = e.Instance as Controller;
if (controller != null)
controller.ActionInvoker = (IActionInvoker)e.Context.ResolveService(new TypedService(typeof(IActionInvoker)));
});
}

OrchardControllerFactory

然后控制器工厂OrchardControllerFactory类通过RouteData寻找Controller
路由数据就是在ShellRoute中找到匹配的路由数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected override Type GetControllerType(RequestContext requestContext, string controllerName) {
var routeData = requestContext.RouteData;

// Determine the area name for the request, and fall back to stock orchard controllers
var areaName = routeData.GetAreaName();
//这里通过routeData里的area controllername 然后去寻找Metadata匹配
// Service name pattern matches the identification strategy
var serviceKey = (areaName + "/" + controllerName).ToLowerInvariant();

// Now that the request container is known - try to resolve the controller information
Meta<Lazy<IController>> info;
var workContext = requestContext.GetWorkContext();
if (TryResolve(workContext, serviceKey, out info)) {
return (Type) info.Metadata["ControllerType"];
}

return null;
}

orchard-route

发表于 2015-09-30 | 更新于 2019-05-09

Orchard路由解析1

DefaultOrchardShell类

DefaultOrchardShell中跟路由相关的就是Active函数。看代码:

1
2
3
var allRoutes = new List<RouteDescriptor>();
//注册的时候 只有 Orchard.Setup.Routes这个类
allRoutes.AddRange(_routeProviders.SelectMany(provider => provider.GetRoutes()));//SellContainerFactory中注册到容器

然后再通过routePublisher 加入RouteCollection集合

1
2
3
4
5
6
7
8
//封装成ShellRoute对象
//传入shellSettings
var shellRoute = new ShellRoute(routeDescriptor.Route, _shellSettings, _workContextAccessor, _runningShellTable) {
IsHttpRoute = routeDescriptor is HttpRouteDescriptor,
SessionState = sessionStateBehavior
};
//_routeCollection 为RouteTable.Routes
_routeCollection.Add(routeDescriptor.Name, shellRoute);

publish的时候会先对RouteArray排序,根据Priority

1
2
3
var routesArray = routes
.OrderByDescending(r => r.Priority)
.ToArray();

模块的RouteBase对象都封装成ShellRoute对象,以后都在ShellRoute中解析。

接下来我们来看看安装完后 首页是如何定位到Controller的。

orchard-recipe

发表于 2015-09-30 | 更新于 2019-05-09

Orchard的Recipe是如何解析的

SetupService类

安装的时候先去发现Recipe:

1
_recipeHarvester.HarvestRecipes("Orchard.Setup");

RecipeHarvester类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// 比如extensionId 为 Orchard.Setup
/// 找到以.recipe.xml结束的文件
/// </summary>
/// <param name="extensionId"></param>
/// <returns></returns>
public IEnumerable<Recipe> HarvestRecipes(string extensionId) {
var recipes = new List<Recipe>();
var extension = _extensionManager.GetExtension(extensionId);
if (extension != null) {
// ~/Modules/Orchard.Setup/Recipes
var recipeLocation = Path.Combine(extension.Location, extensionId, "Recipes");
var recipeFiles = _webSiteFolder.ListFiles(recipeLocation, true);
recipes.AddRange(
from recipeFile in recipeFiles
where recipeFile.EndsWith(".recipe.xml", StringComparison.OrdinalIgnoreCase)
select _recipeParser.ParseRecipe(_webSiteFolder.ReadFile(recipeFile)));//根据虚拟目录来
}
else {
Logger.Error("Could not discover recipes because module '{0}' was not found.", extensionId);
}
//返回组装好的Recipe
return recipes;
}

RecipeParser类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public Recipe ParseRecipe(string recipeText) {
var recipe = new Recipe();

try {
var recipeSteps = new List<RecipeStep>();
TextReader textReader = new StringReader(recipeText);
var recipeTree = XElement.Load(textReader, LoadOptions.PreserveWhitespace);
textReader.Close();

foreach (var element in recipeTree.Elements()) {
// Recipe mETaDaTA
if (element.Name.LocalName == "Recipe") {
foreach (var metadataElement in element.Elements()) {
switch (metadataElement.Name.LocalName) {
case "Name":
recipe.Name = metadataElement.Value;
break;
case "Description":
recipe.Description = metadataElement.Value;
break;
case "Author":
recipe.Author = metadataElement.Value;
break;
case "WebSite":
recipe.WebSite = metadataElement.Value;
break;
case "Version":
recipe.Version = metadataElement.Value;
break;
case "Tags":
recipe.Tags = metadataElement.Value;
break;
default:
Logger.Error("Unrecognized recipe metadata element {0} encountered. Skipping.", metadataElement.Name.LocalName);
break;
}
}
}
// Recipe step
else {
var recipeStep = new RecipeStep { Name = element.Name.LocalName, Step = element };
recipeSteps.Add(recipeStep);
}
}
recipe.RecipeSteps = recipeSteps;
}
catch (Exception exception) {
Logger.Error(exception, "Parsing recipe failed. Recipe text was: {0}.", recipeText);
throw;
}

return recipe;
}

安装的时候就会通过在界面中选择的model.Recipe去Execute对应的Recipe.

RecipeManager类

1
2
3
4
5
6
7
8
9
10
11
12
if (recipe == null)
return null;

var executionId = Guid.NewGuid().ToString("n");
_recipeJournal.ExecutionStart(executionId);

foreach (var recipeStep in recipe.RecipeSteps) {
_recipeStepQueue.Enqueue(executionId, recipeStep);
}
_recipeScheduler.ScheduleWork(executionId);

return executionId;

先把RecipeStep加入recipeStepQueue中,其实内部就是写入一个个文件

RecipeStepQueue类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void Enqueue(string executionId, RecipeStep step) {
var recipeStepElement = new XElement("RecipeStep");
recipeStepElement.Add(new XElement("Name", step.Name));
recipeStepElement.Add(step.Step);

if (_appDataFolder.DirectoryExists(Path.Combine(_recipeQueueFolder, executionId))) {
int stepIndex = GetLastStepIndex(executionId) + 1;
_appDataFolder.CreateFile(Path.Combine(_recipeQueueFolder, executionId + Path.DirectorySeparatorChar + stepIndex),
recipeStepElement.ToString());
}
else {
_appDataFolder.CreateFile(
Path.Combine(_recipeQueueFolder, executionId + Path.DirectorySeparatorChar + "0"),
recipeStepElement.ToString());
}
}

然后RecipeStepExecutor就去去执行各个Step

RecipeStepExecutor类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public bool ExecuteNextStep(string executionId) {
var nextRecipeStep= _recipeStepQueue.Dequeue(executionId);
if (nextRecipeStep == null) {
_recipeJournal.ExecutionComplete(executionId);
return false;
}
_recipeJournal.WriteJournalEntry(executionId, string.Format("Executing step {0}.", nextRecipeStep.Name));
var recipeContext = new RecipeContext { RecipeStep = nextRecipeStep, Executed = false };
try {
foreach (var recipeHandler in _recipeHandlers) {
recipeHandler.ExecuteRecipeStep(recipeContext);
}
}
...

有没有发现 其实到最后是通过recipeHandler来处理的
我们来看看有多少Handler
FeatureRecipeHandler
CommandRecipeHandler
DataRecipeHandler
MetadataRecipeHandler
MigrationRecipHandler
ModuleRecipeHandler
SettingsRecipeHandler
ThemeRecipeHandler

看看FeatureRecipeHandler内部是怎么ExecuteRecipeStep的

FeatureRecipeHandler类

1
2
3
4
5
6
// <Feature enable="f1,f2,f3" disable="f4" />
// Enable/Disable features.
public void ExecuteRecipeStep(RecipeContext recipeContext) {
if (!String.Equals(recipeContext.RecipeStep.Name, "Feature", StringComparison.OrdinalIgnoreCase)) {
return;
}

一开始判断RecipeStep.Name是不是Feature,不是就直接Return.

1
2
3
4
5
foreach (var featureName in featuresToEnable) {
if (!availableFeatures.Contains(featureName)) {
throw new InvalidOperationException(string.Format("Could not enable feature {0} because it was not found.", featureName));
}
}

随后去availableFeatures中判断是不是存在featuresToEnable中的某个feature。

1
2
3
4
然后去EnableFeatures.
if (featuresToEnable.Count != 0) {
_featureManager.EnableFeatures(featuresToEnable, true);
}

FeatureManager类

1
2
_shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures,
shellDescriptor.Parameters);

我们先来看这两段代码:

1
2
ShellDescriptor shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
List<ShellFeature> enabledFeatures = shellDescriptor.Features.ToList();

Orchard-Layout-Create

发表于 2015-09-28 | 更新于 2019-05-09

Orchard安装界面的布局创建过程

LayoutAwareViewEngine类

这个类是布局创建的入口,什么时候调用这个类呢?看同级目录ThemeAwareness中的ThemedViewResultFilter.cs。

从类命名就可以看出这是一个视图结果筛选器,在这里面把视图引擎集合重新定义了一下。

1
viewResultBase.ViewEngineCollection = new ViewEngineCollection(new[] { _layoutAwareViewEngine });

当然_layoutAwareViewEngine是通过Autofac这个强大的IOC容器去resolve出来的。Autofac的解析我们以后会分析。
实际就是调用了LayoutAwareViewEngine类。
那么这个过滤器是什么时候去调用的呢?就靠FilterProvider这个抽象类了。FilterProvider实现了IFilterProvider接口。
IFilterProvider接口是在SetupMode类中注册的

1
2
builder.RegisterType<ThemedViewResultFilter>().As<IFilterProvider>().InstancePerLifetimeScope();
builder.RegisterType<ThemeFilter>().As<IFilterProvider>().InstancePerLifetimeScope();

为什么要在SetupMode中注册呢?因为执行安装的时候没有把Orchard.Framework的ShellFeature给包装进来,看代码:

1
2
3
4
5
6
7
8
var descriptor = new ShellDescriptor {
SerialNumber = -1,
Features = new[] {
new ShellFeature { Name = "Orchard.Setup" },
new ShellFeature { Name = "Shapes" },//这里 用于找 模板
new ShellFeature { Name = "Orchard.jQuery" },
},
};

如果不是安装过程 ,那么直接在CreateContainerFactory.CreateContainer中通过IDependency接口去调用

1
2
3
4
5
//实现IDependency接口的依赖都可以使用动态代理 AOP
foreach (var item in blueprint.Dependencies.Where(t => typeof(IDependency).IsAssignableFrom(t.Type))) {
var registration = RegisterType(builder, item)
.EnableDynamicProxy(dynamicProxyContext)
.InstancePerLifetimeScope();

jquery-find

发表于 2015-09-25 | 更新于 2019-05-09

jquery1.0源码解读

find函数

1.0源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
find: function( t, context ) {
// Make sure that the context is a DOM Element
if ( context && context.nodeType == undefined )
context = null;

// Set the correct context (if none is provided)
//设置当前上下文为Document
context = context || jQuery.context || document;

if ( t.constructor != String ) return [t];

if ( !t.indexOf("//") ) {
context = context.documentElement;
t = t.substr(2,t.length);
} else if ( !t.indexOf("/") ) {
context = context.documentElement;
t = t.substr(1,t.length);
// FIX Assume the root element is right :(
if ( t.indexOf("/") >= 1 )
t = t.substr(t.indexOf("/"),t.length);
}

var ret = [context];
var done = [];
var last = null;

while ( t.length > 0 && last != t ) {
var r = [];
last = t;

t = jQuery.trim(t).replace( /^\/\//i, "" );

var foundToken = false;
//这里调用token 正则匹配 然后在map里传入function的字符串。。
for ( var i = 0; i < jQuery.token.length; i += 2 ) {
var re = new RegExp("^(" + jQuery.token[i] + ")");
var m = re.exec(t);

if ( m ) {
r = ret = jQuery.map( ret, jQuery.token[i+1] );
t = jQuery.trim( t.replace( re, "" ) );
foundToken = true;
}
}

if ( !foundToken ) {
if ( !t.indexOf(",") || !t.indexOf("|") ) {
if ( ret[0] == context ) ret.shift();
done = jQuery.merge( done, ret );
r = ret = [context];
t = " " + t.substr(1,t.length);
} else {
var re2 = /^([#.]?)([a-z0-9\\*_-]*)/i;//# 或 .开头
var m = re2.exec(t);

if ( m[1] == "#" ) {
// Ummm, should make this work in all XML docs
var oid = document.getElementById(m[2]);
r = ret = oid ? [oid] : [];
t = t.replace( re2, "" );
} else {
if ( !m[2] || m[1] == "." ) m[2] = "*";
//参数为div#example,那么会先找到div
//调用getElementsByTagName,获取所有div
for ( var i = 0; i < ret.length; i++ )
r = jQuery.merge( r,
m[2] == "*" ?
jQuery.getAll(ret[i]) :
ret[i].getElementsByTagName(m[2])
);
}
}
}

if ( t ) {//获取所有div后再通过调用jQuery静态filter函数
//过滤出example元素
var val = jQuery.filter(t,r);
ret = r = val.r;
t = jQuery.trim(val.t);
}
}

if ( ret && ret[0] == context ) ret.shift();
done = jQuery.merge( done, ret );

return done;
}

filter函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
filter: function(t,r,not) {
// Figure out if we're doing regular, or inverse, filtering
//grep查找满足过滤函数的数组元素。原始数组不受影响
var g = not !== false ? jQuery.grep :
function(a,f) {return jQuery.grep(a,f,true);};

while ( t && /^[a-z[({<*:.#]/i.test(t) ) {

var p = jQuery.parse;//解析数组存放 规则

for ( var i = 0; i < p.length; i++ ) {
var re = new RegExp( "^" + p[i][0]

// Look for a string-like sequence
.replace( 'S', "([a-z*_-][a-z0-9_-]*)" )

// Look for something (optionally) enclosed with quotes
.replace( 'Q', " *'?\"?([^'\"]*?)'?\"? *" ), "i" );
//i 在 RegExp级别下

var m = re.exec( t );

if ( m ) {//div#example 匹配[ "([:.#]*)S", 0 ]
// Re-organize the match
if ( p[i][1] )//检查parse数组 第二个元素是0还是1 0就跳过
m = ["", m[1], m[3], m[2], m[4]];

// Remove what we just matched
//div#exmaple 把div给替换掉了 接下来再遍历匹配#example
t = t.replace( re, "" );

break;
}
}

// :not() is a special case that can be optomized by
// keeping it out of the expression list
if ( m[1] == ":" && m[2] == "not" )
r = jQuery.filter(m[3],r,false).r;

// Otherwise, find the expression to execute
else {
var f = jQuery.expr[m[1]]; //寻找表达式数组匹配元素
// "": "m[2]== '*'||a.nodeName.toUpperCase()==m[2].toUpperCase()",
// "#": "a.getAttribute('id')&&a.getAttribute('id')==m[2]",
if ( f.constructor != String )
f = jQuery.expr[m[1]][m[2]];

// Build a custom macro to enclose it
//制造一个自定义的函数
eval("f = function(a,i){" +
( m[1] == "@" ? "z=jQuery.attr(a,m[3]);" : "" ) +
"return " + f + "}");

// Execute it against the current filter

//过滤 通过f 函数
r = g( r, f );
}
}

// Return an array of filtered elements (r)
// and the modified expression string (t)
return { r: r, t: t };
}
1…454647

John

232 日志
43 标签
GitHub Twitter
欢迎关注我的公众号:沉迷Spring
© 2023 johnwonder
由 Hexo 强力驱动 v3.2.0
|
主题 – NexT.Pisces v7.1.1
|
0%