Skip to content

Instantly share code, notes, and snippets.

@jagregory
Created July 21, 2011 22:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jagregory/1098406 to your computer and use it in GitHub Desktop.
Save jagregory/1098406 to your computer and use it in GitHub Desktop.
Index: src/FluentNHibernate.Testing/DatabaseObjects/FluentIndex/FluentIndexBaseTests.cs
===================================================================
--- src/FluentNHibernate.Testing/DatabaseObjects/FluentIndex/FluentIndexBaseTests.cs (revision 0)
+++ src/FluentNHibernate.Testing/DatabaseObjects/FluentIndex/FluentIndexBaseTests.cs (revision 0)
@@ -0,0 +1,64 @@
+using System.Text.RegularExpressions;
+using FluentNHibernate.Cfg;
+using FluentNHibernate.Cfg.Db;
+using FluentNHibernate.DatabaseObjects.FluentIndex;
+using FluentNHibernate.Testing.DomainModel;
+using NHibernate.Cfg;
+using NHibernate.Dialect;
+using NHibernate.Engine;
+using NUnit.Framework;
+using Rhino.Mocks;
+
+namespace FluentNHibernate.Testing.DatabaseObjects.FluentIndex
+{
+ [TestFixture]
+ public class FluentIndexBaseTests : FluentIndexBase
+ {
+ static readonly Configuration config = Fluently.Configure()
+ .Database(SQLiteConfiguration.Standard.InMemory)
+ .Mappings(m =>
+ m.FluentMappings.AddFromAssemblyOf<Record>())
+ .BuildConfiguration();
+
+ private readonly Dialect dialect = MockRepository.GenerateStub<Dialect>();
+ private readonly IMapping mapping = MockRepository.GenerateStub<IMapping>();
+
+ readonly Regex createClustered = new Regex("(CREATE).*?(CLUSTERED).*?(Record)(_)(id).*?(schema).*?(Record).*?(id).*?(ASC)");
+ readonly Regex createUniqueNonclustered = new Regex("(CREATE).*?(UNIQUE)( )(NONCLUSTERED).*?(Record)(_)(Name).*?(schema).*?(Record).*?(Name).*?(ASC)");
+ readonly Regex createNonclustered = new Regex("(CREATE).*?(NONCLUSTERED).*?(SuperRecord)(_)(Type).*?(schema).*?(SuperRecord).*?(Type).*?(ASC)");
+
+ readonly Regex dropClustered = new Regex("(DROP).*?(Record)(_)(id).*?(schema).*?(Record)");
+ readonly Regex dropUniqueNonclustered = new Regex("(DROP).*?(Record)(_)(Name).*?(schema).*?(Record)");
+ readonly Regex dropNonclustered = new Regex("(DROP).*?(SuperRecord)(_)(Type).*?(schema).*?(SuperRecord)");
+
+ public FluentIndexBaseTests() : this(config) { }
+
+ public FluentIndexBaseTests(Configuration configuration)
+ : base(configuration)
+ {
+ ClusteredIndex<Record>().OnPrimaryKey();
+ UniqueNonClusteredIndex<Record>().OnProperty(x => x.Name);
+ NonClusteredIndex<ChildRecord>().OnDiscriminator();
+ }
+
+ [Test]
+ public void SqlCreateStringGeneratesExpectedIndexes()
+ {
+ string indexes = SqlCreateString(dialect, mapping, "catalog", "schema");
+
+ createClustered.Match(indexes).Success.ShouldBeTrue();
+ createUniqueNonclustered.Match(indexes).Success.ShouldBeTrue();
+ createNonclustered.Match(indexes).Success.ShouldBeTrue();
+ }
+
+ [Test]
+ public void SqlDropStringGeneratesExpectedIndexes()
+ {
+ string indexes = SqlDropString(dialect, "catalog", "schema");
+
+ dropClustered.Match(indexes).Success.ShouldBeTrue();
+ dropUniqueNonclustered.Match(indexes).Success.ShouldBeTrue();
+ dropNonclustered.Match(indexes).Success.ShouldBeTrue();
+ }
+ }
+}
Index: src/FluentNHibernate.Testing/DatabaseObjects/FluentIndex/IndexTests.cs
===================================================================
--- src/FluentNHibernate.Testing/DatabaseObjects/FluentIndex/IndexTests.cs (revision 0)
+++ src/FluentNHibernate.Testing/DatabaseObjects/FluentIndex/IndexTests.cs (revision 0)
@@ -0,0 +1,91 @@
+using FluentNHibernate.Cfg;
+using FluentNHibernate.Cfg.Db;
+using FluentNHibernate.DatabaseObjects.FluentIndex;
+using FluentNHibernate.Testing.DomainModel;
+using NHibernate.Cfg;
+using NHibernate.Dialect;
+using NUnit.Framework;
+using Rhino.Mocks;
+
+namespace FluentNHibernate.Testing.DatabaseObjects.FluentIndex
+{
+ [TestFixture]
+ public class IndexTests
+ {
+ private Configuration config;
+ private readonly Dialect dialect = MockRepository.GenerateStub<Dialect>();
+
+ [TestFixtureSetUp]
+ public void SetUpFixture()
+ {
+ config = Fluently.Configure()
+ .Database(SQLiteConfiguration.Standard.InMemory)
+ .Mappings(m =>
+ m.FluentMappings.AddFromAssemblyOf<Record>())
+ .BuildConfiguration();
+ }
+
+ [Test]
+ public void IndexResolvesCorrectTable()
+ {
+ var index = new Index<Record>(IndexType.Clustered, config);
+ index.OnPrimaryKey();
+
+ index.Table.ShouldContain("Record");
+ }
+
+ [Test]
+ public void IndexResolvesCorrectShortTable()
+ {
+ var index = new Index<Record>(IndexType.Clustered, config);
+ index.OnPrimaryKey();
+
+ index.ShortTable.ShouldEqual("Record");
+ }
+
+ [Test]
+ public void OnPrimaryKeyResolvesCorrectColumn()
+ {
+ var index = new Index<Record>(IndexType.Clustered, config);
+ index.OnPrimaryKey();
+
+ index.Column.ShouldEqual("id");
+ }
+
+ [Test]
+ public void OnPropertyResolvesCorrectColumn()
+ {
+ var index = new Index<Record>(IndexType.Clustered, config);
+ index.OnProperty(x => x.Name);
+
+ index.Column.ShouldEqual("Name");
+ }
+
+ [Test]
+ public void OnDiscriminatorResolvesCorrectColumn()
+ {
+ var index = new Index<ChildRecord>(IndexType.Clustered, config);
+ index.OnDiscriminator();
+
+ index.Column.ShouldEqual("Type");
+ }
+
+ [Test]
+ public void Ascending()
+ {
+ var index = new Index<Record>(IndexType.Clustered, config);
+ index.OnPrimaryKey().Ascending();
+
+ index.GetCreateString(dialect, "catalog", "schema").ShouldContain("ASC");
+ }
+
+ [Test]
+ public void Descending()
+ {
+ var index = new Index<Record>(IndexType.Clustered, config);
+ index.OnPrimaryKey().Descending();
+
+ index.GetCreateString(dialect, "catalog", "schema").ShouldContain("DESC");
+ }
+ }
+}
Index: src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj
===================================================================
--- src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj (revision 423)
+++ src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj (working copy)
@@ -3,7 +3,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>9.0.30729</ProductVersion>
+ <ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F5DC3221-827E-4CB4-B61C-5F50EB4D32EA}</ProjectGuid>
<OutputType>Library</OutputType>
@@ -125,6 +125,8 @@
<Compile Include="ConventionsTests\SubclassDiscoveryConventionTester.cs" />
<Compile Include="ConventionsTests\SubclassMappingPartDiscoveryConventionTester.cs" />
<Compile Include="ConventionsTests\VersionDiscoveryConventionTester.cs" />
+ <Compile Include="DatabaseObjects\FluentIndex\FluentIndexBaseTests.cs" />
+ <Compile Include="DatabaseObjects\FluentIndex\IndexTests.cs" />
<Compile Include="DomainModel\Mapping\ClassMapConventionsTester.cs" />
<Compile Include="DomainModel\Mapping\ClassMapDynamicInsertTester.cs" />
<Compile Include="DomainModel\Mapping\ClassMapDynamicUpdateTester.cs" />
Index: src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj.user
===================================================================
--- src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj.user (revision 423)
+++ src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj.user (working copy)
@@ -1,5 +1,5 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
- <ProjectView>ShowAllFiles</ProjectView>
+ <ProjectView>ProjectFiles</ProjectView>
</PropertyGroup>
</Project>
\ No newline at end of file
Index: src/FluentNHibernate/DatabaseObjects/FluentIndex/FluentIndexBase.cs
===================================================================
--- src/FluentNHibernate/DatabaseObjects/FluentIndex/FluentIndexBase.cs (revision 0)
+++ src/FluentNHibernate/DatabaseObjects/FluentIndex/FluentIndexBase.cs (revision 0)
@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+using System.Text;
+using NHibernate.Cfg;
+using NHibernate.Dialect;
+using NHibernate.Engine;
+using NHibernate.Mapping;
+
+namespace FluentNHibernate.DatabaseObjects.FluentIndex
+{
+ public abstract class FluentIndexBase : AbstractAuxiliaryDatabaseObject
+ {
+ private readonly IList<IIndex> indexes = new List<IIndex>();
+ private readonly Configuration configuration;
+
+ protected FluentIndexBase(Configuration configuration)
+ {
+ this.configuration = configuration;
+ }
+
+ protected IIndexIncomplete<T> ClusteredIndex<T>()
+ {
+ Index<T> index = new Index<T>(IndexType.Clustered, configuration);
+ indexes.Add(index);
+ return index;
+ }
+
+ protected IIndexIncomplete<T> NonClusteredIndex<T>()
+ {
+ Index<T> index = new Index<T>(IndexType.NonClustered, configuration);
+ indexes.Add(index);
+ return index;
+ }
+
+ protected IIndexIncomplete<T> UniqueNonClusteredIndex<T>()
+ {
+ Index<T> index = new Index<T>(IndexType.NonClusteredUnique, configuration);
+ indexes.Add(index);
+ return index;
+ }
+
+ public override string SqlCreateString(Dialect d, IMapping p, string defaultCatalog, string defaultSchema)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (var index in indexes)
+ { sb.AppendLine(index.GetCreateString(d, defaultCatalog, defaultSchema)); }
+ return sb.ToString();
+ }
+
+ public override string SqlDropString(Dialect d, string defaultCatalog, string defaultSchema)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (var index in indexes)
+ { sb.AppendLine(index.GetDropString(d, defaultCatalog, defaultSchema)); }
+ return sb.ToString();
+ }
+ }
+}
\ No newline at end of file
Index: src/FluentNHibernate/DatabaseObjects/FluentIndex/Index.cs
===================================================================
--- src/FluentNHibernate/DatabaseObjects/FluentIndex/Index.cs (revision 0)
+++ src/FluentNHibernate/DatabaseObjects/FluentIndex/Index.cs (revision 0)
@@ -0,0 +1,162 @@
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using FluentNHibernate.Utils;
+using NHibernate;
+using NHibernate.Cfg;
+using NHibernate.Dialect;
+using NHibernate.Persister.Entity;
+
+namespace FluentNHibernate.DatabaseObjects.FluentIndex
+{
+ public interface IIndex
+ {
+ string GetCreateString(Dialect dialect, string defaultCatalog, string defaultSchema);
+ string GetDropString(Dialect dialect, string defaultCatalog, string defaultSchema);
+ }
+
+ public interface IIndexIncomplete<T>
+ {
+ IIndexOptional OnDiscriminator();
+ IIndexOptional OnPrimaryKey();
+ IIndexOptional OnProperty(Expression<Func<T, object>> property);
+ }
+
+ public interface IIndexOptional
+ {
+ void Ascending();
+ void Descending();
+ }
+
+ public enum IndexType
+ {
+ Clustered,
+ NonClustered,
+ NonClusteredUnique
+ }
+
+ public class Index<T> : IIndex, IIndexIncomplete<T>, IIndexOptional
+ {
+ private static Type IndexedClass { get { return typeof(T); } }
+ private readonly IndexType indexType;
+ private readonly Configuration configuration;
+
+ public Index(IndexType indexType, Configuration configuration)
+ {
+ this.indexType = indexType;
+ this.configuration = configuration;
+ }
+
+ private bool onDiscriminator;
+ public IIndexOptional OnDiscriminator()
+ {
+ onDiscriminator = true;
+ return this;
+ }
+
+ private bool onPrimaryKey;
+ public IIndexOptional OnPrimaryKey()
+ {
+ onPrimaryKey = true;
+ return this;
+ }
+
+ protected MemberInfo IndexedProperty { get; set; }
+ private bool onProperty;
+ public IIndexOptional OnProperty(Expression<Func<T, object>> property)
+ {
+ IndexedProperty = ReflectionHelper.GetProperty(property);
+ onProperty = true;
+ return this;
+ }
+
+ private bool isAscending = true;
+ public void Ascending()
+ {
+ isAscending = true;
+ }
+
+ public void Descending()
+ {
+ isAscending = false;
+ }
+
+ protected bool MappingIsResolved { get; set; }
+ protected void ResolveMapping()
+ {
+ ISessionFactory sessionFactory = configuration.BuildSessionFactory();
+ AbstractEntityPersister classMetadata = (AbstractEntityPersister)sessionFactory.GetClassMetadata(IndexedClass);
+
+ Table = classMetadata.TableName
+ // Remove SQL escape characters
+ .Replace("\"", "")
+ .Replace("[", "")
+ .Replace("]", "");
+ if (onProperty)
+ {
+ Column = classMetadata.GetPropertyColumnNames(IndexedProperty.Name)[0];
+ }
+ else if (onDiscriminator)
+ {
+ Column = classMetadata.DiscriminatorColumnName;
+ }
+ else if (onPrimaryKey)
+ {
+ Column = classMetadata.KeyColumnNames[0];
+ }
+ else
+ {
+ throw new InvalidOperationException("Must provide a column to be indexed.");
+ }
+ MappingIsResolved = true;
+ }
+
+ public string ShortTable { get { return Table.Split('.').Last(); } }
+
+ private string m_Table;
+ public string Table
+ {
+ get
+ {
+ if (!MappingIsResolved) ResolveMapping();
+ return m_Table;
+ }
+ private set { m_Table = value; }
+ }
+
+ private string m_Column;
+ public string Column
+ {
+ get
+ {
+ if (!MappingIsResolved) ResolveMapping();
+ return m_Column;
+ }
+ private set { m_Column = value; }
+ }
+
+ public string GetCreateString(Dialect dialect, string defaultCatalog, string defaultSchema)
+ {
+ string sort = isAscending ? "ASC" : "DESC";
+ string createPrefix = GetCreateIndexPrefix();
+ return string.Format(createPrefix + "INDEX [IX_{1}_{2}] ON {0}.[{1}] ([{2}] {3})", defaultSchema, ShortTable, Column, sort);
+ }
+
+ public string GetDropString(Dialect dialect, string defaultCatalog, string defaultSchema)
+ {
+ return string.Format("DROP INDEX [IX_{1}_{2}] ON {0}.[{1}]", defaultSchema, ShortTable, Column);
+ }
+
+ protected string GetCreateIndexPrefix()
+ {
+ switch (indexType)
+ {
+ case IndexType.Clustered: return "CREATE CLUSTERED ";
+ case IndexType.NonClustered: return "CREATE NONCLUSTERED ";
+ case IndexType.NonClusteredUnique: return "CREATE UNIQUE NONCLUSTERED ";
+ default: throw new ArgumentException("indexType is unknown");
+ }
+ }
+ }
+}
\ No newline at end of file
Index: src/FluentNHibernate/FluentNHibernate.csproj
===================================================================
--- src/FluentNHibernate/FluentNHibernate.csproj (revision 423)
+++ src/FluentNHibernate/FluentNHibernate.csproj (working copy)
@@ -3,7 +3,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>9.0.30729</ProductVersion>
+ <ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{1C988DFB-1EC5-484E-87D9-1D3C775BA435}</ProjectGuid>
<OutputType>Library</OutputType>
@@ -175,6 +175,8 @@
<Compile Include="Conventions\IJoinedSubclassConvention.cs" />
<Compile Include="Conventions\ISubclassConvention.cs" />
<Compile Include="Conventions\MultipleAttribute.cs" />
+ <Compile Include="DatabaseObjects\FluentIndex\FluentIndexBase.cs" />
+ <Compile Include="DatabaseObjects\FluentIndex\Index.cs" />
<Compile Include="Data\Entity.cs" />
<Compile Include="Data\InMemoryRepository.cs" />
<Compile Include="Data\IRepository.cs" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment