mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-18 17:47:54 +08:00
Providing a query builder off of the IContentManager interface.
--HG-- extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4042269
This commit is contained in:
@@ -13,10 +13,11 @@ using Orchard.Data;
|
||||
namespace Orchard.Tests {
|
||||
public static class DataUtility {
|
||||
public static ISessionFactory CreateSessionFactory(string fileName, params Type[] types) {
|
||||
|
||||
var persistenceModel = AutoMap.Source(new Types(types))
|
||||
.Alterations(alt => AddAlterations(alt, types))
|
||||
.Conventions.AddFromAssemblyOf<DataModule>();
|
||||
|
||||
//var persistenceModel = AutoMap.Source(new Types(types))
|
||||
// .Alterations(alt => AddAlterations(alt, types))
|
||||
// .Conventions.AddFromAssemblyOf<DataModule>();
|
||||
var persistenceModel = HackSessionLocator.CreatePersistenceModel(types);
|
||||
|
||||
return Fluently.Configure()
|
||||
.Database(SQLiteConfiguration.Standard.UsingFile(fileName).ShowSql())
|
||||
@@ -25,9 +26,10 @@ namespace Orchard.Tests {
|
||||
.BuildSessionFactory();
|
||||
}
|
||||
|
||||
private static void AddAlterations(AutoMappingAlterationCollection alterations, Type[] types) {
|
||||
private static void AddAlterations(AutoMappingAlterationCollection alterations, IEnumerable<Type> types) {
|
||||
foreach (var assembly in types.Select(t => t.Assembly).Distinct()) {
|
||||
alterations.Add(new AutoMappingOverrideAlteration(assembly));
|
||||
alterations.AddFromAssembly(assembly);
|
||||
}
|
||||
alterations.AddFromAssemblyOf<DataModule>();
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Packages;
|
||||
using Orchard.Tests.Models.Records;
|
||||
using Orchard.Tests.Models.Stubs;
|
||||
|
||||
namespace Orchard.Tests.Environment {
|
||||
[TestFixture]
|
||||
public class DefaultCompositionStrategyTests {
|
||||
[Test]
|
||||
public void ExpectedRecordsShouldComeBack() {
|
||||
var packageManager = new Moq.Mock<IPackageManager>();
|
||||
packageManager.Setup(x => x.ActivePackages()).Returns(new[] {
|
||||
new PackageEntry
|
||||
{ExportedTypes = new[] {typeof (GammaRecord), typeof (DeltaRecord), typeof (Delta)}}
|
||||
});
|
||||
var strategy = new DefaultCompositionStrategy(packageManager.Object);
|
||||
var recordTypes = strategy.GetRecordTypes();
|
||||
|
||||
Assert.That(recordTypes.Count(), Is.Not.EqualTo(0));
|
||||
Assert.That(recordTypes, Has.Some.EqualTo(typeof(DeltaRecord)));
|
||||
Assert.That(recordTypes, Has.Some.EqualTo(typeof(GammaRecord)));
|
||||
Assert.That(recordTypes, Has.None.EqualTo(typeof(Delta)));
|
||||
}
|
||||
}
|
||||
}
|
@@ -80,6 +80,10 @@ namespace Orchard.Tests.Environment {
|
||||
public IEnumerable<Type> GetDependencyTypes() {
|
||||
return Enumerable.Empty<Type>();
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetRecordTypes() {
|
||||
return Enumerable.Empty<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
183
src/Orchard.Tests/Models/ContentQueryTests.cs
Normal file
183
src/Orchard.Tests/Models/ContentQueryTests.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Autofac;
|
||||
using Autofac.Builder;
|
||||
using Autofac.Modules;
|
||||
using NHibernate;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Data;
|
||||
using Orchard.Models;
|
||||
using Orchard.Models.Driver;
|
||||
using Orchard.Models.Records;
|
||||
using Orchard.Tests.Models.Records;
|
||||
using Orchard.Tests.Models.Stubs;
|
||||
|
||||
namespace Orchard.Tests.Models {
|
||||
[TestFixture]
|
||||
public class ContentQueryTests {
|
||||
private IContainer _container;
|
||||
private IContentManager _manager;
|
||||
private ISessionFactory _sessionFactory;
|
||||
private ISession _session;
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void InitFixture() {
|
||||
var databaseFileName = System.IO.Path.GetTempFileName();
|
||||
_sessionFactory = DataUtility.CreateSessionFactory(
|
||||
databaseFileName,
|
||||
typeof(GammaRecord),
|
||||
typeof(DeltaRecord),
|
||||
typeof(ContentItemRecord),
|
||||
typeof(ContentTypeRecord));
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void TermFixture() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterModule(new ImplicitCollectionSupportModule());
|
||||
builder.RegisterModule(new ContentModule());
|
||||
builder.Register<DefaultContentManager>().As<IContentManager>();
|
||||
builder.Register<AlphaProvider>().As<IContentProvider>();
|
||||
builder.Register<BetaProvider>().As<IContentProvider>();
|
||||
builder.Register<GammaProvider>().As<IContentProvider>();
|
||||
builder.Register<DeltaProvider>().As<IContentProvider>();
|
||||
builder.Register<FlavoredProvider>().As<IContentProvider>();
|
||||
builder.Register<StyledProvider>().As<IContentProvider>();
|
||||
|
||||
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
|
||||
|
||||
_session = _sessionFactory.OpenSession();
|
||||
builder.Register(new DefaultModelManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();
|
||||
|
||||
_session.Delete("from GammaRecord");
|
||||
_session.Delete("from DeltaRecord");
|
||||
_session.Delete("from ContentItemRecord");
|
||||
_session.Delete("from ContentTypeRecord");
|
||||
_session.Flush();
|
||||
_session.Clear();
|
||||
|
||||
_container = builder.Build();
|
||||
_manager = _container.Resolve<IContentManager>();
|
||||
|
||||
}
|
||||
|
||||
private void AddSampleData() {
|
||||
_manager.Create<Alpha>("alpha", init => { });
|
||||
_manager.Create<Beta>("beta", init => { });
|
||||
_manager.Create<Gamma>("gamma", init => { init.Record.Frap = "the frap value"; });
|
||||
_manager.Create<Delta>("delta", init => { init.Record.Quux = "the quux value"; });
|
||||
_session.Flush();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QueryInstanceIsDifferentEachTimeYouCreateOne() {
|
||||
var contentManager1 = _container.Resolve<IContentManager>();
|
||||
var query1a = contentManager1.Query();
|
||||
var query1b = contentManager1.Query();
|
||||
|
||||
var contentManager2 = _container.Resolve<IContentManager>();
|
||||
var query2a = contentManager2.Query();
|
||||
var query2b = contentManager2.Query();
|
||||
|
||||
Assert.That(contentManager1, Is.SameAs(contentManager2));
|
||||
Assert.That(query1a, Is.SameAs(query1a));
|
||||
|
||||
Assert.That(query1a, Is.Not.SameAs(query1b));
|
||||
Assert.That(query1a, Is.Not.SameAs(query2a));
|
||||
Assert.That(query1a, Is.Not.SameAs(query2b));
|
||||
|
||||
Assert.That(query1b, Is.Not.SameAs(query2a));
|
||||
Assert.That(query1b, Is.Not.SameAs(query2b));
|
||||
|
||||
Assert.That(query2a, Is.Not.SameAs(query2b));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentManagerPropertyIsSet() {
|
||||
var contentManager = _container.Resolve<IContentManager>();
|
||||
var query = contentManager.Query();
|
||||
Assert.That(query.ContentManager, Is.SameAs(contentManager));
|
||||
|
||||
var mockManager = new Moq.Mock<IContentManager>().Object;
|
||||
var anotherQuery = _container.Resolve<IContentQuery>(TypedParameter.From(mockManager));
|
||||
Assert.That(anotherQuery, Is.Not.SameAs(query));
|
||||
Assert.That(anotherQuery.ContentManager, Is.SameAs(mockManager));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AllItemsAreReturnedByDefault() {
|
||||
AddSampleData();
|
||||
|
||||
var allItems = _manager.Query().Select();
|
||||
|
||||
Assert.That(allItems.Count(), Is.EqualTo(4));
|
||||
Assert.That(allItems.Count(x => x.Has<Alpha>()), Is.EqualTo(1));
|
||||
Assert.That(allItems.Count(x => x.Has<Beta>()), Is.EqualTo(1));
|
||||
Assert.That(allItems.Count(x => x.Has<Gamma>()), Is.EqualTo(1));
|
||||
Assert.That(allItems.Count(x => x.Has<Delta>()), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpecificTypeIsReturnedWhenSpecified() {
|
||||
AddSampleData();
|
||||
|
||||
var alphaBeta = _manager.Query().ForType("alpha", "beta").Select();
|
||||
|
||||
Assert.That(alphaBeta.Count(), Is.EqualTo(2));
|
||||
Assert.That(alphaBeta.Count(x => x.Has<Alpha>()), Is.EqualTo(1));
|
||||
Assert.That(alphaBeta.Count(x => x.Has<Beta>()), Is.EqualTo(1));
|
||||
Assert.That(alphaBeta.Count(x => x.Has<Gamma>()), Is.EqualTo(0));
|
||||
Assert.That(alphaBeta.Count(x => x.Has<Delta>()), Is.EqualTo(0));
|
||||
|
||||
var gammaDelta = _manager.Query().ForType("gamma", "delta").Select();
|
||||
|
||||
Assert.That(gammaDelta.Count(), Is.EqualTo(2));
|
||||
Assert.That(gammaDelta.Count(x => x.Has<Alpha>()), Is.EqualTo(0));
|
||||
Assert.That(gammaDelta.Count(x => x.Has<Beta>()), Is.EqualTo(0));
|
||||
Assert.That(gammaDelta.Count(x => x.Has<Gamma>()), Is.EqualTo(1));
|
||||
Assert.That(gammaDelta.Count(x => x.Has<Delta>()), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WherePredicateRestrictsResults() {
|
||||
AddSampleData();
|
||||
_manager.Create<Gamma>("gamma", init => { init.Record.Frap = "one"; });
|
||||
_manager.Create<Gamma>("gamma", init => { init.Record.Frap = "two"; });
|
||||
_manager.Create<Gamma>("gamma", init => { init.Record.Frap = "three"; });
|
||||
_manager.Create<Gamma>("gamma", init => { init.Record.Frap = "four"; });
|
||||
_session.Flush();
|
||||
var twoOrFour = _manager.Query().Where<GammaRecord>(x => x.Frap == "one" || x.Frap == "four").Select();
|
||||
|
||||
Assert.That(twoOrFour.Count(), Is.EqualTo(2));
|
||||
Assert.That(twoOrFour.Count(x => x.Has<Gamma>()), Is.EqualTo(2));
|
||||
Assert.That(twoOrFour.Count(x => x.Get<Gamma>().Record.Frap == "one"), Is.EqualTo(1));
|
||||
Assert.That(twoOrFour.Count(x => x.Get<Gamma>().Record.Frap == "four"), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void EmptyWherePredicateRequiresRecord() {
|
||||
AddSampleData();
|
||||
var gammas = _manager.Query().Where<GammaRecord>().Select();
|
||||
var deltas = _manager.Query().Where<DeltaRecord>().Select();
|
||||
|
||||
Assert.That(gammas.Count(), Is.EqualTo(1));
|
||||
Assert.That(deltas.Count(), Is.EqualTo(1));
|
||||
Assert.That(gammas.AsPart<Gamma>().Single().Record.Frap, Is.EqualTo("the frap value"));
|
||||
Assert.That(deltas.AsPart<Delta>().Single().Record.Quux, Is.EqualTo("the quux value"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -8,6 +8,7 @@ using Orchard.Data;
|
||||
using Orchard.Models;
|
||||
using Orchard.Models.Driver;
|
||||
using Orchard.Models.Records;
|
||||
using Orchard.Tests.Models.Records;
|
||||
using Orchard.Tests.Models.Stubs;
|
||||
|
||||
namespace Orchard.Tests.Models {
|
||||
|
7
src/Orchard.Tests/Models/Records/DeltaRecord.cs
Normal file
7
src/Orchard.Tests/Models/Records/DeltaRecord.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Orchard.Models.Records;
|
||||
|
||||
namespace Orchard.Tests.Models.Records {
|
||||
public class DeltaRecord : ContentPartRecord {
|
||||
public virtual string Quux { get; set; }
|
||||
}
|
||||
}
|
7
src/Orchard.Tests/Models/Records/GammaRecord.cs
Normal file
7
src/Orchard.Tests/Models/Records/GammaRecord.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Orchard.Models.Records;
|
||||
|
||||
namespace Orchard.Tests.Models.Records {
|
||||
public class GammaRecord : ContentPartRecord {
|
||||
public virtual string Frap { get; set; }
|
||||
}
|
||||
}
|
21
src/Orchard.Tests/Models/Stubs/Delta.cs
Normal file
21
src/Orchard.Tests/Models/Stubs/Delta.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Orchard.Data;
|
||||
using Orchard.Models;
|
||||
using Orchard.Models.Driver;
|
||||
using Orchard.Tests.Models.Records;
|
||||
|
||||
namespace Orchard.Tests.Models.Stubs {
|
||||
public class Delta : ContentPart<DeltaRecord> {
|
||||
}
|
||||
|
||||
|
||||
public class DeltaProvider : ContentProvider {
|
||||
public DeltaProvider(IRepository<DeltaRecord> repository) {
|
||||
Filters.Add(new ActivatingFilter<Delta>(x => x == "delta"));
|
||||
Filters.Add(new StorageFilter<DeltaRecord>(repository));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,21 +1,48 @@
|
||||
using Orchard.Data;
|
||||
using System.Linq;
|
||||
using FluentNHibernate.Automapping;
|
||||
using FluentNHibernate.Automapping.Alterations;
|
||||
using Orchard.Data;
|
||||
using Orchard.Models;
|
||||
using Orchard.Models.Driver;
|
||||
using Orchard.Models.Records;
|
||||
using Orchard.Tests.Models.Records;
|
||||
|
||||
namespace Orchard.Tests.Models.Stubs {
|
||||
public class Gamma : ContentPart<GammaRecord> {
|
||||
}
|
||||
|
||||
public class GammaRecord : ContentPartRecord {
|
||||
public virtual string Frap { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class GammaProvider : ContentProvider {
|
||||
public GammaProvider(IRepository<GammaRecord> repository){
|
||||
public GammaProvider(IRepository<GammaRecord> repository) {
|
||||
Filters.Add(new ActivatingFilter<Gamma>(x => x == "gamma"));
|
||||
Filters.Add(new StorageFilter<GammaRecord>(repository));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public class ContentItemRecordAlteration : IAutoMappingAlteration {
|
||||
// public void Alter(AutoPersistenceModel model) {
|
||||
// model.OverrideAll(mapping => {
|
||||
// var genericArguments = mapping.GetType().GetGenericArguments();
|
||||
// if (!genericArguments.Single().IsSubclassOf(typeof (ContentPartRecord))) {
|
||||
// return;
|
||||
// }
|
||||
// });
|
||||
|
||||
// model.Override<ContentItemRecord>(mapping => mapping.HasOne(record => (GammaRecord)record["GammaRecord"]).Access.NoOp().Fetch.Select());
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// interface IAlteration {
|
||||
// void Override(object mapping);
|
||||
// }
|
||||
|
||||
// class Alteration<T> : IAlteration where T : ContentPartRecord {
|
||||
// public void Override(object mappingObj) {
|
||||
// var mapping = (AutoMapping<T>)mappingObj;
|
||||
// mapping.Id(x => x.Id).GeneratedBy.Foreign("ContentItem");
|
||||
// mapping.HasOne(x => x.ContentItem).Constrained();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
@@ -100,11 +100,13 @@
|
||||
<Compile Include="DataUtility.cs" />
|
||||
<Compile Include="Data\RepositoryTests.cs" />
|
||||
<Compile Include="Data\StubLocator.cs" />
|
||||
<Compile Include="Environment\DefaultCompositionStrategyTests.cs" />
|
||||
<Compile Include="Environment\DefaultOrchardHostTests.cs" />
|
||||
<Compile Include="Environment\DefaultOrchardShellTests.cs" />
|
||||
<Compile Include="Environment\OrchardStarterTests.cs" />
|
||||
<Compile Include="Localization\NullLocalizerTests.cs" />
|
||||
<Compile Include="Logging\LoggingModuleTests.cs" />
|
||||
<Compile Include="Models\ContentQueryTests.cs" />
|
||||
<Compile Include="Models\DefaultModelManagerTests.cs" />
|
||||
<Compile Include="Models\Drivers\ModelBuilderTests.cs" />
|
||||
<Compile Include="Models\Drivers\ModelDriverTests.cs" />
|
||||
@@ -112,9 +114,12 @@
|
||||
<Compile Include="Models\Stubs\AlphaProvider.cs" />
|
||||
<Compile Include="Models\Stubs\Beta.cs" />
|
||||
<Compile Include="Models\Stubs\BetaProvider.cs" />
|
||||
<Compile Include="Models\Stubs\Delta.cs" />
|
||||
<Compile Include="Models\Records\DeltaRecord.cs" />
|
||||
<Compile Include="Models\Stubs\Flavored.cs" />
|
||||
<Compile Include="Models\Stubs\FlavoredProvider.cs" />
|
||||
<Compile Include="Models\Stubs\Gamma.cs" />
|
||||
<Compile Include="Models\Records\GammaRecord.cs" />
|
||||
<Compile Include="Models\Stubs\Styled.cs" />
|
||||
<Compile Include="Models\Stubs\StyledProvider.cs" />
|
||||
<Compile Include="Mvc\ModelBinders\KeyedListModelBinderTests.cs" />
|
||||
|
@@ -5,7 +5,7 @@ using Orchard.Security;
|
||||
|
||||
namespace Orchard.Blogs.Models {
|
||||
public class BlogPost : ContentPart<BlogPostRecord> {
|
||||
//public Blog Blog { get; set; }
|
||||
public Blog Blog { get; set; }
|
||||
public string Title { get { return this.As<RoutableAspect>().Title; } }
|
||||
public string Body { get { return this.As<BodyAspect>().Body; } }
|
||||
public string Slug { get { return this.As<RoutableAspect>().Slug; } }
|
||||
|
@@ -11,7 +11,7 @@ namespace Orchard.Blogs.Models {
|
||||
Filters.Add(new ActivatingFilter<RoutableAspect>("blogpost"));
|
||||
Filters.Add(new ActivatingFilter<BodyAspect>("blogpost"));
|
||||
Filters.Add(new StorageFilter<BlogPostRecord>(repository));
|
||||
//AddOnLoaded<BlogPost>((context, bp) => bp.Blog = contentManager.Get<Blog>(context.ContentItem.Id));
|
||||
AddOnLoaded<BlogPost>((context, bp) => bp.Blog = contentManager.Get<Blog>(bp.Record.Blog.Id));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
@@ -9,71 +10,71 @@ using FluentNHibernate.Cfg;
|
||||
using FluentNHibernate.Cfg.Db;
|
||||
using NHibernate;
|
||||
using NHibernate.Tool.hbm2ddl;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Models;
|
||||
using Orchard.Models.Records;
|
||||
|
||||
namespace Orchard.Data {
|
||||
public class HackSessionLocator : ISessionLocator, IDisposable {
|
||||
private readonly ICompositionStrategy _compositionStrategy;
|
||||
private static ISessionFactory _sessionFactory;
|
||||
private ISession _session;
|
||||
|
||||
public ISessionFactory SessionFactory {
|
||||
get {
|
||||
// TEMP: a real scenario would call for a session factory locator
|
||||
// that would eventually imply the need for configuration against one or more actual sources
|
||||
// and a means to enlist record types from active packages into correct session factory
|
||||
|
||||
var database =
|
||||
SQLiteConfiguration.Standard.UsingFile(HttpContext.Current.Server.MapPath("~/App_Data/hack.db"));
|
||||
|
||||
var automaps = new[] {
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.CmsPages")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Users")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Roles")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Media")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Core")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Sandbox")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Comments")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Tags")),
|
||||
CreatePersistenceModel(Assembly.Load("Orchard.Blogs")),
|
||||
};
|
||||
|
||||
return _sessionFactory ??
|
||||
Interlocked.CompareExchange(
|
||||
ref _sessionFactory,
|
||||
Fluently.Configure()
|
||||
.Database(database)
|
||||
.Mappings(m => {
|
||||
foreach (var automap in automaps) {
|
||||
m.AutoMappings.Add(automap);
|
||||
}
|
||||
})
|
||||
.ExposeConfiguration(
|
||||
c => new SchemaUpdate(c).Execute(false /*script*/, true /*doUpdate*/))
|
||||
.BuildSessionFactory(), null) ?? _sessionFactory;
|
||||
}
|
||||
public HackSessionLocator(ICompositionStrategy compositionStrategy) {
|
||||
_compositionStrategy = compositionStrategy;
|
||||
}
|
||||
|
||||
private static AutoPersistenceModel CreatePersistenceModel(Assembly assembly) {
|
||||
return AutoMap.Assembly(assembly)
|
||||
.Where(IsRecordType)
|
||||
.Alterations(alt => alt
|
||||
.Add(new AutoMappingOverrideAlteration(assembly))
|
||||
.AddFromAssemblyOf<DataModule>())
|
||||
private ISessionFactory BindSessionFactory() {
|
||||
// TEMP: a real scenario would call for a session factory locator
|
||||
// that would eventually imply the need for configuration against one or more actual sources
|
||||
// and a means to enlist record types from active packages into correct session factory
|
||||
|
||||
var database =
|
||||
SQLiteConfiguration.Standard.UsingFile(HttpContext.Current.Server.MapPath("~/App_Data/hack.db"));
|
||||
|
||||
var recordTypes = _compositionStrategy.GetRecordTypes();
|
||||
|
||||
return _sessionFactory ??
|
||||
Interlocked.CompareExchange(
|
||||
ref _sessionFactory,
|
||||
BuildSessionFactory(database, recordTypes), null) ?? _sessionFactory;
|
||||
|
||||
}
|
||||
|
||||
private static ISessionFactory BuildSessionFactory(IPersistenceConfigurer database, IEnumerable<Type> recordTypes) {
|
||||
return Fluently.Configure()
|
||||
.Database(database)
|
||||
.Mappings(m => m.AutoMappings.Add(CreatePersistenceModel(recordTypes)))
|
||||
.ExposeConfiguration(c => new SchemaUpdate(c).Execute(false /*script*/, true /*doUpdate*/))
|
||||
.BuildSessionFactory();
|
||||
}
|
||||
|
||||
public static AutoPersistenceModel CreatePersistenceModel(IEnumerable<Type> recordTypes) {
|
||||
return AutoMap.Source(new TypeSource(recordTypes))
|
||||
.Alterations(alt => {
|
||||
foreach (var recordAssembly in recordTypes.Select(x => x.Assembly).Distinct()) {
|
||||
alt.Add(new AutoMappingOverrideAlteration(recordAssembly));
|
||||
}
|
||||
alt.AddFromAssemblyOf<DataModule>();
|
||||
alt.Add(new ContentItemRecordAlteration(recordTypes));
|
||||
})
|
||||
.Conventions.AddFromAssemblyOf<DataModule>();
|
||||
}
|
||||
|
||||
private static bool IsRecordType(Type type) {
|
||||
return (type.Namespace.EndsWith(".Models") || type.Namespace.EndsWith(".Records")) &&
|
||||
type.GetProperty("Id") != null &&
|
||||
type.GetProperty("Id").GetAccessors().All(x => x.IsVirtual) &&
|
||||
!type.IsSealed &&
|
||||
!type.IsAbstract &&
|
||||
!typeof(IContent).IsAssignableFrom(type);
|
||||
private class TypeSource : ITypeSource {
|
||||
private readonly IEnumerable<Type> _recordTypes;
|
||||
|
||||
public TypeSource(IEnumerable<Type> recordTypes) {
|
||||
_recordTypes = recordTypes;
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetTypes() {
|
||||
return _recordTypes;
|
||||
}
|
||||
}
|
||||
|
||||
public ISession For(Type entityType) {
|
||||
return _session ?? Interlocked.CompareExchange(ref _session, SessionFactory.OpenSession(), null) ?? _session;
|
||||
return _session ?? Interlocked.CompareExchange(ref _session, BindSessionFactory().OpenSession(), null) ?? _session;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
|
13
src/Orchard/Data/IDataManager.cs
Normal file
13
src/Orchard/Data/IDataManager.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Orchard.Data {
|
||||
public interface IDataManager {
|
||||
}
|
||||
|
||||
public class DataManager : IDataManager {
|
||||
|
||||
}
|
||||
}
|
@@ -5,6 +5,8 @@ using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Web.Compilation;
|
||||
using Autofac;
|
||||
using Orchard.Models;
|
||||
using Orchard.Models.Records;
|
||||
using Orchard.Packages;
|
||||
|
||||
namespace Orchard.Environment {
|
||||
@@ -14,6 +16,7 @@ namespace Orchard.Environment {
|
||||
IEnumerable<Assembly> GetAssemblies();
|
||||
IEnumerable<Type> GetModuleTypes();
|
||||
IEnumerable<Type> GetDependencyTypes();
|
||||
IEnumerable<Type> GetRecordTypes();
|
||||
}
|
||||
|
||||
public class DefaultCompositionStrategy : ICompositionStrategy {
|
||||
@@ -26,13 +29,13 @@ namespace Orchard.Environment {
|
||||
public IEnumerable<Assembly> GetAssemblies() {
|
||||
return _packageManager.ActivePackages()
|
||||
.Select(entry => entry.Assembly)
|
||||
.Concat(new[]{typeof(IOrchardHost).Assembly});
|
||||
.Concat(new[] { typeof(IOrchardHost).Assembly });
|
||||
//return BuildManager.GetReferencedAssemblies().OfType<Assembly>();
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetModuleTypes() {
|
||||
var types = _packageManager.ActivePackages().SelectMany(x => x.ExportedTypes);
|
||||
types = types.Concat(typeof (IOrchardHost).Assembly.GetExportedTypes());
|
||||
types = types.Concat(typeof(IOrchardHost).Assembly.GetExportedTypes());
|
||||
var nonAbstractClasses = types.Where(t => t.IsClass && !t.IsAbstract);
|
||||
var modules = nonAbstractClasses.Where(t => typeof(IModule).IsAssignableFrom(t));
|
||||
return modules;
|
||||
@@ -45,5 +48,22 @@ namespace Orchard.Environment {
|
||||
var modules = nonAbstractClasses.Where(t => typeof(IDependency).IsAssignableFrom(t));
|
||||
return modules;
|
||||
}
|
||||
|
||||
public IEnumerable<Type> GetRecordTypes() {
|
||||
var types = _packageManager.ActivePackages().SelectMany(x => x.ExportedTypes);
|
||||
var recordTypes = types.Where(IsRecordType)
|
||||
.Concat(new[] { typeof(ContentItemRecord), typeof(ContentPartRecord), typeof(ContentTypeRecord) });
|
||||
return recordTypes;
|
||||
}
|
||||
|
||||
|
||||
private static bool IsRecordType(Type type) {
|
||||
return (type.Namespace.EndsWith(".Models") || type.Namespace.EndsWith(".Records")) &&
|
||||
type.GetProperty("Id") != null &&
|
||||
(type.GetProperty("Id").GetAccessors() ?? Enumerable.Empty<MethodInfo>()).All(x => x.IsVirtual) &&
|
||||
!type.IsSealed &&
|
||||
!type.IsAbstract &&
|
||||
!typeof(IContent).IsAssignableFrom(type);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Models {
|
||||
|
||||
@@ -47,6 +49,9 @@ namespace Orchard.Models {
|
||||
}
|
||||
|
||||
|
||||
public static IEnumerable<T> AsPart<T>(this IEnumerable<ContentItem> items) where T : class {
|
||||
return items == null ? null : items.Where(item => item.Is<T>()).Select(item => item.As<T>());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
13
src/Orchard/Models/ContentModule.cs
Normal file
13
src/Orchard/Models/ContentModule.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Autofac.Builder;
|
||||
|
||||
namespace Orchard.Models {
|
||||
public class ContentModule : Module {
|
||||
protected override void Load(ContainerBuilder builder) {
|
||||
builder.Register<DefaultContentQuery>().As<IContentQuery>().FactoryScoped();
|
||||
}
|
||||
}
|
||||
}
|
@@ -145,6 +145,10 @@ namespace Orchard.Models {
|
||||
return context.Editors;
|
||||
}
|
||||
|
||||
public IContentQuery Query() {
|
||||
return _context.Resolve<IContentQuery>(TypedParameter.From<IContentManager>(this));
|
||||
}
|
||||
|
||||
private ContentTypeRecord AcquireContentTypeRecord(string contentType) {
|
||||
var contentTypeRecord = _contentTypeRepository.Get(x => x.Name == contentType);
|
||||
if (contentTypeRecord == null) {
|
||||
|
82
src/Orchard/Models/DefaultContentQuery.cs
Normal file
82
src/Orchard/Models/DefaultContentQuery.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using NHibernate;
|
||||
using NHibernate.Criterion;
|
||||
using NHibernate.Impl;
|
||||
using NHibernate.Linq;
|
||||
using Orchard.Data;
|
||||
using Orchard.Models.Records;
|
||||
|
||||
namespace Orchard.Models {
|
||||
public class DefaultContentQuery : IContentQuery {
|
||||
private readonly ISessionLocator _sessionLocator;
|
||||
private ISession _session;
|
||||
private ICriteria _itemCriteria;
|
||||
|
||||
public DefaultContentQuery(IContentManager contentManager, ISessionLocator sessionLocator) {
|
||||
_sessionLocator = sessionLocator;
|
||||
ContentManager = contentManager;
|
||||
}
|
||||
|
||||
public IContentManager ContentManager { get; private set; }
|
||||
|
||||
ISession BindSession() {
|
||||
if (_session == null)
|
||||
_session = _sessionLocator.For(typeof(ContentItemRecord));
|
||||
return _session;
|
||||
}
|
||||
|
||||
ICriteria BindItemCriteria() {
|
||||
if (_itemCriteria == null) {
|
||||
_itemCriteria = BindSession().CreateCriteria<ContentItemRecord>();
|
||||
}
|
||||
return _itemCriteria;
|
||||
}
|
||||
|
||||
ICriteria BindCriteriaByPath(string path) {
|
||||
var itemCriteria = BindItemCriteria();
|
||||
return itemCriteria.GetCriteriaByPath(path) ?? itemCriteria.CreateCriteria(path);
|
||||
}
|
||||
|
||||
|
||||
public IContentQuery ForType(params string[] contentTypeNames) {
|
||||
BindCriteriaByPath("ContentType").Add(Restrictions.InG("Name", contentTypeNames));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public IContentQuery Where<TRecord>() {
|
||||
// this simply demands an inner join
|
||||
BindCriteriaByPath(typeof(TRecord).Name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IContentQuery Where<TRecord>(Expression<Func<TRecord, bool>> predicate) {
|
||||
|
||||
// build a linq to nhibernate expression
|
||||
var options = new QueryOptions();
|
||||
var queryProvider = new NHibernateQueryProvider(BindSession(), options);
|
||||
var queryable = new Query<TRecord>(queryProvider, options).Where(predicate);
|
||||
|
||||
// translate it into the nhibernate ICriteria implementation
|
||||
var criteria = (CriteriaImpl)queryProvider.TranslateExpression(queryable.Expression);
|
||||
|
||||
// attach the criterion from the predicate to this query's criteria for the record
|
||||
var recordCriteria = BindCriteriaByPath(typeof(TRecord).Name);
|
||||
foreach (var entry in criteria.IterateExpressionEntries()) {
|
||||
recordCriteria.Add(entry.Criterion);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<ContentItem> Select() {
|
||||
return BindItemCriteria()
|
||||
.List<ContentItemRecord>()
|
||||
.Select(x => ContentManager.Get(x.Id));
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,5 +13,7 @@ namespace Orchard.Models {
|
||||
IEnumerable<ModelTemplate> GetDisplays(IContent contentItem);
|
||||
IEnumerable<ModelTemplate> GetEditors(IContent contentItem);
|
||||
IEnumerable<ModelTemplate> UpdateEditors(IContent contentItem, IUpdateModel updater);
|
||||
|
||||
IContentQuery Query();
|
||||
}
|
||||
}
|
||||
|
17
src/Orchard/Models/IContentQuery.cs
Normal file
17
src/Orchard/Models/IContentQuery.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Orchard.Models {
|
||||
public interface IContentQuery {
|
||||
IContentManager ContentManager { get; }
|
||||
|
||||
IContentQuery ForType(params string[] contentTypeNames);
|
||||
|
||||
IContentQuery Where<TRecord>();
|
||||
IContentQuery Where<TRecord>(Expression<Func<TRecord, bool>> predicate);
|
||||
|
||||
IEnumerable<ContentItem> Select();
|
||||
|
||||
}
|
||||
}
|
127
src/Orchard/Models/Records/ContentItemRecordAlteration.cs
Normal file
127
src/Orchard/Models/Records/ContentItemRecordAlteration.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using FluentNHibernate.Automapping;
|
||||
using FluentNHibernate.Automapping.Alterations;
|
||||
|
||||
namespace Orchard.Models.Records {
|
||||
class ContentItemRecordAlteration : IAutoMappingAlteration {
|
||||
private readonly IEnumerable<Type> _recordTypes;
|
||||
|
||||
public ContentItemRecordAlteration() {
|
||||
_recordTypes = Enumerable.Empty<Type>();
|
||||
}
|
||||
public ContentItemRecordAlteration(IEnumerable<Type> recordTypes) {
|
||||
_recordTypes = recordTypes;
|
||||
}
|
||||
|
||||
public void Alter(AutoPersistenceModel model) {
|
||||
model.Override<ContentItemRecord>(mapping => {
|
||||
foreach (var recordType in _recordTypes.Where(x => x.IsSubclassOf(typeof(ContentPartRecord)))) {
|
||||
var type = typeof(Alteration<>).MakeGenericType(recordType);
|
||||
var alteration = (IAlteration)Activator.CreateInstance(type);
|
||||
alteration.Override(mapping);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface IAlteration {
|
||||
void Override(AutoMapping<ContentItemRecord> mapping);
|
||||
}
|
||||
|
||||
class Alteration<TPartRecord> : IAlteration where TPartRecord : ContentPartRecord {
|
||||
public void Override(AutoMapping<ContentItemRecord> mapping) {
|
||||
var name = typeof(TPartRecord).Name;
|
||||
|
||||
|
||||
var syntheticMethod = new DynamicMethod(name, typeof(TPartRecord), null, typeof(ContentItemRecord));
|
||||
var syntheticProperty = new FakePropertyInfo(syntheticMethod);
|
||||
|
||||
var parameter = Expression.Parameter(typeof(ContentItemRecord), "record");
|
||||
var syntheticExpression = (Expression<Func<ContentItemRecord, TPartRecord>>)Expression.Lambda(
|
||||
typeof(Func<ContentItemRecord, TPartRecord>),
|
||||
Expression.Property(parameter, syntheticProperty),
|
||||
parameter);
|
||||
|
||||
mapping.HasOne(syntheticExpression).Access.NoOp().Fetch.Select();
|
||||
}
|
||||
|
||||
private class FakePropertyInfo : PropertyInfo {
|
||||
private readonly DynamicMethod _getMethod;
|
||||
|
||||
public FakePropertyInfo(DynamicMethod dynamicMethod) {
|
||||
_getMethod = dynamicMethod;
|
||||
}
|
||||
|
||||
public override object[] GetCustomAttributes(bool inherit) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool IsDefined(Type attributeType, bool inherit) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MethodInfo[] GetAccessors(bool nonPublic) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MethodInfo GetGetMethod(bool nonPublic) {
|
||||
return _getMethod;
|
||||
}
|
||||
|
||||
public override MethodInfo GetSetMethod(bool nonPublic) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override ParameterInfo[] GetIndexParameters() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return _getMethod.Name; }
|
||||
}
|
||||
|
||||
public override Type DeclaringType {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override Type ReflectedType {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override Type PropertyType {
|
||||
get { return _getMethod.ReturnType; }
|
||||
}
|
||||
|
||||
public override PropertyAttributes Attributes {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override bool CanRead {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanWrite {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override object[] GetCustomAttributes(Type attributeType, bool inherit) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,42 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentNHibernate.Automapping;
|
||||
using FluentNHibernate.Automapping.Alterations;
|
||||
|
||||
namespace Orchard.Models.Records {
|
||||
public abstract class ContentPartRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual ContentItemRecord ContentItem { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class ContentPartRecordAlteration : IAutoMappingAlteration {
|
||||
public void Alter(AutoPersistenceModel model) {
|
||||
|
||||
model.OverrideAll(mapping => {
|
||||
var genericArguments = mapping.GetType().GetGenericArguments();
|
||||
if (!genericArguments.Single().IsSubclassOf(typeof(ContentPartRecord))) {
|
||||
return;
|
||||
}
|
||||
|
||||
var type = typeof(Alteration<>).MakeGenericType(genericArguments);
|
||||
var alteration = (IAlteration)Activator.CreateInstance(type);
|
||||
alteration.Override(mapping);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
interface IAlteration {
|
||||
void Override(object mapping);
|
||||
}
|
||||
|
||||
class Alteration<T> : IAlteration where T : ContentPartRecord {
|
||||
public void Override(object o) {
|
||||
var mapping = (AutoMapping<T>)o;
|
||||
mapping.Id(x => x.Id).GeneratedBy.Foreign("ContentItem");
|
||||
mapping.HasOne(x => x.ContentItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
35
src/Orchard/Models/Records/ContentPartRecordAlteration.cs
Normal file
35
src/Orchard/Models/Records/ContentPartRecordAlteration.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FluentNHibernate.Automapping;
|
||||
using FluentNHibernate.Automapping.Alterations;
|
||||
|
||||
namespace Orchard.Models.Records {
|
||||
public class ContentPartRecordAlteration : IAutoMappingAlteration {
|
||||
public void Alter(AutoPersistenceModel model) {
|
||||
|
||||
model.OverrideAll(mapping => {
|
||||
var genericArguments = mapping.GetType().GetGenericArguments();
|
||||
if (!genericArguments.Single().IsSubclassOf(typeof(ContentPartRecord))) {
|
||||
return;
|
||||
}
|
||||
|
||||
var type = typeof(Alteration<>).MakeGenericType(genericArguments);
|
||||
var alteration = (IAlteration)Activator.CreateInstance(type);
|
||||
alteration.Override(mapping);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
interface IAlteration {
|
||||
void Override(object mapping);
|
||||
}
|
||||
|
||||
class Alteration<T> : IAlteration where T : ContentPartRecord {
|
||||
public void Override(object mappingObj) {
|
||||
var mapping = (AutoMapping<T>)mappingObj;
|
||||
mapping.Id(x => x.Id).GeneratedBy.Foreign("ContentItem");
|
||||
mapping.HasOne(x => x.ContentItem).Constrained();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -126,6 +126,8 @@
|
||||
<Compile Include="Localization\NullLocalizer.cs" />
|
||||
<Compile Include="Models\Aspects\ICommonAspect.cs" />
|
||||
<Compile Include="Models\ContentItem.cs" />
|
||||
<Compile Include="Models\ContentModule.cs" />
|
||||
<Compile Include="Models\DefaultContentQuery.cs" />
|
||||
<Compile Include="Models\Driver\ActivatedContentContext.cs" />
|
||||
<Compile Include="Models\Driver\ActivatingFilter.cs" />
|
||||
<Compile Include="Models\Driver\IContentActivatingFilter.cs" />
|
||||
@@ -159,7 +161,10 @@
|
||||
<Compile Include="Models\ContentExtensions.cs" />
|
||||
<Compile Include="Models\ContentPart.cs" />
|
||||
<Compile Include="Models\IContent.cs" />
|
||||
<Compile Include="Models\IContentQuery.cs" />
|
||||
<Compile Include="Models\Records\ContentItemRecordAlteration.cs" />
|
||||
<Compile Include="Models\Records\ContentPartRecord.cs" />
|
||||
<Compile Include="Models\Records\ContentPartRecordAlteration.cs" />
|
||||
<Compile Include="Models\Records\ContentTypeRecord.cs" />
|
||||
<Compile Include="Models\Records\ContentItemRecord.cs" />
|
||||
<Compile Include="Models\Driver\UpdateContentContext.cs" />
|
||||
|
Reference in New Issue
Block a user