流利的NHibernate, hasmanymany和主键问题

本文关键字:问题 hasmanymany NHibernate | 更新日期: 2023-09-27 18:15:32

我认为这应该是直接的,但是对于NHibernate来说,简单的事情总是最复杂的。

我有两个poco,它们通过hasmanymany:

相互引用
public class Foo1
{
//other properties
public virtual IList<Foo2> Foo2s {get;set;}
}
public class Foo2
{
//other properties
public virtual IList<Foo1> Foo1s {get;set;}
}

和映射:

class Foo1Map : ClassMap<Foo1>
{
//other mappings
HasManyToMany(c => c.Foo2s);
}
class Foo2Map : ClassMap<Foo2>
{
//other mappings
HasManyToMany(c => c.Foo1s);
}

连接表是正确创建的,它有2个字段是在各自表的外键上,都很好,巨大的问题是这2个字段应该是主键(或至少是唯一的),而它们不是。我试着玩了各种流畅的方法链接到hasmanymany,但没有任何结果。

我如何得到生成的manymany表在2个外键字段上有主键,而不必创建自定义POCO并将其映射为复合键?

谢谢。

流利的NHibernate, hasmanymany和主键问题

唯一约束和索引

class Foo1Map : ClassMap<Foo1>
{
    //other mappings
    HasManyToMany(c => c.Foo2s)
        .ParentKeyColumns.Add("foo1_id", p => p.UniqueKey("unique_constraint1").Index("someIndex"))
        .ChildKeyColumns.Add("foo2_id", p => p.UniqueKey("unique_constraint1"));
}

已更新:或作为HasMany的约定(像这样的代码我在项目中使用),不太好,但工作

public class IndexOneToManyColumnsConvention : IHasManyConvention
{
    public void Apply(IOneToManyCollectionInstance instance)
    {
        var mappingfield = instance.Key.Columns.First().GetType()
            .GetField("mapping", BindingFlags.Instance | BindingFlags.NonPublic);
        var columnMapping = (ColumnMapping)mappingfield.GetValue(instance.Key.Columns.First());
        if (!columnMapping.HasValue(c => c.Index))
        {
            var typename = instance.Member.DeclaringType.Name;
            columnMapping.Index = string.Format("index_{0}_{1}",
                typename.ToLower(), instance.Member.Name.ToLower());
        }
    }
}

我认为在FNH的主干中有代码可以摆脱这个hack

我已经遇到了fluent的局限性,并且已经放弃了使用它来设置更高级的模式调整。我有一些头痛设置索引的查找表,所以只是添加SQL后创建脚本。

我们有一个数据库项目,它使用NHibernate创建数据库,然后运行一个设置后创建脚本,创建索引和我们使用的存储过程。

我知道这不能直接回答你的问题,但我昨天正在努力解决这个问题,所以我想我应该分享我的解决方案和一些代码,如果这是你决定向下的路线,给你一个良好的开端。

看看其他人在这里做了什么很有趣!

private void ExecuteScripts(string dir, bool useTransaction = true, bool useMasterConnection = false)
{
    foreach (var file in SqlFiles.Where(x => x.Contains(dir)))
    {
        var resource = file.Substring((Assembly.GetExecutingAssembly().GetName().Name + ".scripts.").Length);               
        if (useTransaction)
            ExecuteSqlWithTransaction(resource, useMasterConnection);
        else
            ExecuteSqlWithoutTransaction(resource, useMasterConnection);
    }
}

 private void ExecuteSqlWithoutTransaction(string sqlFile, bool useMasterConnection = false)
        {
            var sql = string.Empty;
            var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream((Assembly.GetExecutingAssembly().GetName().Name + ".scripts.") + sqlFile.Replace('''', '.'));
            using (var reader = new StreamReader(stream))
            {
                sql = reader.ReadToEnd();
            }
            // replace the [[[dbName]]] and [[[SQLDATAFILEPATH]]] with ACTUAL DB settings we want to work with
            sql = sql.Replace("[[[DBNAME]]]", DbName);
            sql = sql.Replace("[[[SQLDATAFILEPATH]]]", SqlDataFilePath);
            var regex = new Regex("^GO", RegexOptions.IgnoreCase | RegexOptions.Multiline);
            var lines = regex.Split(sql);
            var currentConnection = (useMasterConnection) ? MasterSqlConnection : SqlConnection;
            using (var cmd = currentConnection.CreateCommand())
            {
                cmd.Connection = currentConnection;
                foreach (var line in lines)
                {
                    if (line.Length > 0)
                    {
                        cmd.CommandText = line;
                        cmd.CommandType = CommandType.Text;
                        try
                        {
                            cmd.ExecuteNonQuery();
                        }
                        catch (SqlException ex)
                        {
                            //ShowInfo("Error running script " + sqlFile + "    Error: " + ex.Message);
                            throw new Exception(ex.Message);
                        }
                    }
                }
            }
        }

ALTER TABLE [dbo].[ArticleProjectAssignments] ADD  CONSTRAINT [pk_ArticleProjectAssignments] PRIMARY KEY CLUSTERED 
(
    [ArticleId] ASC,
    [ProjectId] ASC
)
GO