views:

753

answers:

3

I want to do exactly what this question asks: http://stackoverflow.com/questions/586888/cascade-saves-with-fluent-nhibernate-automapping

Using Fluent Nhibernate Mappings to turn on "cascade" globally once for all classes and relation types using one call rather than setting it for each mapping individually.

The answer to the earlier question looks great, but I'm afraid that the Fluent Nhibernate API altered its .WithConvention syntax last year and broke the answer... either that or I'm missing something.

I keep getting a bunch of name space not found errors relating to the IOneToOnePart, IManyToOnePart and all their variations:

"The type or namespace name 'IOneToOnePart' could not be found (are you missing a using directive or an assembly reference?)"

I've tried the official example dll's, the RTM dll's and the latest build and none of them seem to make VS 2008 see the required namespace.

The second problem is that I want to use the class with my AutoPersistenceModel but I'm not sure where to this line: .ConventionDiscovery.AddFromAssemblyOf() in my factory creation method.

 private static ISessionFactory CreateSessionFactory()
            {

                return Fluently.Configure()
                  .Database(SQLiteConfiguration.Standard.UsingFile(DbFile))
                  .Mappings(m => m.AutoMappings
                        .Add(AutoMap.AssemblyOf<Shelf>(type => type.Namespace.EndsWith("Entities"))
                                .Override<Shelf>(map =>
                                {
                                    map.HasManyToMany(x => x.Products).Cascade.All();
                                })
                             )

                      )//emd mappings
                .ExposeConfiguration(BuildSchema)
                .BuildSessionFactory();//finalizes the whole thing to send back. 

            }

Below is the class and using statements I'm trying

   using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;

    using FluentNHibernate.Conventions;
    using FluentNHibernate.Cfg;
    using FluentNHibernate.Cfg.Db;
    using NHibernate;
    using NHibernate.Cfg;
    using NHibernate.Tool.hbm2ddl;
    using FluentNHibernate.Mapping;


    namespace TestCode
    {
        public class CascadeAll : IHasOneConvention, IHasManyConvention, IReferenceConvention
        {
            public bool Accept(IOneToOnePart target)
            {
                return true;
            }

            public void Apply(IOneToOnePart target)
            {
                target.Cascade.All();
            }

            public bool Accept(IOneToManyPart target)
            {
                return true;
            }

            public void Apply(IOneToManyPart target)
            {
                target.Cascade.All();
            }

            public bool Accept(IManyToOnePart target)
            {
                return true;
            }

            public void Apply(IManyToOnePart target)
            {
                target.Cascade.All();
            }
        }

    }
+1  A: 

The signature for the conventions has changed. Are you not using something like ReSharper? That would point you to that conclusion.

You can read more about the new conventions on the wiki.

James Gregory
James,Thanks for all your great work on Fluent Nhibernate. I'm about a day into implementing it on a new project and I'm loving your examples. The code (answer below) runs without errors and puts the expected values in the db. Does my approach make sense?
Glenn
+1  A: 

Here's a full working example similar to the Getting Started guide http://wiki.fluentnhibernate.org/Getting_started

    //=====CONSOLE MAIN
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    using FluentNHibernate.Cfg;
    using FluentNHibernate.Cfg.Db;
    using NHibernate;
    using NHibernate.Cfg;
    using NHibernate.Tool.hbm2ddl;
    using System.IO;
    using FluentNHibernate.Automapping;
    using App4.Entities;
    using System.Diagnostics;

    namespace App4
    {
        class Program
        {
            static void Main(string[] args)
            {
                // create our NHibernate session factory
                var sessionFactory = CreateSessionFactory();

                using (var session = sessionFactory.OpenSession())
                {
                    // populate the database
                    using (var transaction = session.BeginTransaction())
                    {
                        // create a couple of Stores each with some Products and Employees
                        var topShelf = new Shelf();
                        var sw = new Stopwatch();
                        sw.Start();
                        for (var i = 0; i < 1000; i++)
                        {
                            var potatoes = new Product { Name = "Potatoes" + i.ToString(), Price = 3.60 + i };
                            var meat = new Product { Name = "Meat" + i.ToString(), Price = 4.49 + i };
                            //session.SaveOrUpdate(potatoes); //===<<cascading save handles this :-)
                            //session.SaveOrUpdate(meat);
                            topShelf.Products.Add(meat);
                            topShelf.Products.Add(potatoes);
                        }
                        sw.Stop();

                        session.SaveOrUpdate(topShelf);
                        //session.SaveOrUpdate(superMart);
                        transaction.Commit();

                        Console.WriteLine("Add Items: " + sw.ElapsedMilliseconds);
                    }
                }

                using (var session = sessionFactory.OpenSession())
                {
                    // retreive all stores and display them
                    using (session.BeginTransaction())
                    {
                        var shelves = session.CreateCriteria(typeof(Shelf)).List<Shelf>();

                        foreach (var store in shelves)
                        {
                            WriteShelfPretty(store);
                        }
                    }
                }

                Console.ReadLine();
            }

            private const string DbFile = "FIVEProgram.db";
            private static ISessionFactory CreateSessionFactory()
            {
                return Fluently.Configure()
                  .Database(SQLiteConfiguration.Standard.UsingFile(DbFile))
                  .Mappings(m => m.AutoMappings
                        .Add(AutoMap.AssemblyOf<Shelf>(type => type.Namespace.EndsWith("Entities"))
                                .Override<Shelf>(map =>
                                {
                                    map.HasManyToMany(x => x.Products);//.Cascade.All();
                                })
                                .Conventions.AddFromAssemblyOf<CascadeAll>()
                             )

                      ) //emd mappings
                .ExposeConfiguration(BuildSchema)//Delete and remake db (see function below)
                .BuildSessionFactory();//finalizes the whole thing to send back. 

            }

            private static void BuildSchema(Configuration config)
            {
                // delete the existing db on each run
                if (File.Exists(DbFile))
                    File.Delete(DbFile);

                // this NHibernate tool takes a configuration (with mapping info in)
                // and exports a database schema from it
                new SchemaExport(config)
                    .Create(false, true);
            }

            private static void WriteShelfPretty(Shelf shelf)
            {
                Console.WriteLine(shelf.Id);
                Console.WriteLine("  Products:");

                foreach (var product in shelf.Products)
                {
                    Console.WriteLine("    " + product.Name);
                }

                Console.WriteLine();
            }

        }



    }


//Data Classes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace App4.Entities 
{
    public class Product
    {
        public virtual int Id { get; private set; }
        public virtual string Name { get; set; }
        public virtual double Price { get; set; }
    }

    public class Shelf
    {
        public virtual int Id { get; private set; }
        public virtual IList<Product> Products { get; private set; }

        public Shelf()
        {
            Products = new List<Product>();
        }
    }
}



//Cascade All Helper Class
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.AcceptanceCriteria;
using FluentNHibernate.Conventions.Inspections;
using FluentNHibernate.Conventions.Instances;
using System;
using System.Collections.Generic;


namespace App4
{
    public class CascadeAll : 
        IHasOneConvention, //Actually Apply the convention
        IHasManyConvention, 
        IReferenceConvention, 
        IHasManyToManyConvention,

        IHasOneConventionAcceptance, //Test to see if we should use the convention
        IHasManyConventionAcceptance, //I think we could skip these since it will always be true
        IReferenceConventionAcceptance, //adding them for reference later
        IHasManyToManyConventionAcceptance
    {

        //One to One

        public void Accept(IAcceptanceCriteria<IOneToOneInspector> criteria)
        {
            //criteria.Expect(x => (true));
        }

        public void Apply(IOneToOneInstance instance)
        {
            instance.Cascade.All();
        }




        //One to Many

        public void Accept(IAcceptanceCriteria<IOneToManyCollectionInspector> criteria)
        {
            //criteria.Expect(x => (true));
        }

        public void Apply(IOneToManyCollectionInstance instance)
        {
            instance.Cascade.All();
        }




        //Many to One

        public void Accept(IAcceptanceCriteria<IManyToOneInspector> criteria)
        {
           // criteria.Expect(x => (true));
        }

        public void Apply(IManyToOneInstance instance)
        {
            instance.Cascade.All();
        }





        //Many to Many

        public void Accept(IAcceptanceCriteria<IManyToManyCollectionInspector> criteria)
        {
           // criteria.Expect(x => (true));
        }

        public void Apply(IManyToManyCollectionInstance instance)
        {
            instance.Cascade.All();
        }



    }


}
Glenn
Looks good! The Acceptance interfaces don't need to be implemented if you aren't using the criteria, but I gather you know that already based on your comments.
James Gregory
It's nice to have the flexibility of this method and the simplicity of the method Tom mentioned below for the normal case. I'm really looking forward to the easy maintenance Fluent will bring. The speed is great too, I get thousands of inserts per second using disk bound slq lite with this test code in debug mode.
Glenn
+5  A: 

The easiest way I've found to do this for a whole project is to use DefaultCascade:

.Conventions.Add( DefaultCascade.All() );     

Go to Convention Shortcut section "Even Shorter Shortcuts" on the wiki, for this, and a list of others.

Edit: Here's the list from the Wiki:

Table.Is(x => x.EntityType.Name + "Table")
PrimaryKey.Name.Is(x => "ID")
AutoImport.Never()
DefaultAccess.Field()
DefaultCascade.All()
DefaultLazy.Always()
DynamicInsert.AlwaysTrue()
DynamicUpdate.AlwaysTrue()
OptimisticLock.Is(x => x.Dirty())
Cache.Is(x => x.AsReadOnly())
ForeignKey.EndsWith("ID")

A word of warning - some of the method names in the Wiki may be wrong. I edited the Wiki with what I could verify (i.e. DefaultCascade and DefaultLazy), but can't vouch for the rest. But you should be able to figure out the proper names with Intellisense if the need arises.

Tom Bushell
Thanks tom. That's a really easy solution. Definitely less code than my way :-)
Glenn
Glad you like it - I certainly did when they were pointed out to me on the mailing list! I just did a major edit on the Wiki section http://wiki.fluentnhibernate.org/Conventions to bring these to attention of more people using FNH.
Tom Bushell