Further progress towards tenant-specific routing

Ability to follow redirects from integration tests
Route publisher wraps handler with ShellRoute which controls request lifetime scope
ShellRoute at the moment is restricted to "Default" - host and prefix TBD
Route publisher remove-and-insert routes for a specific tenant to allow incremental and repeated shell route publish
Offers a more deterministic knowledge of which shell container is used than just-in-time http context
Removes Autofac default httpmodule, controller factory, httpapplication icontainerprovideraccessor, etc
Adds support for optional ~/Config/Sites.{yourtenantname}.config file for manual per-shell component override
Integration tests capture Orchard and NHibernate trace output. Levels are configurable in orchard.specs diagnostics.config
Concept of IRunningShellTable instroduced. Might not be needed - will be removed if so.

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-04-22 12:23:58 -07:00
parent eaf553b048
commit 79431a11a2
34 changed files with 863 additions and 265 deletions

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Diagnostics;
using System.Linq;
using System.Text; using System.Text;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
using Orchard.Environment.Topology; using Orchard.Environment.Topology;
@@ -46,6 +47,8 @@ namespace Orchard.Specs.Bindings {
descriptor.EnabledFeatures.Concat(new[] { new ShellFeature { Name = name } }), descriptor.EnabledFeatures.Concat(new[] { new ShellFeature { Name = name } }),
descriptor.Parameters); descriptor.Parameters);
} }
Trace.WriteLine("This call to Host.Reinitialize should not be needed, eventually");
MvcApplication.Host.Reinitialize_Obsolete(); MvcApplication.Host.Reinitialize_Obsolete();
}); });

View File

@@ -1,12 +1,18 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Remoting;
using System.Web; using System.Web;
using System.Web.Hosting; using System.Web.Hosting;
using System.Xml.Linq; using System.Xml.Linq;
using Castle.Core.Logging;
using HtmlAgilityPack; using HtmlAgilityPack;
using log4net.Appender;
using log4net.Core;
using log4net.Repository;
using Orchard.Specs.Hosting; using Orchard.Specs.Hosting;
using Orchard.Specs.Util; using Orchard.Specs.Util;
using TechTalk.SpecFlow; using TechTalk.SpecFlow;
@@ -34,12 +40,51 @@ namespace Orchard.Specs.Bindings {
public void GivenIHaveACleanSiteBasedOn(string siteFolder) { public void GivenIHaveACleanSiteBasedOn(string siteFolder) {
_webHost = new WebHost(); _webHost = new WebHost();
Host.Initialize(siteFolder, "/"); Host.Initialize(siteFolder, "/");
var cb = new cb1();
var sink = new MessageSink();
Host.Execute(() => { Host.Execute(() => {
HostingTraceListener.SetHook(msg => sink.Receive(msg)); log4net.Config.BasicConfigurator.Configure(new CallAppender(cb.cb2));
HostingTraceListener.SetHook(msg => cb.sink.Receive(" "+ msg));
}); });
_messages = sink; _messages = cb.sink;
}
public class CallAppender : IAppender {
private readonly cb2 _cb2;
public CallAppender(cb2 cb2) { _cb2 = cb2; }
public void Close() { }
public string Name { get; set; }
public void DoAppend(LoggingEvent loggingEvent) {
var traceLoggerFactory = new TraceLoggerFactory();
var logger = traceLoggerFactory.Create(loggingEvent.LoggerName);
if (loggingEvent.Level <= Level.Debug)
logger.Debug(loggingEvent.RenderedMessage);
else if (loggingEvent.Level <= Level.Info)
logger.Info(loggingEvent.RenderedMessage);
else if (loggingEvent.Level <= Level.Warn)
logger.Warn(loggingEvent.RenderedMessage);
else if (loggingEvent.Level <= Level.Error)
logger.Error(loggingEvent.RenderedMessage);
else
logger.Fatal(loggingEvent.RenderedMessage);
}
}
[Serializable]
public class cb1 {
public cb2 cb2 = new cb2();
public MessageSink sink = new MessageSink();
}
public class cb2 : MarshalByRefObject {
private IDictionary<string, TraceSource> _sources;
public void Trace(string loggerName, Level level, string message) {
var traceLoggerFactory = new TraceLoggerFactory();
traceLoggerFactory.Create(loggerName).Info(message);
//System.Diagnostics.Trace.WriteLine(message);
}
} }
[Given(@"I have module ""(.*)""")] [Given(@"I have module ""(.*)""")]
@@ -135,6 +180,16 @@ namespace Orchard.Specs.Bindings {
_doc.Load(new StringReader(_details.ResponseText)); _doc.Load(new StringReader(_details.ResponseText));
} }
[When(@"I am redirected")]
public void WhenIAmRedirected() {
var urlPath = "";
if (_details.ResponseHeaders.TryGetValue("Location", out urlPath)) {
WhenIGoTo(urlPath);
}
else {
Assert.Fail("No Location header returned");
}
}
[Then(@"the status should be (.*) (.*)")] [Then(@"the status should be (.*) (.*)")]
public void ThenTheStatusShouldBe(int statusCode, string statusDescription) { public void ThenTheStatusShouldBe(int statusCode, string statusDescription) {

View File

@@ -1,11 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
namespace Orchard.Specs.Hosting { namespace Orchard.Specs.Hosting {
public class MessageSink : MarshalByRefObject { public class MessageSink : MarshalByRefObject {
readonly IList<string> _messages = new List<string>(); readonly IList<string> _messages = new List<string>();
public void Receive(string message) { public void Receive(string message) {
Trace.WriteLine(message);
_messages.Add(message); _messages.Add(message);
} }
} }

View File

@@ -1,11 +1,34 @@
<system.diagnostics> <system.diagnostics>
<trace autoflush="true"/> <trace autoflush="true"/>
<sources> <sources>
<source name="Default" switchValue="Verbose"> <source name="Default" switchValue="Warning">
<listeners> <listeners>
<add name="CaptureTraceMessages" /> <add name="CaptureTraceMessages" />
</listeners> </listeners>
</source> </source>
<source name="Orchard" switchValue="Verbose">
<listeners>
<add name="CaptureTraceMessages" />
</listeners>
</source>
<source name="Orchard.Data.SessionLocator" switchValue="Information">
<listeners>
<add name="CaptureTraceMessages" />
</listeners>
</source>
<source name="NHibernate.SQL" switchValue="Verbose">
<listeners>
<add name="CaptureTraceMessages" />
</listeners>
</source>
<source name="Orchard.Mvc.ViewEngines.WebFormsViewEngineProvider" switchValue="Warning">
<listeners>
<add name="CaptureTraceMessages" />
</listeners>
</source>
<!--<source name="Orchard.Localization.Text" switchValue="Warning"/>
<source name="Orchard.Mvc.ViewEngines.WebFormsViewEngineProvider" switchValue="Warning"/>-->
</sources> </sources>
<sharedListeners> <sharedListeners>
<add name="CaptureTraceMessages" type="Orchard.Specs.Hosting.HostingTraceListener, Orchard.Specs" initializeData="" /> <add name="CaptureTraceMessages" type="Orchard.Specs.Hosting.HostingTraceListener, Orchard.Specs" initializeData="" />

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
</configSections>
<autofac defaultAssembly="Orchard.Framework">
<components>
<!--<component instance-scope="single-instance"
type="Orchard.Environment.Configuration.AzureBlobTenantManager"
service="Orchard.Environment.Configuration.IShellSettingsManager">
<parameters>
<parameter name="account" value="devstoreaccount1"/>
<parameter name="key" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
<parameter name="container" value="mycontainer"/>
</parameters>
</component>-->
</components>
</autofac>
</configuration>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration"/>
</configSections>
<autofac defaultAssembly="Orchard.Framework">
<components>
<component instance-scope="single-instance"
type="Orchard.Specs.Hosting.TraceEnabledSessionFactoryBuilder, Orchard.Specs"
service="Orchard.Data.Builders.ISessionFactoryBuilder, Orchard.Framework"/>
</components>
</autofac>
</configuration>

View File

@@ -111,6 +111,9 @@ namespace Orchard.Specs.Hosting {
if (index == HeaderSetCookie) { if (index == HeaderSetCookie) {
_details.ResponseHeaders.Add("Set-Cookie", value); _details.ResponseHeaders.Add("Set-Cookie", value);
} }
else if (index == HeaderLocation) {
_details.ResponseHeaders.Add("Location", value);
}
else { else {
_details.ResponseHeaders.Add("known header #" + index, value); _details.ResponseHeaders.Add("known header #" + index, value);
} }

View File

@@ -0,0 +1,5 @@
<%@ Page %>
<%
Response.Redirect("Page.aspx"); %>

View File

@@ -0,0 +1,24 @@
using FluentNHibernate.Cfg.Db;
using NHibernate;
using Orchard.Data.Builders;
namespace Orchard.Specs.Hosting {
public class TraceEnabledSessionFactoryBuilder : ISessionFactoryBuilder {
public TraceEnabledSessionFactoryBuilder() {
}
public ISessionFactory BuildSessionFactory(SessionFactoryParameters parameters) {
var builder = new TraceEnabledBuilder(parameters.DataFolder, parameters.ConnectionString);
return builder.BuildSessionFactory(parameters);
}
class TraceEnabledBuilder : SQLiteBuilder {
public TraceEnabledBuilder(string dataFolder, string connectionString) : base(dataFolder, connectionString) {
}
protected override IPersistenceConfigurer GetPersistenceConfigurer(bool createDatabase) {
var config = (SQLiteConfiguration)base.GetPersistenceConfigurer(createDatabase);
return config.ShowSql();
}
}
}
}

View File

@@ -8,6 +8,25 @@ Scenario: Default site is listed
And I have installed "Orchard.MultiTenancy" And I have installed "Orchard.MultiTenancy"
When I go to "Admin/MultiTenancy" When I go to "Admin/MultiTenancy"
Then I should see "List of Site's Tenants" Then I should see "List of Site's Tenants"
And I should see "Default" And I should see "<td>Default</td>"
And the status should be 200 OK And the status should be 200 OK
Scenario: New tenant fields are required
Given I have installed Orchard
And I have installed "Orchard.MultiTenancy"
When I go to "Admin/MultiTenancy/Add"
And I hit "Save"
Then I should see "is required"
Scenario: A new tenant is created
Given I have installed Orchard
And I have installed "Orchard.MultiTenancy"
When I go to "Admin/MultiTenancy/Add"
And I fill in
| name | value |
| Name | Scott |
| DataProvider | SQLite |
And I hit "Save"
And I am redirected
Then I should see "<td>Scott</td>"
And the status should be 200 OK

View File

@@ -2,7 +2,7 @@
// <auto-generated> // <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/). // This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.2.0.0 // SpecFlow Version:1.2.0.0
// Runtime Version:2.0.50727.4927 // Runtime Version:2.0.50727.4200
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@@ -66,9 +66,67 @@ this.ScenarioSetup(scenarioInfo);
#line 10 #line 10
testRunner.Then("I should see \"List of Site\'s Tenants\""); testRunner.Then("I should see \"List of Site\'s Tenants\"");
#line 11 #line 11
testRunner.And("I should see \"Default\""); testRunner.And("I should see \"<td>Default</td>\"");
#line 12 #line 12
testRunner.And("the status should be 200 OK"); testRunner.And("the status should be 200 OK");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("New tenant fields are required")]
public virtual void NewTenantFieldsAreRequired()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("New tenant fields are required", ((string[])(null)));
#line 14
this.ScenarioSetup(scenarioInfo);
#line 15
testRunner.Given("I have installed Orchard");
#line 16
testRunner.And("I have installed \"Orchard.MultiTenancy\"");
#line 17
testRunner.When("I go to \"Admin/MultiTenancy/Add\"");
#line 18
testRunner.And("I hit \"Save\"");
#line 19
testRunner.Then("I should see \"is required\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("A new tenant is created")]
public virtual void ANewTenantIsCreated()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("A new tenant is created", ((string[])(null)));
#line 21
this.ScenarioSetup(scenarioInfo);
#line 22
testRunner.Given("I have installed Orchard");
#line 23
testRunner.And("I have installed \"Orchard.MultiTenancy\"");
#line 24
testRunner.When("I go to \"Admin/MultiTenancy/Add\"");
#line hidden
TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table1.AddRow(new string[] {
"Name",
"Scott"});
table1.AddRow(new string[] {
"DataProvider",
"SQLite"});
#line 25
testRunner.And("I fill in", ((string)(null)), table1);
#line 29
testRunner.And("I hit \"Save\"");
#line 30
testRunner.And("I am redirected");
#line 31
testRunner.Then("I should see \"<td>Scott</td>\"");
#line 32
testRunner.And("the status should be 200 OK");
#line hidden #line hidden
testRunner.CollectScenarioErrors(); testRunner.CollectScenarioErrors();
} }

View File

@@ -43,6 +43,14 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\autofac\Autofac.Integration.Web.Mvc.dll</HintPath> <HintPath>..\..\lib\autofac\Autofac.Integration.Web.Mvc.dll</HintPath>
</Reference> </Reference>
<Reference Include="Castle.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\Castle Windsor 2.0\bin\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="FluentNHibernate, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8aa435e3cb308880, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\FluentNHibernate.dll</HintPath>
</Reference>
<Reference Include="FluentPath, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="FluentPath, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentpath\FluentPath.dll</HintPath> <HintPath>..\..\lib\fluentpath\FluentPath.dll</HintPath>
@@ -51,8 +59,16 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\htmlagilitypack\HtmlAgilityPack.dll</HintPath> <HintPath>..\..\lib\htmlagilitypack\HtmlAgilityPack.dll</HintPath>
</Reference> </Reference>
<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\log4net.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.TeamSystem.Data.UnitTesting, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <Reference Include="Microsoft.VisualStudio.TeamSystem.Data.UnitTesting, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="NHibernate, Version=2.1.2.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\fluentnhibernate\NHibernate.dll</HintPath>
</Reference>
<Reference Include="nunit.framework, Version=2.5.2.9222, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.5.2.9222, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\nunit\nunit.framework.dll</HintPath> <HintPath>..\..\lib\nunit\nunit.framework.dll</HintPath>
@@ -90,6 +106,7 @@
<Compile Include="Bindings\OrchardSiteFactory.cs" /> <Compile Include="Bindings\OrchardSiteFactory.cs" />
<Compile Include="Hosting\MessageSink.cs" /> <Compile Include="Hosting\MessageSink.cs" />
<Compile Include="Hosting\HostingTraceListener.cs" /> <Compile Include="Hosting\HostingTraceListener.cs" />
<Compile Include="Hosting\TraceEnabledSessionFactoryBuilder.cs" />
<Compile Include="Hosting\Simple.Web\HelloYetAgainHandler.cs" /> <Compile Include="Hosting\Simple.Web\HelloYetAgainHandler.cs" />
<Compile Include="Hosting\RequestExtensions.cs" /> <Compile Include="Hosting\RequestExtensions.cs" />
<Compile Include="Hosting\RequestDetails.cs" /> <Compile Include="Hosting\RequestDetails.cs" />
@@ -136,12 +153,18 @@
<Content Include="Hosting\Orchard.Web\Themes\Web.config"> <Content Include="Hosting\Orchard.Web\Themes\Web.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Hosting\Orchard.Web\Config\Sites.Default.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Hosting\Orchard.Web\Config\Diagnostics.config"> <None Include="Hosting\Orchard.Web\Config\Diagnostics.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<Content Include="Hosting\Simple.Web\Web.config"> <Content Include="Hosting\Simple.Web\Web.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Hosting\Orchard.Web\Config\Host.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="MultiTenancy.feature"> <None Include="MultiTenancy.feature">
<Generator>SpecFlowSingleFileGenerator</Generator> <Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>MultiTenancy.feature.cs</LastGenOutput> <LastGenOutput>MultiTenancy.feature.cs</LastGenOutput>
@@ -239,6 +262,9 @@
<Content Include="Hosting\Simple.Web\Simple\Cookie-Set.aspx"> <Content Include="Hosting\Simple.Web\Simple\Cookie-Set.aspx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Hosting\Simple.Web\Simple\Redir.aspx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Hosting\Simple.Web\Simple\Results.aspx"> <Content Include="Hosting\Simple.Web\Simple\Results.aspx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>

View File

@@ -2,7 +2,7 @@
// <auto-generated> // <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/). // This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.2.0.0 // SpecFlow Version:1.2.0.0
// Runtime Version:2.0.50727.4927 // Runtime Version:2.0.50727.4200
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.

View File

@@ -44,3 +44,9 @@ Scenario: Cookies follow along your request
When I go to "/simple/cookie-set.aspx" When I go to "/simple/cookie-set.aspx"
And I go to "/simple/cookie-show.aspx" And I go to "/simple/cookie-show.aspx"
Then I should see "foo:bar" Then I should see "foo:bar"
Scenario: Being redirected
Given I have a clean site based on Simple.Web
When I go to "/simple/redir.aspx"
And I am redirected
Then I should see "Hello again"

View File

@@ -2,7 +2,7 @@
// <auto-generated> // <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/). // This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.2.0.0 // SpecFlow Version:1.2.0.0
// Runtime Version:2.0.50727.4927 // Runtime Version:2.0.50727.4200
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@@ -175,6 +175,25 @@ this.ScenarioSetup(scenarioInfo);
testRunner.And("I go to \"/simple/cookie-show.aspx\""); testRunner.And("I go to \"/simple/cookie-show.aspx\"");
#line 46 #line 46
testRunner.Then("I should see \"foo:bar\""); testRunner.Then("I should see \"foo:bar\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("Being redirected")]
public virtual void BeingRedirected()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Being redirected", ((string[])(null)));
#line 48
this.ScenarioSetup(scenarioInfo);
#line 49
testRunner.Given("I have a clean site based on Simple.Web");
#line 50
testRunner.When("I go to \"/simple/redir.aspx\"");
#line 51
testRunner.And("I am redirected");
#line 52
testRunner.Then("I should see \"Hello again\"");
#line hidden #line hidden
testRunner.CollectScenarioErrors(); testRunner.CollectScenarioErrors();
} }

View File

@@ -1,109 +1,109 @@
using System; //using System;
using System.Linq; //using System.Linq;
using System.Threading; //using System.Threading;
using System.Web.Mvc; //using System.Web.Mvc;
using System.Web.Routing; //using System.Web.Routing;
using NUnit.Framework; //using NUnit.Framework;
using Orchard.Mvc.Routes; //using Orchard.Mvc.Routes;
using Orchard.Tests.Stubs; //using Orchard.Tests.Stubs;
namespace Orchard.Tests.Mvc { //namespace Orchard.Tests.Mvc {
[TestFixture] // [TestFixture]
public class RouteCollectionPublisherTests { // public class RouteCollectionPublisherTests {
static RouteDescriptor Desc(string name, string url) { // static RouteDescriptor Desc(string name, string url) {
return new RouteDescriptor {Name = name, Route = new Route(url, new MvcRouteHandler())}; // return new RouteDescriptor {Name = name, Route = new Route(url, new MvcRouteHandler())};
} // }
[Test] // //[Test]
public void PublisherShouldReplaceRoutes() { // //public void PublisherShouldReplaceRoutes() {
var routes = new RouteCollection(); // // var routes = new RouteCollection();
routes.MapRoute("foo", "{controller}"); // // routes.MapRoute("foo", "{controller}");
IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null)); // // IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null));
publisher.Publish(new[] {Desc("barname", "bar"), Desc("quuxname", "quux")}); // // publisher.Publish(new[] {Desc("barname", "bar"), Desc("quuxname", "quux")});
Assert.That(routes.Count(), Is.EqualTo(2)); // // Assert.That(routes.Count(), Is.EqualTo(2));
} // //}
[Test] // //[Test]
public void RoutesCanHaveNullOrEmptyNames() { // //public void RoutesCanHaveNullOrEmptyNames() {
var routes = new RouteCollection(); // // var routes = new RouteCollection();
routes.MapRoute("foo", "{controller}"); // // routes.MapRoute("foo", "{controller}");
IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null)); // // IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null));
publisher.Publish(new[] { Desc(null, "bar"), Desc(string.Empty, "quux") }); // // publisher.Publish(new[] { Desc(null, "bar"), Desc(string.Empty, "quux") });
Assert.That(routes.Count(), Is.EqualTo(2)); // // Assert.That(routes.Count(), Is.EqualTo(2));
} // //}
[Test] // //[Test]
[ExpectedException(typeof(ArgumentException))] // //[ExpectedException(typeof(ArgumentException))]
public void SameNameTwiceCausesExplosion() { // //public void SameNameTwiceCausesExplosion() {
var routes = new RouteCollection(); // // var routes = new RouteCollection();
routes.MapRoute("foo", "{controller}"); // // routes.MapRoute("foo", "{controller}");
IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null)); // // IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null));
publisher.Publish(new[] {Desc("yarg", "bar"), Desc("yarg", "quux")}); // // publisher.Publish(new[] {Desc("yarg", "bar"), Desc("yarg", "quux")});
Assert.That(routes.Count(), Is.EqualTo(2)); // // Assert.That(routes.Count(), Is.EqualTo(2));
} // //}
[Test] // [Test]
public void ExplosionLeavesOriginalRoutesIntact() { // public void ExplosionLeavesOriginalRoutesIntact() {
var routes = new RouteCollection(); // var routes = new RouteCollection();
routes.MapRoute("foo", "{controller}"); // routes.MapRoute("foo", "{controller}");
IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null)); // IRoutePublisher publisher = new RoutePublisher(routes, new StubContainerProvider(null, null));
try { // try {
publisher.Publish(new[] { Desc("yarg", "bar"), Desc("yarg", "quux") }); // publisher.Publish(new[] { Desc("yarg", "bar"), Desc("yarg", "quux") });
} // }
catch (ArgumentException) { // catch (ArgumentException) {
Assert.That(routes.Count(), Is.EqualTo(1)); // Assert.That(routes.Count(), Is.EqualTo(1));
Assert.That(routes.OfType<Route>().Single().Url, Is.EqualTo("{controller}")); // Assert.That(routes.OfType<Route>().Single().Url, Is.EqualTo("{controller}"));
} // }
} // }
[Test] // [Test]
public void RoutesArePaintedWithConainerProviderAsTheyAreApplied() { // public void RoutesArePaintedWithConainerProviderAsTheyAreApplied() {
var routes = new RouteCollection(); // var routes = new RouteCollection();
routes.MapRoute("foo", "{controller}"); // routes.MapRoute("foo", "{controller}");
var containerProvider = new StubContainerProvider(null, null); // var containerProvider = new StubContainerProvider(null, null);
IRoutePublisher publisher = new RoutePublisher(routes, containerProvider); // IRoutePublisher publisher = new RoutePublisher(routes, containerProvider);
publisher.Publish(new[] { Desc("barname", "bar"), Desc("quuxname", "quux") }); // publisher.Publish(new[] { Desc("barname", "bar"), Desc("quuxname", "quux") });
Assert.That(routes.OfType<Route>().Count(), Is.EqualTo(2)); // Assert.That(routes.OfType<Route>().Count(), Is.EqualTo(2));
Assert.That(routes.OfType<Route>().SelectMany(r => r.DataTokens.Values).Count(), Is.EqualTo(2)); // Assert.That(routes.OfType<Route>().SelectMany(r => r.DataTokens.Values).Count(), Is.EqualTo(2));
Assert.That(routes.OfType<Route>().SelectMany(r => r.DataTokens.Values), Has.All.SameAs(containerProvider)); // Assert.That(routes.OfType<Route>().SelectMany(r => r.DataTokens.Values), Has.All.SameAs(containerProvider));
} // }
[Test] // [Test]
public void WriteBlocksWhileReadIsInEffect() { // public void WriteBlocksWhileReadIsInEffect() {
var routes = new RouteCollection(); // var routes = new RouteCollection();
routes.MapRoute("foo", "{controller}"); // routes.MapRoute("foo", "{controller}");
var containerProvider = new StubContainerProvider(null, null); // var containerProvider = new StubContainerProvider(null, null);
IRoutePublisher publisher = new RoutePublisher(routes, containerProvider); // IRoutePublisher publisher = new RoutePublisher(routes, containerProvider);
var readLock = routes.GetReadLock(); // var readLock = routes.GetReadLock();
string where = "init"; // string where = "init";
var action = new Action(() => { // var action = new Action(() => {
where = "before"; // where = "before";
publisher.Publish(new[] { Desc("barname", "bar"), Desc("quuxname", "quux") }); // publisher.Publish(new[] { Desc("barname", "bar"), Desc("quuxname", "quux") });
where = "after"; // where = "after";
}); // });
Assert.That(where, Is.EqualTo("init")); // Assert.That(where, Is.EqualTo("init"));
var async = action.BeginInvoke(null, null); // var async = action.BeginInvoke(null, null);
Thread.Sleep(75); // Thread.Sleep(75);
Assert.That(where, Is.EqualTo("before")); // Assert.That(where, Is.EqualTo("before"));
readLock.Dispose(); // readLock.Dispose();
Thread.Sleep(75); // Thread.Sleep(75);
Assert.That(where, Is.EqualTo("after")); // Assert.That(where, Is.EqualTo("after"));
action.EndInvoke(async); // action.EndInvoke(async);
} // }
} // }
} //}

View File

@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Autofac;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.Mvc.Routes;
using Orchard.Tests.Stubs;
namespace Orchard.Tests.Mvc.Routes {
[TestFixture]
public class ShellRouteTests {
private RouteCollection _routes;
private ILifetimeScope _containerA;
private ILifetimeScope _containerB;
private ShellSettings _settingsA;
private ShellSettings _settingsB;
private IContainer _rootContainer;
[Test]
public void FactoryMethodWillCreateShellRoutes() {
var settings = new ShellSettings { Name = "Alpha" };
var builder = new ContainerBuilder();
builder.RegisterType<ShellRoute>().InstancePerDependency();
builder.Register(ctx => settings);
var container = builder.Build();
var buildShellRoute = container.Resolve<Func<RouteBase, ShellRoute>>();
var routeA = new Route("foo", new MvcRouteHandler());
var route1 = buildShellRoute(routeA);
var routeB = new Route("bar", new MvcRouteHandler()) {
DataTokens = new RouteValueDictionary { { "area", "Beta" } }
};
var route2 = buildShellRoute(routeB);
Assert.That(route1, Is.Not.SameAs(route2));
Assert.That(route1.ShellSettingsName, Is.EqualTo("Alpha"));
Assert.That(route1.Area, Is.Null);
Assert.That(route2.ShellSettingsName, Is.EqualTo("Alpha"));
Assert.That(route2.Area, Is.EqualTo("Beta"));
}
private void Init() {
_settingsA = new ShellSettings {Name = "Alpha"};
_settingsB = new ShellSettings {Name = "Beta"};
_routes = new RouteCollection();
var rootBuilder = new ContainerBuilder();
rootBuilder.Register(ctx => _routes);
rootBuilder.RegisterType<ShellRoute>().InstancePerDependency();
rootBuilder.RegisterType<RunningShellTable>().As<IRunningShellTable>().SingleInstance();
_rootContainer = rootBuilder.Build();
_containerA = _rootContainer.BeginLifetimeScope(
builder => {
builder.Register(ctx => _settingsA);
builder.RegisterType<RoutePublisher>().As<IRoutePublisher>();
});
_containerB = _rootContainer.BeginLifetimeScope(
builder => {
builder.Register(ctx => _settingsB);
builder.RegisterType<RoutePublisher>().As<IRoutePublisher>();
});
}
[Test]
public void RoutePublisherReplacesOnlyNamedShellsRoutes() {
Init();
var routeA = new Route("foo", new MvcRouteHandler());
var routeB = new Route("bar", new MvcRouteHandler());
var routeC = new Route("quux", new MvcRouteHandler());
_containerA.Resolve<IRoutePublisher>().Publish(
new[] {new RouteDescriptor {Priority = 0, Route = routeA}});
_containerB.Resolve<IRoutePublisher>().Publish(
new[] {new RouteDescriptor {Priority = 0, Route = routeB}});
Assert.That(_routes.Count(), Is.EqualTo(2));
_containerA.Resolve<IRoutePublisher>().Publish(
new[] {new RouteDescriptor {Priority = 0, Route = routeC}});
Assert.That(_routes.Count(), Is.EqualTo(2));
_containerB.Resolve<IRoutePublisher>().Publish(
new[] {
new RouteDescriptor {Priority = 0, Route = routeA},
new RouteDescriptor {Priority = 0, Route = routeB},
});
Assert.That(_routes.Count(), Is.EqualTo(3));
}
[Test]
public void MatchingRouteToActiveShellTableWillLimitTheAbilityToMatchRoutes() {
Init();
var routeA = new Route("foo", new MvcRouteHandler());
var routeB = new Route("bar", new MvcRouteHandler());
var routeC = new Route("quux", new MvcRouteHandler());
_containerA.Resolve<IRoutePublisher>().Publish(
new[] {new RouteDescriptor {Priority = 0, Route = routeA}});
_containerB.Resolve<IRoutePublisher>().Publish(
new[] {new RouteDescriptor {Priority = 0, Route = routeB}});
var httpContext = new StubHttpContext("~/foo");
var routeData = _routes.GetRouteData(httpContext);
Assert.That(routeData, Is.Null);
}
}
}

View File

@@ -167,6 +167,7 @@
<Compile Include="Environment\OrchardStarterTests.cs" /> <Compile Include="Environment\OrchardStarterTests.cs" />
<Compile Include="Environment\ShellBuilders\DefaultShellContainerFactoryTests.cs" /> <Compile Include="Environment\ShellBuilders\DefaultShellContainerFactoryTests.cs" />
<Compile Include="Environment\ShellBuilders\DefaultShellContextFactoryTests.cs" /> <Compile Include="Environment\ShellBuilders\DefaultShellContextFactoryTests.cs" />
<Compile Include="Mvc\Routes\ShellRouteTests.cs" />
<Compile Include="Utility\ContainerExtensions.cs" /> <Compile Include="Utility\ContainerExtensions.cs" />
<Compile Include="Environment\TestDependencies\TestDependency.cs" /> <Compile Include="Environment\TestDependencies\TestDependency.cs" />
<Compile Include="Environment\Topology\DefaultShellDescriptorCacheTests.cs" /> <Compile Include="Environment\Topology\DefaultShellDescriptorCacheTests.cs" />

View File

@@ -15,9 +15,8 @@ namespace Orchard.Web {
// Note: For instructions on enabling IIS6 or IIS7 classic mode, // Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801 // visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : HttpApplication, IContainerProviderAccessor { public class MvcApplication : HttpApplication {
private static IOrchardHost _host; private static IOrchardHost _host;
private static IContainerProvider _containerProvider;
public static void RegisterRoutes(RouteCollection routes) { public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
@@ -31,11 +30,6 @@ namespace Orchard.Web {
new Version("2.0.50129.0")/*MVC2 RC2 file version #*/, new Version("2.0.50129.0")/*MVC2 RC2 file version #*/,
new Version("2.0.41211.0")/*MVC2 RC file version #*/); new Version("2.0.41211.0")/*MVC2 RC file version #*/);
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
_containerProvider = new ContainerProvider(builder.Build());
ControllerBuilder.Current.SetControllerFactory(new AutofacControllerFactory(ContainerProvider));
RegisterRoutes(RouteTable.Routes); RegisterRoutes(RouteTable.Routes);
_host = OrchardStarter.CreateHost(MvcSingletons); _host = OrchardStarter.CreateHost(MvcSingletons);
@@ -108,14 +102,5 @@ namespace Orchard.Web {
builder.RegisterInstance(ViewEngines.Engines); builder.RegisterInstance(ViewEngines.Engines);
} }
#region Implementation of IContainerProviderAccessor
public IContainerProvider ContainerProvider {
get {
return _containerProvider;
}
}
#endregion
} }
} }

View File

@@ -33,10 +33,8 @@ namespace Orchard.MultiTenancy.Controllers {
} }
[HttpPost, ActionName("Add")] [HttpPost, ActionName("Add")]
public ActionResult AddPOST() { public ActionResult AddPOST(TenantsAddViewModel viewModel) {
var viewModel = new TenantsAddViewModel();
try { try {
UpdateModel(viewModel);
if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Couldn't create tenant"))) if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Couldn't create tenant")))
return new HttpUnauthorizedResult(); return new HttpUnauthorizedResult();
_tenantService.CreateTenant( _tenantService.CreateTenant(

View File

@@ -144,7 +144,6 @@
</httpHandlers> </httpHandlers>
<httpModules> <httpModules>
<add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"/>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</httpModules> </httpModules>
@@ -178,7 +177,6 @@
<validation validateIntegratedModeConfiguration="false"/> <validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"> <modules runAllManagedModulesForAllRequests="true">
<add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"/>
<remove name="ScriptModule" /> <remove name="ScriptModule" />
<remove name="UrlRoutingModule" /> <remove name="UrlRoutingModule" />
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using Autofac;
using Autofac.Core;
namespace Orchard.Environment.AutofacUtil {
public class LifetimeScopeContainer : IContainer {
private readonly ILifetimeScope _lifetimeScope;
public LifetimeScopeContainer(ILifetimeScope lifetimeScope) {
_lifetimeScope = lifetimeScope;
}
public object Resolve(IComponentRegistration registration, IEnumerable<Parameter> parameters) {
return _lifetimeScope.Resolve(registration, parameters);
}
public IComponentRegistry ComponentRegistry {
get { return _lifetimeScope.ComponentRegistry; }
}
public void Dispose() {
}
public ILifetimeScope BeginLifetimeScope() {
return _lifetimeScope.BeginLifetimeScope();
}
public ILifetimeScope BeginLifetimeScope(object tag) {
return _lifetimeScope.BeginLifetimeScope(tag);
}
public ILifetimeScope BeginLifetimeScope(Action<ContainerBuilder> configurationAction) {
return _lifetimeScope.BeginLifetimeScope(configurationAction);
}
public ILifetimeScope BeginLifetimeScope(object tag, Action<ContainerBuilder> configurationAction) {
return _lifetimeScope.BeginLifetimeScope(tag, configurationAction);
}
public IDisposer Disposer {
get { return _lifetimeScope.Disposer; }
}
public object Tag {
get { return _lifetimeScope.Tag; }
}
}
}

View File

@@ -13,22 +13,23 @@ using Orchard.Utility.Extensions;
namespace Orchard.Environment { namespace Orchard.Environment {
public class DefaultOrchardHost : IOrchardHost { public class DefaultOrchardHost : IOrchardHost {
//private readonly IContainerProvider _containerProvider;
private readonly ControllerBuilder _controllerBuilder; private readonly ControllerBuilder _controllerBuilder;
private readonly IShellSettingsManager _shellSettingsManager; private readonly IShellSettingsManager _shellSettingsManager;
private readonly IShellContextFactory _shellContextFactory; private readonly IShellContextFactory _shellContextFactory;
private readonly IRunningShellTable _runningShellTable;
private IEnumerable<ShellContext> _current; private IEnumerable<ShellContext> _current;
public DefaultOrchardHost( public DefaultOrchardHost(
//IContainerProvider containerProvider,
IShellSettingsManager shellSettingsManager, IShellSettingsManager shellSettingsManager,
IShellContextFactory shellContextFactory, IShellContextFactory shellContextFactory,
IRunningShellTable runningShellTable,
ControllerBuilder controllerBuilder) { ControllerBuilder controllerBuilder) {
//_containerProvider = containerProvider; //_containerProvider = containerProvider;
_shellSettingsManager = shellSettingsManager; _shellSettingsManager = shellSettingsManager;
_shellContextFactory = shellContextFactory; _shellContextFactory = shellContextFactory;
_runningShellTable = runningShellTable;
_controllerBuilder = controllerBuilder; _controllerBuilder = controllerBuilder;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
@@ -54,10 +55,12 @@ namespace Orchard.Environment {
void IOrchardHost.BeginRequest() { void IOrchardHost.BeginRequest() {
Logger.Debug("BeginRequest");
BeginRequest(); BeginRequest();
} }
void IOrchardHost.EndRequest() { void IOrchardHost.EndRequest() {
Logger.Debug("EndRequest");
EndRequest(); EndRequest();
} }
@@ -81,12 +84,14 @@ namespace Orchard.Environment {
settings => { settings => {
var context = CreateShellContext(settings); var context = CreateShellContext(settings);
context.Shell.Activate(); context.Shell.Activate();
_runningShellTable.Add(settings);
return context; return context;
}); });
} }
var setupContext = CreateSetupContext(); var setupContext = CreateSetupContext();
setupContext.Shell.Activate(); setupContext.Shell.Activate();
_runningShellTable.Add(setupContext.Settings);
return new[] { setupContext }; return new[] { setupContext };
} }
@@ -105,7 +110,6 @@ namespace Orchard.Environment {
} }
protected virtual void EndRequest() { protected virtual void EndRequest() {
//_containerProvider.EndRequestLifetime();
} }

View File

@@ -123,7 +123,7 @@ namespace Orchard.Environment.Extensions {
var featureName = featureDescriptor.Name; var featureName = featureDescriptor.Name;
string extensionName = GetExtensionForFeature(featureName); string extensionName = GetExtensionForFeature(featureName);
if (extensionName == null) throw new ArgumentException(T("Feature ") + featureName + T(" was not found in any of the installed extensions")); if (extensionName == null) throw new ArgumentException(T("Feature ") + featureName + T(" was not found in any of the installed extensions"));
var extension = ActiveExtensions_Obsolete().Where(x => x.Descriptor.Name == extensionName).FirstOrDefault(); var extension = BuildActiveExtensions().Where(x => x.Descriptor.Name == extensionName).FirstOrDefault();
if (extension == null) throw new InvalidOperationException(T("Extension ") + extensionName + T(" is not active")); if (extension == null) throw new InvalidOperationException(T("Extension ") + extensionName + T(" is not active"));
var extensionTypes = extension.ExportedTypes.Where(t => t.IsClass && !t.IsAbstract); var extensionTypes = extension.ExportedTypes.Where(t => t.IsClass && !t.IsAbstract);

View File

@@ -1,17 +1,13 @@
using Autofac; using Autofac;
using Autofac.Integration.Web; using Autofac.Integration.Web;
using Orchard.Environment.AutofacUtil;
namespace Orchard.Environment { namespace Orchard.Environment {
class FiniteContainerProvider : IContainerProvider { class FiniteContainerProvider : IContainerProvider {
public FiniteContainerProvider(ILifetimeScope applicationContainer) { public FiniteContainerProvider(ILifetimeScope applicationContainer) {
//ApplicationContainer = applicationContainer; ApplicationContainer = new LifetimeScopeContainer(applicationContainer);
RequestLifetime = ApplicationContainer.BeginLifetimeScope("httpRequest");
RequestLifetime = applicationContainer.BeginLifetimeScope(
builder=> {
// also inject this instance in case anyone asks for the container provider
builder.Register(ctx => this).As<IContainerProvider>();
});
} }
public void EndRequestLifetime() { public void EndRequestLifetime() {

View File

@@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Configuration; using System.Configuration;
using System.IO; using System.IO;
using System.Web.Hosting; using System.Web.Hosting;
using Autofac; using Autofac;
using Autofac.Configuration; using Autofac.Configuration;
using Autofac.Core;
using Autofac.Integration.Web; using Autofac.Integration.Web;
using Orchard.Environment.AutofacUtil; using Orchard.Environment.AutofacUtil;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
@@ -63,12 +61,13 @@ namespace Orchard.Environment {
} }
} }
builder.RegisterType<RunningShellTable>().As<IRunningShellTable>().SingleInstance();
builder.RegisterType<DefaultOrchardShell>().As<IOrchardShell>().InstancePerMatchingLifetimeScope("shell"); builder.RegisterType<DefaultOrchardShell>().As<IOrchardShell>().InstancePerMatchingLifetimeScope("shell");
// The container provider gives you access to the lowest container at the time, // The container provider gives you access to the lowest container at the time,
// and dynamically creates a per-request container. The EndRequestLifetime method // and dynamically creates a per-request container. The EndRequestLifetime method
// still needs to be called on end request, but that's the host component's job to worry about // still needs to be called on end request, but that's the host component's job to worry about
builder.RegisterType<ContainerProvider>().As<IContainerProvider>().InstancePerLifetimeScope(); //builder.RegisterType<ContainerProvider>().As<IContainerProvider>().InstancePerLifetimeScope();
@@ -91,49 +90,6 @@ namespace Orchard.Environment {
return builder.Build(); return builder.Build();
} }
public class LifetimeScopeContainer : IContainer {
private readonly ILifetimeScope _lifetimeScope;
public LifetimeScopeContainer(ILifetimeScope lifetimeScope) {
_lifetimeScope = lifetimeScope;
}
public object Resolve(IComponentRegistration registration, IEnumerable<Parameter> parameters) {
return _lifetimeScope.Resolve(registration, parameters);
}
public IComponentRegistry ComponentRegistry {
get { return _lifetimeScope.ComponentRegistry; }
}
public void Dispose() {
}
public ILifetimeScope BeginLifetimeScope() {
return _lifetimeScope.BeginLifetimeScope();
}
public ILifetimeScope BeginLifetimeScope(object tag) {
return _lifetimeScope.BeginLifetimeScope(tag);
}
public ILifetimeScope BeginLifetimeScope(Action<ContainerBuilder> configurationAction) {
return _lifetimeScope.BeginLifetimeScope(configurationAction);
}
public ILifetimeScope BeginLifetimeScope(object tag, Action<ContainerBuilder> configurationAction) {
return _lifetimeScope.BeginLifetimeScope(tag, configurationAction);
}
public IDisposer Disposer {
get { return _lifetimeScope.Disposer; }
}
public object Tag {
get { return _lifetimeScope.Tag; }
}
}
public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) { public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) {
var container = CreateHostContainer(registrations); var container = CreateHostContainer(registrations);
return container.Resolve<IOrchardHost>(); return container.Resolve<IOrchardHost>();

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Orchard.Environment.Configuration;
namespace Orchard.Environment {
public interface IRunningShellTable {
void Add(ShellSettings settings);
IEnumerable<ShellSettings> List();
ShellSettings Match(HttpContextBase httpContext);
}
public class RunningShellTable : IRunningShellTable {
private IEnumerable<ShellSettings> _shells = Enumerable.Empty<ShellSettings>();
public void Add(ShellSettings settings) {
_shells = _shells
.Where(s=>s.Name != settings.Name)
.Concat(new[] { settings })
.ToArray();
}
public IEnumerable<ShellSettings> List() {
return _shells;
}
public ShellSettings Match(HttpContextBase httpContext) {
return _shells.SingleOrDefault(settings => settings.Name == "Default");
}
}
}

View File

@@ -1,8 +1,11 @@
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Web.Hosting;
using System.Web.Mvc; using System.Web.Mvc;
using Autofac; using Autofac;
using Autofac.Builder; using Autofac.Builder;
using Autofac.Configuration;
using Autofac.Core; using Autofac.Core;
using Autofac.Features.Indexed; using Autofac.Features.Indexed;
using Autofac.Integration.Web.Mvc; using Autofac.Integration.Web.Mvc;
@@ -83,6 +86,10 @@ namespace Orchard.Environment.ShellBuilders {
.InstancePerDependency() .InstancePerDependency()
.InjectActionInvoker(); .InjectActionInvoker();
} }
var optionalHostConfig = HostingEnvironment.MapPath("~/Config/Sites." + settings.Name + ".config");
if (File.Exists(optionalHostConfig))
builder.RegisterModule(new ConfigurationSettingsReader(ConfigurationSettingsReader.DefaultSectionName, optionalHostConfig));
}); });
} }

View File

@@ -90,7 +90,7 @@ namespace Orchard.Environment.ShellBuilders {
public ShellContext CreateSetupContext() { public ShellContext CreateSetupContext() {
Logger.Warning("No shell settings available. Creating shell context for setup"); Logger.Warning("No shell settings available. Creating shell context for setup");
var settings = new ShellSettings { Name = "__Orchard__Setup__" }; var settings = new ShellSettings { Name = "Default" };
var descriptor = new ShellDescriptor { var descriptor = new ShellDescriptor {
SerialNumber = -1, SerialNumber = -1,

View File

@@ -4,12 +4,14 @@ using System.Web.Mvc;
using System.Web.Routing; using System.Web.Routing;
using Autofac; using Autofac;
using Orchard.Mvc.Filters; using Orchard.Mvc.Filters;
using Orchard.Mvc.Routes;
namespace Orchard.Mvc { namespace Orchard.Mvc {
public class MvcModule : Module { public class MvcModule : Module {
protected override void Load(ContainerBuilder moduleBuilder) { protected override void Load(ContainerBuilder moduleBuilder) {
moduleBuilder.RegisterType<FilterResolvingActionInvoker>().As<IActionInvoker>().InstancePerDependency(); moduleBuilder.RegisterType<FilterResolvingActionInvoker>().As<IActionInvoker>().InstancePerDependency();
moduleBuilder.RegisterType<ShellRoute>().InstancePerDependency();
moduleBuilder.Register(ctx => HttpContextBaseFactory(ctx)).As<HttpContextBase>().InstancePerDependency(); moduleBuilder.Register(ctx => HttpContextBaseFactory(ctx)).As<HttpContextBase>().InstancePerDependency();
moduleBuilder.Register(ctx => RequestContextFactory(ctx)).As<RequestContext>().InstancePerDependency(); moduleBuilder.Register(ctx => RequestContextFactory(ctx)).As<RequestContext>().InstancePerDependency();

View File

@@ -1,16 +1,22 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Web.Routing; using System.Web.Routing;
using Autofac.Integration.Web; using Orchard.Environment.Configuration;
namespace Orchard.Mvc.Routes { namespace Orchard.Mvc.Routes {
public class RoutePublisher : IRoutePublisher { public class RoutePublisher : IRoutePublisher {
private readonly RouteCollection _routeCollection; private readonly RouteCollection _routeCollection;
private readonly IContainerProvider _containerProvider; private readonly ShellSettings _shellSettings;
private readonly Func<RouteBase, ShellRoute> _shellRouteFactory;
public RoutePublisher(RouteCollection routeCollection, IContainerProvider containerProvider) { public RoutePublisher(
RouteCollection routeCollection,
ShellSettings shellSettings,
Func<RouteBase, ShellRoute> shellRouteFactory) {
_routeCollection = routeCollection; _routeCollection = routeCollection;
_containerProvider = containerProvider; _shellSettings = shellSettings;
_shellRouteFactory = shellRouteFactory;
} }
public void Publish(IEnumerable<RouteDescriptor> routes) { public void Publish(IEnumerable<RouteDescriptor> routes) {
@@ -24,18 +30,19 @@ namespace Orchard.Mvc.Routes {
using (_routeCollection.GetWriteLock()) { using (_routeCollection.GetWriteLock()) {
// existing routes are removed while the collection is briefly inaccessable // existing routes are removed while the collection is briefly inaccessable
_routeCollection.Clear(); var cropArray = _routeCollection
.OfType<ShellRoute>()
.Where(sr => sr.ShellSettingsName == _shellSettings.Name)
.ToArray();
foreach(var crop in cropArray) {
_routeCollection.Remove(crop);
}
// new routes are added // new routes are added
foreach (var route in routesArray) { foreach (var routeDescriptor in routesArray) {
_routeCollection.Add(route.Name, route.Route); //_routeCollection.Add(route.Name, _shellRouteFactory(_shellSettings.Name, route.Route));
} _routeCollection.Add(routeDescriptor.Name, _shellRouteFactory(routeDescriptor.Route));
// and painted with the IContainerProvider if it's type has data tokens
foreach (var route in routesArray.Select(d => d.Route).OfType<Route>()) {
if (route.DataTokens == null)
route.DataTokens = new RouteValueDictionary();
route.DataTokens["IContainerProvider"] = _containerProvider;
} }
} }
} }

View File

@@ -0,0 +1,146 @@
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Autofac;
using Autofac.Integration.Web;
using Orchard.Environment;
using Orchard.Environment.AutofacUtil;
using Orchard.Environment.Configuration;
namespace Orchard.Mvc.Routes {
public class ShellRoute : RouteBase, IRouteWithArea {
private readonly RouteBase _route;
private readonly ShellSettings _shellSettings;
private readonly IContainer _container;
private readonly IRunningShellTable _runningShellTable;
public ShellRoute(RouteBase route, ShellSettings shellSettings, ILifetimeScope shellLifetimeScope, IRunningShellTable runningShellTable) {
_route = route;
_shellSettings = shellSettings;
_runningShellTable = runningShellTable;
_container = new LifetimeScopeContainer(shellLifetimeScope);
var routeWithArea = route as IRouteWithArea;
if (routeWithArea != null) {
Area = routeWithArea.Area;
}
var routeWithDataTokens = route as Route;
if ((routeWithDataTokens != null) && (routeWithDataTokens.DataTokens != null)) {
Area = (routeWithDataTokens.DataTokens["area"] as string);
}
}
public string ShellSettingsName { get { return _shellSettings.Name; } }
public string Area { get; private set; }
public override RouteData GetRouteData(HttpContextBase httpContext) {
// locate appropriate shell settings for request
var settings = _runningShellTable.Match(httpContext);
// only proceed if there was a match, and it was for this client
if (settings == null || settings.Name != _shellSettings.Name)
return null;
// route didn't match anyway
var routeData = _route.GetRouteData(httpContext);
if (routeData == null)
return null;
// otherwise paint wrap handler and return it
routeData.RouteHandler = new RouteHandler(_container, routeData.RouteHandler);
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
// todo - ignore conditionally
var virtualPath = _route.GetVirtualPath(requestContext, values);
if (virtualPath == null)
return null;
return virtualPath;
}
class RouteHandler : IRouteHandler {
private readonly IContainer _container;
private readonly IRouteHandler _routeHandler;
public RouteHandler(IContainer container, IRouteHandler routeHandler) {
_container = container;
_routeHandler = routeHandler;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
var httpHandler = _routeHandler.GetHttpHandler(requestContext);
return new HttpAsyncHandler(
_container,
requestContext,
(IHttpAsyncHandler)httpHandler);
}
}
class HttpAsyncHandler : IHttpAsyncHandler, IRequiresSessionState, IContainerProvider {
private readonly RequestContext _requestContext;
private readonly IHttpAsyncHandler _httpAsyncHandler;
public HttpAsyncHandler(IContainer applicationContainer, RequestContext requestContext, IHttpAsyncHandler httpAsyncHandler) {
ApplicationContainer = applicationContainer;
_requestContext = requestContext;
_httpAsyncHandler = httpAsyncHandler;
}
public bool IsReusable {
get { return false; }
}
public void ProcessRequest(HttpContext context) {
BeginRequestLifetime();
try {
_httpAsyncHandler.ProcessRequest(context);
}
finally {
EndRequestLifetime();
}
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {
BeginRequestLifetime();
try {
return _httpAsyncHandler.BeginProcessRequest(context, cb, extraData);
}
catch {
EndRequestLifetime();
throw;
}
}
public void EndProcessRequest(IAsyncResult result) {
try {
_httpAsyncHandler.EndProcessRequest(result);
}
finally {
EndRequestLifetime();
}
}
public void BeginRequestLifetime() {
RequestLifetime = ApplicationContainer.BeginLifetimeScope("httpRequest");
_requestContext.RouteData.DataTokens["IContainerProvider"] = this;
}
public void EndRequestLifetime() {
_requestContext.RouteData.DataTokens.Remove("IContainerProvider");
RequestLifetime.Dispose();
}
public IContainer ApplicationContainer { get; set; }
public ILifetimeScope RequestLifetime { get; set; }
}
}
}

View File

@@ -161,6 +161,8 @@
<Compile Include="Data\Builders\SqlServerBuilder.cs" /> <Compile Include="Data\Builders\SqlServerBuilder.cs" />
<Compile Include="Data\Conventions\StringLengthConvention.cs" /> <Compile Include="Data\Conventions\StringLengthConvention.cs" />
<Compile Include="Data\SessionFactoryHolder.cs" /> <Compile Include="Data\SessionFactoryHolder.cs" />
<Compile Include="Environment\AutofacUtil\LifetimeScopeContainer.cs" />
<Compile Include="Environment\RunningShellTable.cs" />
<Compile Include="Environment\AutofacUtil\DynamicProxy2\DynamicProxyContext.cs" /> <Compile Include="Environment\AutofacUtil\DynamicProxy2\DynamicProxyContext.cs" />
<Compile Include="Environment\AutofacUtil\DynamicProxy2\DynamicProxyExtensions.cs" /> <Compile Include="Environment\AutofacUtil\DynamicProxy2\DynamicProxyExtensions.cs" />
<Compile Include="Environment\AutofacUtil\DynamicProxy2\ConstructorFinderWrapper.cs" /> <Compile Include="Environment\AutofacUtil\DynamicProxy2\ConstructorFinderWrapper.cs" />
@@ -191,6 +193,7 @@
<Compile Include="Mvc\Extensions\ControllerExtensions.cs" /> <Compile Include="Mvc\Extensions\ControllerExtensions.cs" />
<Compile Include="Mvc\Html\FileRegistrationContextExtensions.cs" /> <Compile Include="Mvc\Html\FileRegistrationContextExtensions.cs" />
<Compile Include="Mvc\Extensions\UrlHelperExtensions.cs" /> <Compile Include="Mvc\Extensions\UrlHelperExtensions.cs" />
<Compile Include="Mvc\Routes\ShellRoute.cs" />
<Compile Include="Mvc\ViewModels\AdaptedViewModel.cs" /> <Compile Include="Mvc\ViewModels\AdaptedViewModel.cs" />
<Compile Include="Mvc\ViewUserControl.cs"> <Compile Include="Mvc\ViewUserControl.cs">
<SubType>ASPXCodeBehind</SubType> <SubType>ASPXCodeBehind</SubType>