nhibernate / fluent-nhibernate

Fluent NHibernate!
BSD 3-Clause "New" or "Revised" License
1.66k stars 686 forks source link

Automapping a Tree Model with with FluentNhibernate #242

Open haashem opened 11 years ago

haashem commented 11 years ago

I have a tree structured model and designed it with composite Pattern. for iterating through the entire hierachy Im using Composite Iteration. here is the source code:

http://www.mediafire.com/download/v78f97mt51y27j6/Fluent-NHibernate-MVC-3_Combat.rar

but when I want to AutoMap the model, I encounter this problem:

{"The entity '<GetEnumerator>d__0' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)."}

but getEnumerator is a method. I don't know why handle this like an Entity!!

public IEnumerator<CombatElement> GetEnumerator()
        {
             foreach (CombatElement child in combatElements)
                yield return this;
        }

here is my AutoMapping Configuration :

public class AutomappingConfiguration: DefaultAutomappingConfiguration
    {
        //As we do not explicitly map entities or value objects, we define conventions or exceptions 
        //for the AutoMapper. We do this by implementing a configuration class.

        //this method instructs the AutoMapper to consider only those classes for mapping 
        //which reside in the same namespace as the Employeeentity.
        public override bool ShouldMap(Type type)
        {
           return type.Namespace == typeof(CombatElement).Namespace;

        }

    }

Uploaded the sample code:

public abstract class CombatElement
{
    public virtual string Name { get; set; }
    public virtual Guid Id { get; set; }

    public virtual void Add(
        CombatElement element)
    {
        throw new NotImplementedException();
    }

    public virtual void
        Remove(CombatElement element)
    {
        throw new NotImplementedException();
    }

    public virtual
        IEnumerable<CombatElement>
            GetElements()
    {
        throw new NotImplementedException();
    }

    public abstract void Fight();
    public abstract void Move();
}

//////

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.Diagnostics;

namespace FluentNHibernateMvc3.Models

{
    public class Formation : CombatElement
    {
        private List<CombatElement> _elements;
        public virtual IEnumerable<CombatElement> Elements { get { return _elements; } }

        public Formation()
    {
        _elements = new List<CombatElement>();
    }

    public override void Add(
        CombatElement element)
    {
        _elements.Add(element);
    }

    public override void
        Remove(CombatElement element)
    {
        _elements.Remove(element);
    }

    public override void Fight()
    {
        Debug.WriteLine(this.Name + " Formation is fighting");
    }

    public override void Move()
    {
        Debug.WriteLine(this.Name + " Formation is moving");
    }

    public override
        IEnumerable<CombatElement>
            GetElements()
    {
        // yield up this current object first
        yield return this;

        // iterate through all child elements
        foreach (CombatElement fe in
            _elements)
        {
            // + iterate through each of its elements
            foreach (CombatElement feInner
                    in fe.GetElements())
                yield return feInner;
        }
    }
}

}

/////////

public class Soldier : CombatElement
{
    public virtual int Rank { get; set; }

    public override void Fight()
    {
        Debug.WriteLine(this.Name + " soldier is fighting");
    }

    public override void Move()
    {
        Debug.WriteLine(this.Name + " soldier is fighting");
    }

    public override
        IEnumerable<CombatElement>
            GetElements()
    {
        yield return this;
    }
}

and here how I create session factory

 // Returns our session factory
    private static ISessionFactory CreateSessionFactory()
    {
        //m => m.FluentMappings.AddFromAssemblyOf<FormationMap>()
        return Fluently.Configure()
            .Database( CreateDbConfig )
            .Mappings(m => m.AutoMappings.Add(CreateMappings()))
            .ExposeConfiguration( UpdateSchema )
            .CurrentSessionContext<WebSessionContext>()
            .BuildSessionFactory();
    }

    // Returns our database configuration
    private static MsSqlConfiguration CreateDbConfig()
    {
        return MsSqlConfiguration
            .MsSql2008
            .ConnectionString( c => c.FromConnectionStringWithKey( "testConn" ) );
    }

    // Returns our mappings
    private static AutoPersistenceModel CreateMappings()
    {
        var cfg = new AutomappingConfiguration();
        return AutoMap
            .Assemblies(cfg,System.Reflection.Assembly.GetCallingAssembly()).IncludeBase<CombatElement>()
            .Conventions.Setup( c => c.Add( DefaultCascade.SaveUpdate() ) );
    }

    // Updates the database schema if there are any changes to the model,
    // or drops and creates it if it doesn't exist
    private static void UpdateSchema( Configuration cfg )
    {
        new SchemaUpdate( cfg )
            .Execute( false, true );
    }

Does anyone has any idea?

chester89 commented 11 years ago

I'll take a look. First of all - are you sure that "yield return this" makes sense?

chester89 commented 11 years ago

Can you try replacing this line: .Mappings(m => m.AutoMappings.Add(CreateMappings())) for these ones: .Mappings(m => { m.FluentMappings.AddFromAssemblyOf().Conventions.Add(DefaultCascade.SaveUpdate()); }) ? After I did that, your error went away - but I can't check further as I don't have Sql Server configured.

chester89 commented 11 years ago

Markdown ate template argument - I meant AddFromAssemblyOf[SoldierMap]

haashem commented 11 years ago

Soldier is a combat element and when I iterating through combat element if that element is a soldier it should return soldier, so yield return this is correct. as you mentioned if I use fluent mapping it works well without any error.but when I use automapping I encounter that strange problem. I'm confused how to solve that. I want to solve it with automapping

chester89 commented 11 years ago

The reason for an error is that Fluent thinks you didn't provide an Id mapping for Formation type - even after I said that PK is in the Id property

haashem commented 11 years ago

would you explain more? what should I do?

chester89 commented 11 years ago

I didn't get the code working yet - will try again today