Index: src/FluentNHibernate.Testing/DomainModel/Mapping/StoredProcedureTests.cs =================================================================== --- src/FluentNHibernate.Testing/DomainModel/Mapping/StoredProcedureTests.cs (revision 0) +++ src/FluentNHibernate.Testing/DomainModel/Mapping/StoredProcedureTests.cs (revision 0) @@ -0,0 +1,35 @@ +using NUnit.Framework; + +namespace FluentNHibernate.Testing.DomainModel.Mapping +{ + [TestFixture] + public class StoredProcedureTests + { + [Test] + public void Can_specify_sql_insert() + { + new MappingTester() + .ForMapping(m => m.SqlInsert("Insert ABC")) + .RootElement.Element("class/sql-insert") + .ValueEquals("Insert ABC"); + } + + [Test] + public void Can_specify_sql_update() + { + new MappingTester() + .ForMapping(m => m.SqlUpdate("Update ABC")) + .RootElement.Element("class/sql-update") + .ValueEquals("Update ABC"); + } + + [Test] + public void Can_specify_sql_delete() + { + new MappingTester() + .ForMapping(m => m.SqlDelete("Delete ABC")) + .RootElement.Element("class/sql-delete") + .ValueEquals("Delete ABC"); + } + } +} \ No newline at end of file Index: src/FluentNHibernate.Testing/FluentInterfaceTests/BaseModelFixture.cs =================================================================== --- src/FluentNHibernate.Testing/FluentInterfaceTests/BaseModelFixture.cs (revision 647) +++ src/FluentNHibernate.Testing/FluentInterfaceTests/BaseModelFixture.cs (working copy) @@ -145,5 +145,10 @@ { return new ModelTester, CompositeElementMapping>(() => new CompositeElementPart(typeof(MappedObject)), x => ((ICompositeElementMappingProvider)x).GetCompositeElementMapping()); } + + protected ModelTester StoredProcedure() + { + return new ModelTester(() => new StoredProcedurePart(null, null), x => x.GetStoredProcedureMapping()); + } } } \ No newline at end of file Index: src/FluentNHibernate.Testing/FluentInterfaceTests/StoredProcedurePartGenerationTests.cs =================================================================== --- src/FluentNHibernate.Testing/FluentInterfaceTests/StoredProcedurePartGenerationTests.cs (revision 0) +++ src/FluentNHibernate.Testing/FluentInterfaceTests/StoredProcedurePartGenerationTests.cs (revision 0) @@ -0,0 +1,29 @@ +using FluentNHibernate.Mapping; +using FluentNHibernate.Testing.DomainModel.Mapping; +using NUnit.Framework; + +namespace FluentNHibernate.Testing.FluentInterfaceTests +{ + [TestFixture] + public class StoredProcedurePartGenerationTests : BaseModelFixture + { + [Test] + public void CheckTypeShouldBeAbleToSetToNone() + { + StoredProcedure() + .Mapping(m => m.Check.None()) + .ModelShouldMatch(x => x.Check.ShouldEqual("none")) + ; + } + + [Test] + public void CheckTypeShouldBeAbleToSetToRowCount() + { + StoredProcedure() + .Mapping(m => m.Check.RowCount()) + .ModelShouldMatch(x => x.Check.ShouldEqual("rowcount")) + ; + } + + } +} \ No newline at end of file Index: src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj =================================================================== --- src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj (revision 647) +++ src/FluentNHibernate.Testing/FluentNHibernate.Testing.csproj (working copy) @@ -138,15 +138,18 @@ + + + Index: src/FluentNHibernate.Testing/MappingModel/ClassMappingTester.cs =================================================================== --- src/FluentNHibernate.Testing/MappingModel/ClassMappingTester.cs (revision 647) +++ src/FluentNHibernate.Testing/MappingModel/ClassMappingTester.cs (working copy) @@ -118,8 +118,30 @@ visitor.VerifyAllExpectations(); } + + [Test] + public void Can_add_stored_procedure() + { + var storedProcedure = new StoredProcedureMapping(); + _classMapping.AddStoredProcedure(storedProcedure); + _classMapping.StoredProcedures.ShouldContain(storedProcedure); + } [Test] + public void Should_pass_stored_procedure_to_the_visitor() + { + var classMap = new ClassMapping { Name = "class1" }; + classMap.AddStoredProcedure(new StoredProcedureMapping()); + + var visitor = MockRepository.GenerateMock(); + visitor.Expect(x => x.Visit(classMap.StoredProcedures.First())); + + classMap.AcceptVisitor(visitor); + + visitor.VerifyAllExpectations(); + } + + [Test] public void Should_pass_the_discriminator_to_the_visitor() { var classMap = new ClassMapping {Name = "class1" }; @@ -132,5 +154,7 @@ visitor.VerifyAllExpectations(); } + + } } \ No newline at end of file Index: src/FluentNHibernate.Testing/MappingModel/Output/XmlStoredProcedureWriterTester.cs =================================================================== --- src/FluentNHibernate.Testing/MappingModel/Output/XmlStoredProcedureWriterTester.cs (revision 0) +++ src/FluentNHibernate.Testing/MappingModel/Output/XmlStoredProcedureWriterTester.cs (revision 0) @@ -0,0 +1,47 @@ +using FluentNHibernate.Mapping; +using FluentNHibernate.MappingModel; +using FluentNHibernate.MappingModel.ClassBased; +using FluentNHibernate.MappingModel.Output; +using FluentNHibernate.Testing.Testing; +using NUnit.Framework; + +namespace FluentNHibernate.Testing.MappingModel.Output +{ + [TestFixture] + public class XmlStoredProcedureWriterTester + { + private IXmlWriter writer; + + [SetUp] + public void GetWriterFromContainer() + { + var container = new XmlWriterContainer(); + writer = container.Resolve>(); + } + + [Test] + public void ShouldWriteSqlUpdate() + { + var mapping = new ClassMapping(); + + mapping.AddStoredProcedure(new StoredProcedureMapping("sql-update", "update ABC")); + + writer.VerifyXml(mapping) + .Element("sql-update").Exists(); + } + + [Test] + public void ShouldWriteCheckAttribute() + { + IXmlWriter writer; + var container = new XmlWriterContainer(); + writer = container.Resolve>(); + + var testHelper = new XmlWriterTestHelper(); + testHelper.Check(x => x.Check, "none").MapsToAttribute("check"); + + testHelper.VerifyAll(writer); + } + + } +} \ No newline at end of file Index: src/FluentNHibernate/FluentNHibernate.csproj =================================================================== --- src/FluentNHibernate/FluentNHibernate.csproj (revision 647) +++ src/FluentNHibernate/FluentNHibernate.csproj (working copy) @@ -365,10 +365,16 @@ + + + + + + @@ -684,4 +690,4 @@ --> - + \ No newline at end of file Index: src/FluentNHibernate/Mapping/CheckTypeExpression.cs =================================================================== --- src/FluentNHibernate/Mapping/CheckTypeExpression.cs (revision 0) +++ src/FluentNHibernate/Mapping/CheckTypeExpression.cs (revision 0) @@ -0,0 +1,28 @@ +using System; + +namespace FluentNHibernate.Mapping +{ + public class CheckTypeExpression + { + internal Action setValue; + + private readonly TParent parent; + + public CheckTypeExpression(TParent parent, Action setter) + { + this.parent = parent; + this.setValue = setter; + } + + + public void None() + { + setValue("none"); + } + + public void RowCount() + { + setValue("rowcount"); + } + } +} \ No newline at end of file Index: src/FluentNHibernate/Mapping/ClasslikeMapBase.cs =================================================================== --- src/FluentNHibernate/Mapping/ClasslikeMapBase.cs (revision 647) +++ src/FluentNHibernate/Mapping/ClasslikeMapBase.cs (working copy) @@ -18,6 +18,7 @@ protected readonly IList references = new List(); protected readonly IList anys = new List(); protected readonly IList filters = new List(); + protected readonly IList storedProcedures = new List(); public PropertyPart Map(Expression> expression) { @@ -270,6 +271,33 @@ return MapHasManyToMany(expression); } + public StoredProcedurePart SqlInsert(string innerText) + { + var part = new StoredProcedurePart("sql-insert", innerText); + + storedProcedures.Add(part); + + return part; + } + + public StoredProcedurePart SqlUpdate(string innerText) + { + var part = new StoredProcedurePart("sql-update", innerText); + + storedProcedures.Add(part); + + return part; + } + + public StoredProcedurePart SqlDelete(string innerText) + { + var part = new StoredProcedurePart("sql-delete", innerText); + + storedProcedures.Add(part); + + return part; + } + protected virtual IEnumerable Properties { get { return properties; } Index: src/FluentNHibernate/Mapping/ClassMap.cs =================================================================== --- src/FluentNHibernate/Mapping/ClassMap.cs (revision 647) +++ src/FluentNHibernate/Mapping/ClassMap.cs (working copy) @@ -93,6 +93,9 @@ foreach (var filter in filters) mapping.AddFilter(filter.GetFilterMapping()); + foreach (var storedProcedure in storedProcedures) + mapping.AddStoredProcedure(storedProcedure.GetStoredProcedureMapping()); + return mapping; } Index: src/FluentNHibernate/Mapping/Providers/IStoredProcedureMappingProvider.cs =================================================================== --- src/FluentNHibernate/Mapping/Providers/IStoredProcedureMappingProvider.cs (revision 0) +++ src/FluentNHibernate/Mapping/Providers/IStoredProcedureMappingProvider.cs (revision 0) @@ -0,0 +1,9 @@ +using FluentNHibernate.MappingModel; + +namespace FluentNHibernate.Mapping.Providers +{ + public interface IStoredProcedureMappingProvider + { + StoredProcedureMapping GetStoredProcedureMapping(); + } +} \ No newline at end of file Index: src/FluentNHibernate/Mapping/StoredProcedurePart.cs =================================================================== --- src/FluentNHibernate/Mapping/StoredProcedurePart.cs (revision 0) +++ src/FluentNHibernate/Mapping/StoredProcedurePart.cs (revision 0) @@ -0,0 +1,42 @@ +using FluentNHibernate.Mapping.Providers; +using FluentNHibernate.MappingModel; + +namespace FluentNHibernate.Mapping +{ + public class StoredProcedurePart : IStoredProcedureMappingProvider + { + private readonly CheckTypeExpression check; + private readonly string _element; + private readonly string _innerText; + private readonly AttributeStore attributes = new AttributeStore(); + + + public StoredProcedurePart(string element, string innerText) + { + _element = element; + _innerText = innerText; + + check = new CheckTypeExpression(this, value => attributes.Set(x => x.Check, value)); + } + + + public CheckTypeExpression Check + { + get { return check; } + } + + + public StoredProcedureMapping GetStoredProcedureMapping() + { + var mapping = new StoredProcedureMapping(attributes.CloneInner()); + + mapping.SPType = _element; + mapping.Query = _innerText; + + if (!mapping.IsSpecified("Check")) + mapping.SetDefaultValue(x => x.Check, "rowcount"); + + return mapping; + } + } +} \ No newline at end of file Index: src/FluentNHibernate/MappingModel/ClassBased/ClassMappingBase.cs =================================================================== --- src/FluentNHibernate/MappingModel/ClassBased/ClassMappingBase.cs (revision 647) +++ src/FluentNHibernate/MappingModel/ClassBased/ClassMappingBase.cs (working copy) @@ -73,6 +73,11 @@ get { return subclasses; } } + public IEnumerable StoredProcedures + { + get { return mappedMembers.StoredProcedures; } + } + public void AddProperty(PropertyMapping property) { mappedMembers.AddProperty(property); @@ -148,6 +153,10 @@ subclasses.Add(subclass); } + public void AddStoredProcedure(StoredProcedureMapping mapping) + { + mappedMembers.AddStoredProcedure(mapping); + } #endregion public override string ToString() Index: src/FluentNHibernate/MappingModel/IMappingModelVisitor.cs =================================================================== --- src/FluentNHibernate/MappingModel/IMappingModelVisitor.cs (revision 647) +++ src/FluentNHibernate/MappingModel/IMappingModelVisitor.cs (working copy) @@ -45,6 +45,7 @@ void ProcessArray(ArrayMapping mapping); void ProcessFilter(FilterMapping mapping); void ProcessFilterDefinition(FilterDefinitionMapping mapping); + void ProcessStoredProcedure(StoredProcedureMapping mapping); void Visit(IdMapping mapping); void Visit(ClassMapping classMapping); @@ -79,5 +80,6 @@ void Visit(ArrayMapping mapping); void Visit(FilterMapping mapping); void Visit(FilterDefinitionMapping mapping); + void Visit(StoredProcedureMapping mapping); } } Index: src/FluentNHibernate/MappingModel/MappedMembers.cs =================================================================== --- src/FluentNHibernate/MappingModel/MappedMembers.cs (revision 647) +++ src/FluentNHibernate/MappingModel/MappedMembers.cs (working copy) @@ -15,6 +15,7 @@ private readonly List anys; private readonly List joins; private readonly List filters; + private readonly List storedProcedures; public MappedMembers() { @@ -26,6 +27,7 @@ anys = new List(); joins = new List(); filters = new List(); + storedProcedures = new List(); } public IEnumerable Properties @@ -68,6 +70,11 @@ get { return filters; } } + public IEnumerable StoredProcedures + { + get { return storedProcedures; } + } + public void AddProperty(PropertyMapping property) { if (properties.Exists(x => x.Name == property.Name)) @@ -193,11 +200,19 @@ foreach (var filter in filters) visitor.Visit(filter); + + foreach (var storedProcedure in storedProcedures) + visitor.Visit(storedProcedure); } public bool IsSpecified(string property) { return false; } + + public void AddStoredProcedure(StoredProcedureMapping mapping) + { + storedProcedures.Add(mapping); + } } } Index: src/FluentNHibernate/MappingModel/NullMappingModelVisitor.cs =================================================================== --- src/FluentNHibernate/MappingModel/NullMappingModelVisitor.cs (revision 647) +++ src/FluentNHibernate/MappingModel/NullMappingModelVisitor.cs (working copy) @@ -182,6 +182,11 @@ } + public virtual void ProcessStoredProcedure(StoredProcedureMapping mapping) + { + + } + public virtual void ProcessIndex(IIndexMapping indexMapping) { @@ -365,5 +370,10 @@ { } + + public virtual void Visit(StoredProcedureMapping mapping) + { + + } } } Index: src/FluentNHibernate/MappingModel/Output/Sorting/XmlClasslikeNodeSorter.cs =================================================================== --- src/FluentNHibernate/MappingModel/Output/Sorting/XmlClasslikeNodeSorter.cs (revision 647) +++ src/FluentNHibernate/MappingModel/Output/Sorting/XmlClasslikeNodeSorter.cs (working copy) @@ -30,7 +30,10 @@ { "subclass", new SortValue { Position = Last, Level = 3 } }, { "join", new SortValue { Position = Last, Level = 3 } }, { "any", new SortValue { Position = Anywhere, Level = 2 } }, - { "filter", new SortValue { Position = Last, Level = 5 } } + { "filter", new SortValue { Position = Last, Level = 5 } }, + { "sql-insert", new SortValue { Position = Last, Level = 5 } }, + { "sql-update", new SortValue { Position = Last, Level = 5 } }, + { "sql-delete", new SortValue { Position = Last, Level = 5 } }, }; } Index: src/FluentNHibernate/MappingModel/Output/XmlClassWriterBase.cs =================================================================== --- src/FluentNHibernate/MappingModel/Output/XmlClassWriterBase.cs (revision 647) +++ src/FluentNHibernate/MappingModel/Output/XmlClassWriterBase.cs (working copy) @@ -61,5 +61,13 @@ document.ImportAndAppendChild(xml); } + + public override void Visit(StoredProcedureMapping mapping) + { + var writer = serviceLocator.GetWriter(); + var xml = writer.Write(mapping); + + document.ImportAndAppendChild(xml); + } } } \ No newline at end of file Index: src/FluentNHibernate/MappingModel/Output/XmlStoredProcedureWriter.cs =================================================================== --- src/FluentNHibernate/MappingModel/Output/XmlStoredProcedureWriter.cs (revision 0) +++ src/FluentNHibernate/MappingModel/Output/XmlStoredProcedureWriter.cs (revision 0) @@ -0,0 +1,40 @@ +using System.Xml; +using FluentNHibernate.Utils; + +namespace FluentNHibernate.MappingModel.Output +{ + public class XmlStoredProcedureWriter : XmlClassWriterBase, IXmlWriter + { + private readonly IXmlWriterServiceLocator serviceLocator; + + public XmlStoredProcedureWriter(IXmlWriterServiceLocator serviceLocator) : base(serviceLocator) + { + this.serviceLocator = serviceLocator; + } + + public XmlDocument Write(StoredProcedureMapping mappingModel) + { + document = null; + mappingModel.AcceptVisitor(this); + return document; + } + + public override void ProcessStoredProcedure(StoredProcedureMapping mapping) + { + document = new XmlDocument(); + + var element = document.AddElement(mapping.SPType); + element.WithAtt("check", mapping.Check); + element.InnerXml = mapping.Query; + + } + + public override void Visit(StoredProcedureMapping mapping) + { + var writer = serviceLocator.GetWriter(); + var xml = writer.Write(mapping); + + document.ImportAndAppendChild(xml); + } + } +} \ No newline at end of file Index: src/FluentNHibernate/MappingModel/Output/XmlWriterContainer.cs =================================================================== --- src/FluentNHibernate/MappingModel/Output/XmlWriterContainer.cs (revision 647) +++ src/FluentNHibernate/MappingModel/Output/XmlWriterContainer.cs (working copy) @@ -120,6 +120,9 @@ RegisterWriter(c => new XmlFilterDefinitionWriter()); + + RegisterWriter(c => + new XmlStoredProcedureWriter(c.Resolve())); } private void RegisterIdWriters() Index: src/FluentNHibernate/MappingModel/StoredProcedureMapping.cs =================================================================== --- src/FluentNHibernate/MappingModel/StoredProcedureMapping.cs (revision 0) +++ src/FluentNHibernate/MappingModel/StoredProcedureMapping.cs (revision 0) @@ -0,0 +1,88 @@ +using System; +using System.Linq.Expressions; +using FluentNHibernate.Mapping; +using FluentNHibernate.MappingModel.ClassBased; + +namespace FluentNHibernate.MappingModel +{ + public class StoredProcedureMapping : ClassMappingBase + { + private readonly AttributeStore attributes; + + public StoredProcedureMapping() : this("sql-insert", "") + { + } + + public StoredProcedureMapping(AttributeStore attributes) + { + this.attributes = new AttributeStore(attributes); + } + + public StoredProcedureMapping(string spType, string innerText): this(spType, innerText, new AttributeStore()) + { + } + + public StoredProcedureMapping(string spType, string innerText, AttributeStore underlyingStore) + { + attributes = new AttributeStore(underlyingStore); + SPType = spType; + Query = innerText; + Check = "none"; + + } + + public override string Name + { + get { return attributes.Get(x => x.Name); } + set { attributes.Set(x => x.Name, value); } + } + + public override Type Type + { + get { return attributes.Get(x => x.Type); } + set { attributes.Set(x => x.Type, value); } + } + + public override void AcceptVisitor(IMappingModelVisitor visitor) + { + visitor.ProcessStoredProcedure(this); + } + + public override void MergeAttributes(AttributeStore store) + { + attributes.Merge(new AttributeStore(store)); + } + + public override bool IsSpecified(string property) + { + return attributes.IsSpecified(property); + } + + public string Check + { + get { return attributes.Get(x => x.Check); } + set { attributes.Set(x => x.Check, value); } + } + + public string SPType + { + get { return attributes.Get(x => x.SPType); } + set { attributes.Set(x => x.SPType, value); } + } + + public string Query + { + get { return attributes.Get(x => x.Query); } + set { attributes.Set(x => x.Query, value); } + } + + public void SetDefaultValue(Expression> property, TResult value) + { + attributes.SetDefault(property, value); + } + } + + + + +} \ No newline at end of file Index: src/FluentNHibernate/SqlInsert.cs =================================================================== --- src/FluentNHibernate/SqlInsert.cs (revision 0) +++ src/FluentNHibernate/SqlInsert.cs (revision 0) @@ -0,0 +1,13 @@ +using FluentNHibernate.Mapping; + +namespace FluentNHibernate +{ + public class SqlInsert : StoredProcedurePart + { + public SqlInsert(string innerText) : base("sql-insert", innerText) + { + } + + + } +} \ No newline at end of file