2010-04-07 5 views
2

J'ai une classe assez simple que je veux enregistrer dans SQL Server via NHibernate (avec des mappages Fluent). La classe est composée principalement de champs de chaînes optionnels.Comment faire pour que NHibernate conserve une valeur de propriété String.Empty comme NULL

Mon problème est que les champs de classe par défaut sont string.empty pour éviter NullRefExceptions et quand NHibernate enregistre la ligne dans la base de données, chaque colonne contient une chaîne vide au lieu de null. Question: Existe-t-il un moyen pour que NHibernate sauvegarde automatiquement la valeur null lorsque la propriété string est une chaîne vide? Ou ai-je besoin de jeter mon code avec les contrôles if (string.empty)? NHibernate fait ce que vous lui demandez de faire.

Répondre

5

À une extrémité vous dites que vous essayez d'éviter NullReferenceException, à l'autre extrémité vous essayez d'enregistrer NULL dans la base de données quand une valeur n'est pas nulle. Cela me semble être une contradiction. Au lieu d'essayer de contourner ce bogue, essayez soit d'autoriser les valeurs nulles (et de vérifier les données pour empêcher les NRE), soit d'autoriser les valeurs nulles.

S'il existe un cas particulier que vous souhaitez couvrir avec des champs NULL par rapport à des champs vides, lisez les données correctes (et n'initialisez pas String.Empty). Si vous traitez une chaîne vide égale à une valeur nulle dans cette base de données, initialisez simplement tous les champs à la chaîne vide pour la rendre facile et cohérente.

+0

oui ... il fait exactement ce que je lui demande ... espérant toujours un moyen facile de permettre au code de voir null comme étant le même que string.empty mais pour que la base de données les voit comme non égales . – Todd

+2

"bug de fonctionnalité"? J'ajoute définitivement cette phrase à mon vocabulaire. :-) –

6

Vous pouvez le faire avec un UserType. Je suis arrivé à la conclusion que les chaînes nulles sont inutiles (et une douleur dans le cou) dans mes classes de l'entreprise, donc je convertis toutes les colonnes de base de données de chaîne Nullable en chaîne vide et vice versa.

utilisation Courant est:

Map(x => x.MiddleName).CustomType(typeof(NullableString)); 

/// <summary> 
/// UserType for string properties that are database nullable. Using this type 
/// will substitue empty string for null when populating object properties 
/// and null for empty string in database operations. 
/// </summary> 
/// <example> 
/// Map(x => x.MiddleName).Length(30).Nullable().CustomType(typeof(NullableString)); 
/// </example> 
public class NullableString : IUserType 
{ 
    public new bool Equals(object x, object y) 
    { 
     if (ReferenceEquals(x, y)) 
     { 
      return true; 
     } 
     if (x == null || y == null) 
     { 
      return false; 
     } 
     return x.Equals(y); 
    } 

    public int GetHashCode(object x) 
    { 
     return x.GetHashCode(); 
    } 

    public object NullSafeGet(IDataReader rs, string[] names, object owner) 
    { 
     var valueToGet = NHibernateUtil.String.NullSafeGet(rs, names[0]); 
     return valueToGet ?? string.Empty; 
    } 

    public void NullSafeSet(IDbCommand cmd, object value, int index) 
    { 
     var stringObject = value as string; 
     object valueToSet = string.IsNullOrEmpty(stringObject) ? null : stringObject; 
     NHibernateUtil.String.NullSafeSet(cmd, valueToSet, index); 
    } 

    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 DeepCopy(cached); 
    } 

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

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

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

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

Juste une pensée, mais si les chaînes nulles sont une douleur dans le cou (d'accord!), Alors pourquoi ne pas simplement rendre le champ non nul et la valeur par défaut la chaîne vide? Problème résolu et pas besoin d'écrire des solutions de contournement à travers le code. – Abel

+4

Système hérité, DBA rigide, base de données pas entièrement sous mon contrôle, base de données accédée par d'autres systèmes, etc. –

1

Je ne dirais pas que vous devez litière votre code avec des chèques. J'utilise une seule méthode d'extension:

public static class StringExtensions 
{ 
    public static string NullIfEmpty(this string s) 
    { 
     return string.IsNullOrEmpty(s) ? null : s; 
    } 
} 

Ensuite, écrivez votre classe d'entité ainsi:

public class MyEntity 
{ 
    private string name; 

    public string Name 
    { 
     get { return name; } 
     set { name = value.NullIfEmpty(); } 
    } 
} 

Je pense qu'il est préférable que vous indiquez explicitement que vous voulez que ce comportement, car dans bien des cas, une chaîne vide pourrait être une valeur valide dans la base de données.

L'utilisation d'un type personnalisé fonctionne également; cependant, il me semble toujours que ce comportement "invalidant" devrait être un comportement de l'entité et non le mapper, et que l'entité elle-même devrait avoir un contrat qui dit "j'ignore les chaînes vides".

+0

Je vois cela comme faisant partie de la discordance O/R et donc le mappeur a la responsabilité de le gérer. Je n'ai pas encore trouvé un cas d'utilisation valide pour les chaînes nulles dans un objet métier, ou un besoin de distinguer entre chaîne vide et null dans la base de données. Si vous utilisez Oracle, il stocke des chaînes de longueur nulle comme null de toute façon. –

+1

@Jamie: Je suppose avec Oracle que ce serait vrai; Dans SQL Server au moins, les chaînes de longueur nulle sont différentes de 'NULL', donc c'est en fait une correspondance parfaite pour les chaînes .NET, pas une discordance. Les chaînes de longueur nulle sont également sémantiquement différentes de 'NULL' -' NULL' signifie * inconnu * ou * undefined * alors qu'une chaîne de longueur nulle signifie que la valeur * est * définie mais juste vide. – Aaronaught

+0

Les types correspondent parfaitement, mais je suis en train de résoudre la non-concordance O/R de mes objets. Et je sais ce que signifie null, ce que je dis, c'est que la distinction est pratiquement nulle pour les cordes. Le cas standard est que je définis une propriété à partir de l'entrée de l'utilisateur, donc je lis la propriété Text du contrôle qui renvoie une chaîne vide même si l'utilisateur n'a pas touché le contrôle. Afin de savoir que l'utilisateur signifiait "undefined/unknown/null", je devrais fournir un élément d'interface utilisateur supplémentaire pour basculer le contrôle de texte. –

Questions connexes