Como você mapeia um enum como um valor int com NHibernate fluente?

88

A questão diz tudo, o padrão é mapear como um, stringmas eu preciso mapear como um int.

Atualmente estou usando PersistenceModelpara definir minhas convenções, se isso fizer alguma diferença. Desde já, obrigado.

Atualização Descobriu que obter a versão mais recente do código do tronco resolveu meus problemas.

Garry Shutler
fonte
5
se você mesmo resolveu o problema, deve respondê-lo e, em seguida, marcá-lo como a resposta correta para que futuros pesquisadores a encontrem.
Jeff Martin
Você pode postar a resposta?
mxmissile
Feito pessoal. Desculpe o atraso. Eu não tinha certeza do que deveria fazer com uma pergunta que não era uma pergunta, pois eu só precisava da versão mais recente das bibliotecas.
Garry Shutler
2
Alimentos para bots do Google: eu estava obtendo "acesso ilegal para carregar a coleção" antes de implementar isso para meu mapeamento enum.
4imble de

Respostas:

84

A maneira de definir essa convenção mudou algumas vezes atrás, é agora:

public class EnumConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum);
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}
Julien
fonte
4
Esta é a resposta correta para a versão mais recente de nibernato fluente
Sean Chambers
Colisão. ^^ O que Sean disse.
Martin Suchanek
1
Parece que funciona bem para todos os tipos de enum, mas e se você quiser alguns como strings e outros como ints? Acho que isso deve ser configurável no nível de mapeamento de propriedade.
UpTheCreek de
4
Veja a resposta de @SztupY abaixo que estende isso para permitir enums anuláveis. stackoverflow.com/questions/439003/…
Jon Adams
45

Então, como mencionei, tirar a versão mais recente do Fluent NHibernate do porta-malas me levou aonde eu precisava estar. Um exemplo de mapeamento para um enum com o código mais recente é:

Map(quote => quote.Status).CustomTypeIs(typeof(QuoteStatus));

O tipo personalizado força-o a ser tratado como uma instância do enum em vez de usar o GenericEnumMapper<TEnum>.

Na verdade, estou pensando em enviar um patch para poder alterar entre um mapeador enum que persiste uma string e um que persiste um int, pois isso parece algo que você deve ser capaz de definir como uma convenção.


Isso apareceu na minha atividade recente e as coisas mudaram nas versões mais recentes do Fluent NHibernate para tornar isso mais fácil.

Para fazer com que todos os enums sejam mapeados como inteiros, agora você pode criar uma convenção como esta:

public class EnumConvention : IUserTypeConvention
{
    public bool Accept(IProperty target)
    {
        return target.PropertyType.IsEnum;
    }

    public void Apply(IProperty target)
    {
        target.CustomTypeIs(target.PropertyType);
    }

    public bool Accept(Type type)
    {
        return type.IsEnum;
    }
}

Então, seu mapeamento só precisa ser:

Map(quote => quote.Status);

Você adiciona a convenção ao seu mapeamento do Fluent NHibernate assim;

Fluently.Configure(nHibConfig)
    .Mappings(mappingConfiguration =>
    {
        mappingConfiguration.FluentMappings
            .ConventionDiscovery.AddFromAssemblyOf<EnumConvention>();
    })
    ./* other configuration */
Garry Shutler
fonte
3
com "modo int" como padrão. Quem persiste enums como strings ?!
Andrew Bullock
4
Pode ser um banco de dados legado com valores de string já lá
Chris Haines
4
+1 feno. @ Andrew Bullock: resposta à sua pergunta: qualquer pessoa que lida com bancos de dados do mundo real.
Sky Sanders de
Existe alguma interface IProperty no FN?
Tien Do
40

Não se esqueça dos enums anuláveis ​​(como ExampleEnum? ExampleProperty)! Eles precisam ser verificados separadamente. É assim que é feito com a nova configuração de estilo FNH:

public class EnumConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum ||
            (x.Property.PropertyType.IsGenericType && 
             x.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
             x.Property.PropertyType.GetGenericArguments()[0].IsEnum)
            );
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}
SztupY
fonte
4
1 Para esta adição! A primeira versão não funciona para enums anuláveis ​​(eles permanecem como strings).
longda de
@SztupY O tipo de coluna no banco de dados é int? E quando o tipo aceita Sinalizadores? Como:MyEnum.Active | MyEnum.Paused
ridermansb
@RidermandeSousaBarbosa: Para sinalizadores, verifique aqui: stackoverflow.com/questions/2805661/…
SztupY
25

foi assim que mapeei uma propriedade enum com um valor int:

Map(x => x.Status).CustomType(typeof(Int32));

funciona para mim!

Felipe
fonte
2
Obrigado por fornecer a resposta mais simples
Mike
Meu único problema com isso é que você precisa se lembrar de aplicá-lo a todos os enum. É para isso que as convenções foram criadas.
Garry Shutler
Isso funciona para leitura, mas falhou quando tentei uma consulta de critérios. Configurar uma convenção (veja a resposta a esta pergunta) funcionou em todos os casos que tentei.
Thomas Bratt
Bem, eu achei ótimo - mas isso vai causar problemas: veja este post. nhforge.org/blogs/nhibernate/archive/2008/10/20/…
UpTheCreek
@UpTheCreek Parece que isso foi corrigido e agora é recomendado por James Gregory da equipe NH: mail-archive.com/[email protected]/…
Ilya Kogan
1

Para aqueles que usam o Fluent NHibernate com Automapping (e potencialmente um contêiner IoC):

Essa IUserTypeConventioné a resposta de @ Julien acima: https://stackoverflow.com/a/1706462/878612

public class EnumConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType.IsEnum);
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}

A configuração de Automapeamento Fluent NHibernate pode ser configurada assim:

    protected virtual ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
            .Database(SetupDatabase)
            .Mappings(mappingConfiguration =>
                {
                    mappingConfiguration.AutoMappings
                        .Add(CreateAutomappings);
                }
            ).BuildSessionFactory();
    }

    protected virtual IPersistenceConfigurer SetupDatabase()
    {
        return MsSqlConfiguration.MsSql2008.UseOuterJoin()
        .ConnectionString(x => 
             x.FromConnectionStringWithKey("AppDatabase")) // In Web.config
        .ShowSql();
    }

    protected static AutoPersistenceModel CreateAutomappings()
    {
        return AutoMap.AssemblyOf<ClassInAnAssemblyToBeMapped>(
            new EntityAutomapConfiguration())
            .Conventions.Setup(c =>
                {
                    // Other IUserTypeConvention classes here
                    c.Add<EnumConvention>();
                });
    }

* Então, o CreateSessionFactorypode ser utilizado em um IoC como o Castle Windsor (usando um PersistenceFacility e instalador) facilmente. *

    Kernel.Register(
        Component.For<ISessionFactory>()
            .UsingFactoryMethod(() => CreateSessionFactory()),
            Component.For<ISession>()
            .UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
            .LifestylePerWebRequest() 
    );
lko
fonte
0

Você pode criar um NHibernate IUserTypee especificá-lo usando CustomTypeIs<T>()no mapa de propriedades.

James Gregory
fonte
0

Você deve manter os valores int / tinyint em sua tabela de banco de dados. Para mapear seu enum, você precisa especificar o mapeamento corretamente. Por favor, veja o mapeamento abaixo e a amostra de enum,

Classe de Mapeamento

public class TransactionMap: ClassMap Transaction
{
    public TransactionMap ()
    {
        // Outros mapeamentos
        .....
        // Mapeamento para enum
        Map (x => x.Status, "Status"). CustomType ();

        Tabela ("Transação");
    }
}

Enum

public enum TransactionStatus
{
   Esperando = 1,
   Processado = 2,
   RolledBack = 3,
   Bloqueado = 4,
   Reembolsado = 5,
   Já processado = 6,
}
Arkadas Kilic
fonte