1

成功使示例程序运行后,我现在开始使用 Fluent NHibernate 进行实际工作 - 尝试在我的项目的类层次结构上使用 Automapping。

这是一个科学仪器应用程序,我正在映射的类有几个属性是浮点数组,例如

    private float[] _rawY; 
    public virtual float[] RawY 
    { 
        get 
        { 
            return _rawY; 
        } 
        set 
        { 
            _rawY = value; 
        } 
    } 

这些数组最多可以包含 500 个值。

我没想到 Automapping 可以在数组上工作,但无论如何都尝试过,起初取得了一些成功。每个数组都自动映射到一个 BLOB(使用 SQLite),这似乎是一个可行的解决方案。

当我尝试对包含数组的对象调用 SaveOrUpdate 时,第一个问题出现了 - 我得到了“No persister for float[]”异常。

所以我的下一个想法是将我所有的数组转换为 ILists,例如

public virtual IList<float> RawY { get; set; } 

但现在我得到:

NHibernate.MappingException: Association references unmapped class: System.Single 

因为 Automapping 可以处理复杂对象的列表,所以我从来没有想过它不能映射基本类型的列表。但是在进行了一些谷歌搜索以寻求解决方案之后,似乎就是这种情况。有些人似乎已经解决了这个问题,但是我看到的示例代码比我现在需要更多的 NHibernate 知识——我不明白。

问题:

1. 如何使用 Automapping 进行这项工作?

2. 另外,这个应用程序使用数组还是列表更好?

如有必要,我可以修改我的应用程序以使用其中任何一个(尽管我更喜欢列表)。

编辑:

我研究了Mapping Collection of Strings中的代码,我看到源代码中有设置字符串 IList 的测试代码,例如

public virtual IList<string> ListOfSimpleChildren { get; set; }

[Test] 
public void CanSetAsElement() 
{ 
    new MappingTester<OneToManyTarget>() 
        .ForMapping(m => m.HasMany(x => x.ListOfSimpleChildren).Element("columnName")) 
        .Element("class/bag/element").Exists(); 
} 

so this must be possible using pure Automapping, but I've had zero luck getting anything to work, probably because I don't have the requisite knowlege of manually mapping with NHibernate.

Starting to think I'm going to have to hack this (by encoding the array of floats as a single string, or creating a class that contains a single float which I then aggregate into my lists), unless someone can tell me how to do it properly.

End Edit

Here's my CreateSessionFactory method, if that helps formulate a reply...

    private static ISessionFactory CreateSessionFactory() 
    { 
        ISessionFactory sessionFactory = null; 


        const string autoMapExportDir = "AutoMapExport"; 
        if( !Directory.Exists(autoMapExportDir) ) 
            Directory.CreateDirectory(autoMapExportDir); 


        try 
        { 
            var autoPersistenceModel = 
                AutoMap.AssemblyOf<DlsAppOverlordExportRunData>() 
                       .Where(t => t.Namespace == "DlsAppAutomapped") 
                       .Conventions.Add( DefaultCascade.All() ) 
                ; 


            sessionFactory = Fluently.Configure() 
                .Database(SQLiteConfiguration.Standard 
                              .UsingFile(DbFile) 
                              .ShowSql() 
                         ) 
                .Mappings(m => m.AutoMappings.Add(autoPersistenceModel) 
                                             .ExportTo(autoMapExportDir) 
                         ) 
                .ExposeConfiguration(BuildSchema) 
                .BuildSessionFactory() 
                ; 
        } 
        catch (Exception e) 
        { 
            Debug.WriteLine(e); 
        } 


        return sessionFactory; 
    } 
4

4 回答 4

2

I would probably do a one to many relationship and make the list another table...

But maybe you need to rethink your object, is there also a RawX that you could compose into a RawPoint? This would make a table with 3 columns (ParentID, X, Y).

The discontinuity comes from wanting to map a List to a value that in an RDBMS won't go in a column very neatly. A table is really the method that they use to store Lists of data.

This is the whole point of using an ORM like NHibernate. When doing all the querying and SQL composition by hand in your application, adding a table had a high cost in maintenance and implementation. With NHibernate the cost is nearly 0, so take advantage of the strengths of the RDBMS and let NHibernate abstract the ugliness away.


I see your problem with mapping the array, try it with an override mapping first and see if it will work, then you could maybe create a convention override if you want the automap to work.

.Override<MyType>(map =>
{
    map.HasMany(x => x.RawY).AsList();
})

Not sure if that will work, I need to get an nHibernate testing setup configured for this stuff.

于 2009-11-06T22:14:37.143 回答
2

Since I posted my question, the Fluent NHibernate team have fixed this problem.

You can now automap ILists of C# value types (strings, ints, floats, etc).

Just make sure you have a recent version of FNH.

Edit

I recently upgraded from FNH 1.0 to FNH 1.3.

This version will also automap arrays - float[], int[], etc.

Seems to map them as BLOBs. I assume this will be more efficient than ILists, but have not done any profiling to confirm.

于 2010-03-25T20:17:39.240 回答
1

I eventually got an override to work - see the end of the code listing. The key points are:

  • a new mapping class called DlsAppOverlordExportRunDataMap
  • the addition of a UseOverridesFromAssemblyOf clause in CreateSessionFactory

Also, it turns out that (at least with v. 1.0.0.594) there is a very big gotcha with Automapping - the mapping class (e.g. DlsAppOverlordExportRunDataMap) cannot be in the same Namespace as the domain class (e.g. DlsAppOverlordExportRunData)!

Otherwise, NHibernate will throw "NHibernate.MappingException: (XmlDocument)(2,4): XML validation error: ..." , with absolutely no indication of what or where the real problem is.

This is probably a bug, and may be fixed in later versions of Fluent NHibernate.

namespace DlsAppAutomapped
{
    public class DlsAppOverlordExportRunData
    {
        public virtual int Id { get; set; }

        // Note: List<float> needs overrides in order to be mapped by NHibernate. 
        // See class DlsAppOverlordExportRunDataMap.
        public virtual IList<float> RawY { get; set; } 
    }
}


namespace FrontEnd
{
    // NEW - SET UP THE OVERRIDES
    // Must be in different namespace from DlsAppOverlordExportRunData!!!
    public class DlsAppOverlordExportRunDataMap : IAutoMappingOverride<DlsAppOverlordExportRunData>
    {
        public void Override(AutoMapping<DlsAppOverlordExportRunData> mapping)
        {
            // Creates table called "RawY", with primary key
            // "DlsAppOverlordExportRunData_Id", and numeric column "Value"
            mapping.HasMany(x => x.RawY)
                   .Element("Value");
        }
    }
}

    private static ISessionFactory CreateSessionFactory() 
    { 
        ISessionFactory sessionFactory = null; 


        const string autoMapExportDir = "AutoMapExport"; 
        if( !Directory.Exists(autoMapExportDir) ) 
            Directory.CreateDirectory(autoMapExportDir); 


        try 
        { 
            var autoPersistenceModel = 
                AutoMap.AssemblyOf<DlsAppOverlordExportRunData>() 
                       .Where(t => t.Namespace == "DlsAppAutomapped")

                       // NEW - USE THE OVERRIDES    
                       .UseOverridesFromAssemblyOf<DlsAppOverlordExportRunData>() 

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


            sessionFactory = Fluently.Configure() 
                .Database(SQLiteConfiguration.Standard 
                              .UsingFile(DbFile) 
                              .ShowSql() 
                         ) 
                .Mappings(m => m.AutoMappings.Add(autoPersistenceModel) 
                                             .ExportTo(autoMapExportDir) 
                         ) 
                .ExposeConfiguration(BuildSchema) 
                .BuildSessionFactory() 
                ; 
        } 
        catch (Exception e) 
        { 
            Debug.WriteLine(e); 
        } 


        return sessionFactory; 
    } 
于 2009-11-25T22:07:25.163 回答
0

Didn't get any answers here or on the Fluent NHibernate mailing list that actually worked, so here's what I did.

It smells like a horrible hack, but it works. (Whether it will scale up to large data sets remains to be seen).

First, I wrapped a float property (called Value) in a class:

// Hack - need to embed simple types in a class before NHibernate
// will map them
public class MappableFloat
{
    public virtual int Id { get; private set; }
    public virtual float Value { get; set; }
}

I then declare the properties in other classes that need to be Lists of floats e.g.

public virtual IList<MappableFloat> RawYMappable { get; set; }

NHibernate creates a single database table, with multiple foreign keys, e.g.

create table "MappableFloat" (
    Id  integer,
   Value NUMERIC,
   DlsAppOverlordExportRunData_Id INTEGER,
   DlsAppOverlordExportData_Id INTEGER,
   primary key (Id)
)
于 2009-11-13T22:16:57.770 回答