Using a mapped class as a key in a dictionary with a string as a value

Matt's Avatar

Matt

27 Sep, 2010 03:14 PM via web

Hi,
I've dug around a bit but haven't been able to find an answer.
I'm building a reporting tool using FNH. I have a reporting class, which has a many-to-many relationship to another class, parameters.
The problem is that I want to map runtime only information with each of the parameters in the reporting class, there is no need or desire to save this information in the database. I am trying to achieve this by using a dictionary in the reporting class that takes parameters as a key and a string as a value.

public class Parameter
 {
       public virtual Guid ParameterID { get; private set; }
       public virtual String Name { get; set; }
       public virtual IList<Report> Reports { get; set; }

       public Parameter()
       {
            Reports = new List<Report>();
       }

}

public class Report
{
    public virtual Guid ReportID { get; private set; }
    public virtual String Name { get; set; }
    public virtual String Description { get; set; }
    public virtual IDictionary<Parameter,string> Parameters { get; set; }

    public Report()
    {
        Parameters = new Dictionary<Parameter, string>();
    }

    public virtual void AddParameter(Parameter p, string value)
    {

        Parameters.Add(p,value);
        p.Reports.Add(this);

    }
}

I can't for the life of me figure out how to get this to map in the reportMapping class. I've tried all sorts of combinations of the extension methods for HasManyToMany. All I need is simple table Report_Parameters with two fields, ReportID and ParameterID that I can use to build a dictionary<parameter,string>, where the strings are initially null, or later loaded from a new field in Report_Parameters

public class ReportMapping : ClassMap<Report>
{
    public ReportMapping()
    {
        Id(x => x.ReportID);
        Map(x => x.Name);
        Map(x => x.Description);

        HasManyToMany(x => x.Parameters)
            /*what should go here?*/
            .Table("Report_Parameters")
            .Cascade.All();

    }
}

public class ParameterMapping : ClassMap<Parameter>
{
    public ParameterMapping()
    {
        Id(x => x.ParameterID);
        Map(x => x.Name);
        HasManyToMany(x => x.Reports)
            .Inverse()
            .Table("Report_Parameters");
    }
}

Thanks,
Matt

  1. 2 Posted by matt on 28 Sep, 2010 10:21 AM

    matt's Avatar

    I've changed the design of the domain to include default_value in Report_Parameters, as there's a strong case for needing this.

    ReportMapping now looks like this:

    public class ReportMapping : ClassMap<Report>
    {
        public ReportMapping()
        {
            Id(x => x.ReportID);
            Map(x => x.Name);
            Map(x => x.Description);
    
            HasManyToMany<Parameter>(x => x.Parameters)
                .ParentKeyColumn("ReportID")
                .ChildKeyColumn("ParameterID")
                .AsMap<String>("Default_Value")
                .Table("Report_Parameters");
    
        }
    }

    And ParameterMapping looks like this

    public class ParameterMapping : ClassMap<Parameter>
    {
        public ParameterMapping()
        {
            Id(x => x.ParameterID);
            Map(x => x.Name);
            HasManyToMany(x => x.Reports)
                .Table("Report_Parameters")
                .ChildKeyColumn("ParameterID")
                .ParentKeyColumn("ReportID")
                .Inverse();
    
        }
    }

    This gives me the Schema:

    create table "Parameter" (
        ParameterID UNIQUEIDENTIFIER not null,
       Name TEXT,
       primary key (ParameterID)
    )
    
    create table Report_Parameters (
        ReportID UNIQUEIDENTIFIER not null,
       ParameterID UNIQUEIDENTIFIER not null,
       Default_Value TEXT not null,
       primary key (ReportID, Default_Value)
    )
    
    create table "Report" (
        ReportID UNIQUEIDENTIFIER not null,
       Name TEXT,
       Description TEXT,
       primary key (ReportID)
    )

    which, with the exception of the primary key on Report_Parameters.Default_Value is what I want.

    However on committing data in some test code I get a propertyAccessException "Object does not match target type." at "Exception occurred getter of TestMappings.Entities.Parameter.ParameterID" .

    using (var transaction = session.BeginTransaction())

                {
                    Report r = new Report { Name = "Test", Description = "Test" };
                    Parameter p = new Parameter { Name = "test P"};
    
    
                    r.AddParameter(p, "test");
    
                    session.SaveOrUpdate(r);
    
                    transaction.Commit();
                }

    I'm using an sqllite database (will eventually be oracle) and v1.1 of fluent. I've tried removing the mapping on the parameter side and changing the order of the tables, this error is always thrown.

    Thanks in advance,

    Matt

  2. 3 Posted by Matt on 28 Sep, 2010 12:37 PM

    Matt's Avatar

    Got it working, looks like I was confused about the key/value types in my mapping. My mapping is now:

    HasManyToMany(x => x.Parameters)
                .AsMap(null)
                .AsTernaryAssociation()
                .ParentKeyColumn("ReportID")
                .ChildKeyColumn("ParameterID")
                .Element("Default_Value", e => e.Type<String>())
                .Table("Report_Parameters")
                .Cascade.All();

    This works great if I use it like this:

    Report r = new Report { Name = "Test report", Description = "A Test report" };
    Parameter p = new Parameter { Name = "test P"};
    
    r.Parameters.Add( new Parameter { Name = "asdada" },"1st");
    r.Parameters.Add( p,"test");
    session.SaveOrUpdate(p); 
    session.SaveOrUpdate(r);

    If I try creating a new parameter as such:

    r.Parameters.Add( new Parameter { Name = "asdada" },"1st");

    It complains about a transient state needing flushing, however I don't think the need to instantiate a new parameter on adding to the collection will arise.

    Matt

Reply to this discussion

Preview Comments are parsed with Markdown. Help with syntax

Attached Files

    You can attach files up to 10MB

    Ten divided by two is what?