Question

J'essaie d'implémenter un IUserType pour les états et les codes de pays qui me permettra d'accéder à la fois au code à deux lettres (ce qui est stocké dans la base de données) ainsi qu'au nom complet.Je suis l'exemple du livre de recettes NHibernate 3.0 (p. 225), mais mon problème est que ma classe StreetAddress est actuellement mappée en tant que composant dans ma configuration d'automapping:

public override bool IsComponent(Type type)
{
    return type == typeof(StreetAddress);
}

Avec cette classe identifiée comme un composant, je ne sais pas comment je peux utiliser un IUserType pour la propriété de la classe de composant, puisque cette classe n'est pas explicitement mappée.Il n'y a nulle part où je pourrais dire à NHibernate couramment d'utiliser la spécification IUserType.

Était-ce utile?

La solution

@Firo était proche, mais il s'est avéré être une solution beaucoup plus simple.Il y avait deux étapes ici.Tout d'abord, j'ai dû dire à Fluent NHibernate de ne pas mapper les classes State et Country, qui résident dans ma couche de domaine:

public override bool ShouldMap(Type type)
{
    return type.Name != "State" && type.Name != "Country";
}

Ensuite, j'ai simplement dû créer les conventions pour les classes IUserType.Cela s'est avéré plus facile que @Firo l'a suggéré:

public class CountryUserTypeConvention : UserTypeConvention<CountryType>
{
}

public class StateUserTypeConvention : UserTypeConvention<StateType>
{
}

La définition de ces IUserTypes a été extraite du livre de recettes référencé dans la question d'origine, mais au cas où vous ne voudriez pas le lire:

public class CountryType : GenericWellKnownInstanceType<Country, string>
{
    // The StateType is pretty much the same thing, only it uses "StateCode" instead of "CountryCode"
    private static readonly SqlType[] sqlTypes =
        new[] {SqlTypeFactory.GetString(2)};

    public CountryType()
        : base(new Countries(),
               (entity, id) => entity.CountryCode == id,
               entity => entity.CountryCode)
    {
    }

    public override SqlType[] SqlTypes
    {
        get { return sqlTypes; }
    }
}

Et cela dérive du GenericWellKnownInstanceType:

[Serializable]
public abstract class GenericWellKnownInstanceType<T, TId> :
    IUserType where T : class
{
    private Func<T, TId, bool> findPredicate;
    private Func<T, TId> idGetter;
    private IEnumerable<T> repository;

    protected GenericWellKnownInstanceType(
        IEnumerable<T> repository,
        Func<T, TId, bool> findPredicate,
        Func<T, TId> idGetter)
    {
        this.repository = repository;
        this.findPredicate = findPredicate;
        this.idGetter = idGetter;
    }

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

    public bool IsMutable
    {
        get { return false; }
    }

    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }
        if (ReferenceEquals(null, x) ||
            ReferenceEquals(null, y))
        {
            return false;
        }
        return x.Equals(y);
    }

    public int GetHashCode(object x)
    {
        return (x == null) ? 0 : x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs,
                              string[] names, object owner)
    {
        int index0 = rs.GetOrdinal(names[0]);
        if (rs.IsDBNull(index0))
        {
            return null;
        }
        var value = (TId) rs.GetValue(index0);
        return repository.FirstOrDefault(x =>
                                         findPredicate(x, value));
    }

    public void NullSafeSet(IDbCommand cmd,
                            object value, int index)
    {
        if (value == null)
        {
            ((IDbDataParameter) cmd.Parameters[index])
                .Value = DBNull.Value;
        }
        else
        {
            ((IDbDataParameter) cmd.Parameters[index])
                .Value = idGetter((T) value);
        }
    }

    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;
    }

    /// <summary>
    /// The SQL types for the columns
    /// mapped by this type.
    /// </summary>
    public abstract SqlType[] SqlTypes { get; }
}

Les référentiels pour ces classes ne sont qu'une paire d'objets ReadOnlyCollection des objets State et Country.Encore une fois, dans le livre de cuisine:

public class States : ReadOnlyCollection<State>
{
    // Truncated in the interest of brevity
    public static State Arizona = new State("AZ", "Arizona");
    public static State Florida = new State("FL", "Florida");
    public static State California = new State("CA", "California");
    public static State Colorado = new State("CO", "Colorado");
    public static State Oklahoma = new State("OK", "Oklahoma");
    public static State NewMexico = new State("NM", "New Mexico");
    public static State Nevada = new State("NV", "Nevada");
    public static State Texas = new State("TX", "Texas");
    public static State Utah = new State("UT", "Utah");

    public States() : base(new State[]
                               {
                                   Arizona, Florida, California, Colorado,
                                   Oklahoma, NewMexico, Nevada, Texas, Utah
                               }
        )
    {
    }
}

J'espère que cela aidera quelqu'un là-bas.

Autres conseils

Je n'ai pas pu le tester, mais cela devrait être possible en utilisant une convention

public class ComponentConvention : IComponentConvention, IComponentConventionAcceptance
{
    public void Accept(IAcceptanceCriteria<IComponentInspector> criteria)
    {
        criteria.Expect(x => x.Type == typeof(StreetAddress);
    }

    public void Apply(IComponentInstance instance)
    {
        instance.Properties.First(p => p.Name == "CountrCode").CustomType<MyUserType>();
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top