0

J'ai une base de données qui enregistre un Id d'un élément qui est stocké dans un autre système et désérialisé à un objet dans le code. J'essaie d'utiliser Fluent NHibernate pour créer une entité de modèle de domaine composée de données provenant de la base de données et du service externe. Un exemple va l'expliquer mieux. Dans la base de données j'ai une table ressemblant à ceci:Mappage d'une propriété de type personnalisé dans Fluent NHibernate

CREATE TABLE entities 
(
    id integer NOT NULL, 
    custom_thing text NOT NULL, 
    CONSTRAINT entities_id_pk PRIMARY KEY (id) 
); 

PostgreSQL est le problème mais n'est pas spécifique à la base de données. Maintenant, dans le code que j'ai ces classes:

class Entity 
{ 
    public virtual int Id { get; set; } 

    public virtual CustomThing CustomThing { get; set; } 
} 

class CustomThing 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
} 

Je suis en train d'utiliser un CustomMap pour définir la mise en correspondance:

class EntityMap : ClassMap<Entity> 
{ 
    public EntityMap() 
    { 
     Table("entities"); 
     Id(e => e.Id, "id").GeneratedBy.Assigned(); 
     // Map(e => e.CustomThing, "custom_thing"); 
    } 
} 

La question est: comment puis-je la carte du CustomThing? Voici un programme essayant de mapper la classe Entity (besoin du paquet FluentNHibernate et de NpgSQL s'il est exécuté sur une base de données PostgreSQL). Par souci de simplicité je viens de créer des instances de CustomThing dans le code:

class Program 
{ 
    // How to use this in mapping? 
    static CustomThing[] _customThings = 
    { 
     new CustomThing {Id = "abc", Name = "ABC"}, 
     new CustomThing {Id = "def", Name = "DEF"} 
    }; 

    static void Main() 
    { 
     using (ISessionFactory sessionFactory = Fluently.Configure() 
      .Database(PostgreSQLConfiguration.Standard 
       .ConnectionString(
        @"Server=localhost; Database=test_nhibernate; User Id=postgres; Password=little_secret;")) 
      .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Entity>()) 
      .BuildSessionFactory()) 
     { 
      using (ISession session = sessionFactory.OpenSession()) 
      { 
       var entity = session.Get<Entity>(1); 

       Console.WriteLine($"{entity?.Id} {entity?.CustomThing?.Name}"); 
      } 
     } 
     Console.ReadLine(); 
    } 
} 

La sortie est bien sûr que la valeur de la propriété Id parce que la cartographie pour CustomThing n'est pas défini. Est-il possible de configurer le mappage afin que je puisse lui passer des paramètres et mapper des valeurs de colonne custom_thing aux objets dans _customThings par propriété Id en quelque sorte?

+0

J'ai résolu même problème avec le stockage de cette propriété sous forme de texte JSON et la création de nouvelles sur mesure « UserType » sérialiser et désérialiser l'objet, si cette solution vous convient, je peux vous fournir quelques extraits de code? – mdameer

+0

@mdameer Merci, ok je pense que je comprends le point, mais comment as-tu même demandé au mappeur d'assigner ce JSON? Je suppose que vous voulez dire que 'Entity.CustomThing' serait de type' string' ici. Le problème est que dans la table 'entities' je ne stocke pas la totalité de' CustomThing' sérialisé, mais seulement son 'Id'. Les autres propriétés proviennent du service externe, qui a toute la structure JSON 'CustomThing'. Votre solution fonctionnerait-elle dans ce cas? – Caleb9

Répondre

3

Oui, vous pouvez le faire en sauvegardant le Id, et obtenez le reste des données dans votre code, ou mettez tout dans votre mappeur pour vous renvoyer un objet, si vous sélectionnez la deuxième option, vous pouvez suivre ce code :

[Serializable] 
public class CustomThingUserType : IUserType { 
    public new bool Equals(object x, object y) { 
     if (object.ReferenceEquals(x, y)) 
      return true; 

     if (x == null || y == null) 
      return false; 

     return x.Equals(y); 
    } 

    public int GetHashCode(object x) { 
     if (x == null) 
      return 0; 

     return x.GetHashCode(); 
    } 

    public object NullSafeGet(IDataReader rs, string[] names, object owner) { 
     if (names.Length == 0) 
      throw new ArgumentException("Expecting at least one column"); 

     int id = (int)NHibernateUtil.Int32.NullSafeGet(rs, names[0]); 

     var obj = new CustomThing { Id = id }; 

     // here you can grab your data from external service 

     return obj; 
    } 

    public void NullSafeSet(IDbCommand cmd, object value, int index) { 
     var parameter = (DbParameter)cmd.Parameters[index]; 

     if (value == null) { 
      parameter.Value = 0; 
     } 
     else { 
      parameter.Value = ((CustomThing)value).Id; 
     } 
    } 

    public object DeepCopy(object value) { 
     return value; 
    } 

    public object Replace(object original, object target, object owner) { 
     return original; 
    } 

    public object Assemble(object cached, object owner) { 
     return cached; 
    } 

    public object Disassemble(object value) { 
     return value; 
    } 

    public SqlType[] SqlTypes { 
     get { 
      return new SqlType[] { new SqlType(DbType.Int32) }; 
     } 
    } 

    public Type ReturnedType { 
     get { return typeof(CustomThing); } 
    } 

    public bool IsMutable { 
     get { return false; } 
    } 
} 
+0

La deuxième option fonctionne en effet. Pour être complet, j'ajouterai que le mapping dans 'EntityMap' devrait ressembler à ceci:' Map (e => e.CustomThing, "custom_thing"). CustomType (); '. J'ai aussi dû changer le type 'id' en chaîne pour correspondre à mon exemple. Merci! :) – Caleb9