2009-12-09 3 views
6

Comment changer l'ordre des colonnes dans un index à plusieurs colonnes?
i.e.:Comment changer l'ordre des colonnes dans un index à plusieurs colonnes en utilisant fluent-nhibernate?

mapping.References(x => x.SomeReference).SetAttribute("index", "IX_index"); 
mapping.Map(x => x.SomeField).SetAttribute("index", "IX_index"); 

Produit le schéma suivant:

create index IX_index on ApplicantProgramDatas (SomeField, SomeReferenceId) 

Mais je veux faire:

create index IX_index on ApplicantProgramDatas (SomeReferenceId, SomeField) 
+0

+1 Avez-vous trouver un moyen de le faire par la suite?J'ai le même problème. – Groo

+1

Avez-vous essayé d'échanger l'ordre des déclarations de mappage? (un peu de fudge, même si ça marche!) – UpTheCreek

+0

@UpTheCreek: Comme Ben l'a dit plus bas, je pense que ça ne marche pas. Et l'approche échouerait de toute façon si j'avais plus d'un index multi-colonnes, car cela forcerait une seule colonne à être la colonne d'index la plus haute dans * tous * les index. – Groo

Répondre

4

Vous pouvez définir un index dans la base de données en utilisant NHibernate objet < > ou IAuxiliaryDatabaseObject.

Dans un fichier hbm.xml:

<hibernate-mapping xmlns="urn:nhiernate-mapping-2.2"> 
    <database-object> 
    <create>VALID SQL</create> 
    <drop>VALID SQL</create> 
    </database-object> 
</hibernate-mapping> 

N.B. < objet-base de données > peut aller avant ou après un mappage de classe dans le même fichier hbm.xml vous permettant de conserver vos définitions d'index, déclencheurs, etc. avec l'objet auquel ils s'appliquent.

L'autre option est NHibernate.Mapping.IAuxiliaryDatabaseObject:

namespace NHibernate.Mapping { 
    public interface IAuxiliaryDatabaseObject : IRelationalModel { 
     void AddDialectScope(string dialectName); 
     bool AppliesToDialect(Dialect dialect); 
     void SetParameterValues(IDictionary<string, string> parameters); 
    } 
    public interface IRelationalModel { 
     string SqlCreateString(Dialect dialect, IMapping p, string defaultCatalog, string defaultSchema); 
     string SqlDropString(Dialect dialect, string defaultCatalog, string defaultSchema); 
    } 
} 

Étant donné que vous utilisez Fluent NHibernate, IAuxiliaryDatabaseObject sera probablement mieux pour vous. Présentez simplement votre configuration lors de sa construction, puis appelez:

var sqlCreate = "CREATION SCRIPT"; 
var sqlDrop = "DROP SCRIPT";  
cfg.AddAuxiliaryDatabaseObject(new SimpleAuxiliaryDatabaseObject(sqlCreate, sqlDrop)); 

N.B. NHibernate.Mapping.SimpleAuxiliaryDatabaseObject fait partie de NHibernate. Vous n'avez pas besoin de l'écrire vous-même si tout ce que vous avez à faire est de fournir des scripts de création/suppression pour un objet de base de données. J'ai jeté un rapide coup d'oeil dans la base de code Fluent NHibernate et n'ai vu aucun support direct pour IAuxiliaryDatabaseObject. Il s'agit donc d'exposer votre objet de configuration et de fournir vous-même tous vos IAuxiliaryDatabaseObjects. Il ne serait pas trop difficile d'écrire du code qui balaye l'ensemble de mappage en recherchant les types qui implémentent IAuxiliaryDatabaseObject, puis de les forcer à passer à cfg.AddAuxiliaryDatabaseObject (obj).

Vous pouvez trouver plus d'informations sur les objets de base de données auxiliaires dans les NHibernate docs:

http://nhibernate.info/doc/nh/en/index.html#mapping-database-object

3

Je suppose qu'il est impossible. « FluentNHibernate.MappingModel.MappedMembers.AcceptVisitor() » propriétés itère avant les références:

 foreach (var collection in Collections) 
      visitor.Visit(collection); 

     foreach (var property in Properties) 
      visitor.Visit(property); 

     foreach (var reference in References) 
      visitor.Visit(reference); 

En conséquence, vous aurez toujours des propriétés avant des références dans l'index à plusieurs colonnes.

BTW aucun des ORM vous donnera la capacité de définir les options d'index non triviales comme cluster, filtrée, etc.

+0

Merci. Dans ce cas, connaissez-vous un moyen d'inclure un schéma personnalisé (écrit à la main) dans la configuration? Je préférerais avoir tous les SQL liés à la création de DB à un endroit. Je suppose que je pourrais simplement exécuter SQL pur pour ajouter des index, après NHibernate construit le reste du schéma. – Groo

2

Laissez-moi suggérer de passer outre SchemaExport. Il y a un accesseur de champ privé par réflexion, il nécessite un mode de confiance complet. Si cette approche ne répond pas à vos besoins, pensez à la réécriture SchemaExport (classe relativement légère)


using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Reflection; 
using System.Text; 
using FluentNHibernate.Cfg; 
using FluentNHibernate.Cfg.Db; 
using FluentNHibernate.Mapping; 
using NHibernate.Cfg; 
using NHibernate.Tool.hbm2ddl; 

namespace FluentNHib 
{ 

    public class Master 
    { 
     public int Id { get; set; } 
    } 

    public class Child 
    { 
     public int Id { get; set; } 
     [MCIndex("A", 0)] 
     public Master Master { get; set; } 
     [MCIndex("A", 1)] 
     public string Name { get; set; } 
    } 

    public class MCIndexAttribute : Attribute 
    { 
     public string indexName; 
     public int indexOrder; 

     public MCIndexAttribute(string indexName, int i) 
     { 
      this.indexName = indexName; 
      this.indexOrder = i; 
     } 
    } 

    public class MasterMap : ClassMap 
    { 
     public MasterMap() 
     { 
      Id(x => x.Id); 
     } 
    } 

    public class ChildMap : ClassMap 
    { 
     public ChildMap() 
     { 
      Id(x => x.Id); 
      References(x => x.Master).Index("A"); 
      Map(x => x.Name).Index("A"); 

     } 
    } 

    class MySchemaExport : SchemaExport 
    { 
     internal struct MCIndexField 
     { 
      internal int index; 
      internal string Name; 

     } 

     internal class MCIndex 
     { 
      internal string IndexName; 
      public readonly IList fields = new List(); 
      public string Table; 

      public void AddField(string name, int indexOrder) 
      { 
       fields.Add(new MCIndexField {index = indexOrder, Name = name}); 
      } 
     } 

     private readonly Dictionary indexes = new Dictionary(); 

     MCIndex ByName(string name, string table) 
     { 
      MCIndex result; 
      if (!indexes.TryGetValue(name, out result)) 
      { 
       result = new MCIndex 
        { 
         IndexName = name 
        }; 
       indexes.Add(name, result); 
      } 
      return result; 
     } 

     public MySchemaExport(Configuration cfg) : base(cfg) 
     { 
      foreach (var type in typeof(ChildMap).Assembly.GetTypes()) 
      { 
       foreach (var prop in type.GetProperties()) 
       { 
        var attr = prop.GetCustomAttributes(typeof (MCIndexAttribute), true); 
        if (attr.Length == 1) 
        { 
         var attribute = (MCIndexAttribute) attr[0]; 
         ByName(attribute.indexName, type.Name).AddField(prop.Name, attribute.indexOrder); 
        } 
       } 
      } 


      var createSqlProp = typeof(SchemaExport).GetField("createSQL", BindingFlags.NonPublic | BindingFlags.Instance); 
      var wasSql = createSqlProp.GetValue(this); 

      var sb = new StringBuilder(); 
      sb.AppendLine(""); 
      foreach (var mcIndex in indexes) 
      { 
       sb.AppendLine(string.Format("create index {0} on {1} ({2})", mcIndex.Value.IndexName, mcIndex.Value.Table, mcIndex.Value.fields)); 
      } 
      createSqlProp.SetValue(this, wasSql + sb.ToString()); 
     } 
    } 

    class Program 
    { 

     private static void BuildSchema(Configuration config) 
     { 
      new MySchemaExport(config) 
       .Create(s => 
          { 
           Debug.WriteLine(s); 
          }, true); 
     } 

     const string fileName = "c:\\temp\\temp.fdb"; 

     private static string GetConnectionString() 
     { 
      const string userName = "sysdba"; 
      const string password = "masterkey"; 
      return String.Format("ServerType=1;User={0};Password={1};Dialect=3;Database={2}", userName, password, fileName); 
     } 

     private static FluentConfiguration Configurate() 
     { 
      var fbc = new FirebirdConfiguration(); 
      return Fluently.Configure() 
      .Database(fbc.ShowSql().ConnectionString(GetConnectionString())) 
       .Mappings(m => m.FluentMappings 
        .AddFromAssemblyOf() 
       ) 
       .ExposeConfiguration(BuildSchema); 
     } 

     static void Main(string[] args) 
     { 
      FluentConfiguration fluentConfiguration = Configurate(); 

      Configuration cfg = fluentConfiguration.BuildConfiguration(); 
     } 
    } 
}
+0

Merci, pas une mauvaise suggestion. Quelques notes, cependant: a) Je pense que vos parenthèses génériques se sont perdues '<>', b) 'createSQL' semble retourner un tableau de chaînes dans FluentHib. 1.1, c) vous ne trierez jamais les attributs par numéro d'index, d) la liste 'fields' ne sera pas rendue correctement par elle-même, dans' sb.AppendLine'. Je peux les corriger facilement, mais je préférerais avoir des index dans mes classes de mapping plutôt que des entités réelles, donc je vais probablement réécrire une bonne partie de ceci pour les déplacer là. Néanmoins, vous avez répondu à ma question et m'a donné une bonne idée. – Groo

Questions connexes