c# - net - nhibernate tutorial
Generando columnas incorrectas en consultas (3)
Estamos teniendo un problema intermitente con NHibernate donde ocasionalmente generará una consulta con una columna incorrecta en el SQL. Si reiniciamos la aplicación, el problema deja de suceder (a veces requiere más de un reinicio). Cuando se produce el problema, durante la vida de ese proceso, siempre produce el SQL incorrecto para la entidad afectada. No siempre es la misma entidad afectada.
Es una aplicación ASP.NET donde SessionFactory se crea durante el evento Application_Start. Toda la configuración y el mapeo se hacen por código.
No tenemos más ideas sobre cómo probar o depurar la aplicación, y estoy empezando a suponer que hay algún error en NHibernate, ya que la aplicación se corrige al reiniciarse. ¡Cualquier idea / consejo será muy apreciada!
Aquí hay un ejemplo:
Entidad
namespace Example.Clinicas
{
public partial class Clinica : Entidade // Abstract base class that has a property Handle
{
public virtual string Ddd { get; set; }
public virtual string Ddd2 { get; set; }
public virtual long? Duracao { get; set; }
public virtual string Numero { get; set; }
public virtual string Numero2 { get; set; }
public virtual string Prefixo { get; set; }
public virtual string Prefixo2 { get; set; }
public virtual long? HandlePrestador { get; set; }
public virtual Example.Prestadores.Prestador Prestador { get; set; }
}
}
Cartografía
namespace Example.Clinicas.Mappings
{
public class ClinicaMapping : ClassMapping<Clinica>
{
public ClinicaMapping()
{
Table("CLI_CLINICA");
Id(x => x.Handle, map =>
{
map.Column("HANDLE");
map.Generator(Generators.Sequence, g => g.Params(new { sequence = "SEQ_AUTO1816" }));
});
Property(x => x.Ddd, map => map.Column( c=>
{
c.Name("DDD1");
c.Length(4);
}));
Property(x => x.Ddd2, map => map.Column( c=>
{
c.Name("DDD2");
c.Length(4);
}));
Property(x => x.Duracao, map => map.Column("INTERVALOAGENDA"));
Property(x => x.Numero, map => map.Column( c=>
{
c.Name("NUMERO1");
c.Length(5);
}));
Property(x => x.Numero2, map => map.Column( c=>
{
c.Name("NUMERO2");
c.Length(5);
}));
Property(x => x.Prefixo, map => map.Column( c=>
{
c.Name("PREFIXO1");
c.Length(5);
}));
Property(x => x.Prefixo2, map => map.Column( c=>
{
c.Name("PREFIXO2");
c.Length(5);
}));
Property(x => x.HandlePrestador, map => map.Column("PRESTADOR"));
ManyToOne(x => x.Prestador, map =>
{
map.Column("PRESTADOR");
map.Insert(false);
map.Update(false);
});
}
}
}
Mando
Session.Query<Clinica>().FirstOrDefault();
SQL generado
select HANDLE489_,
DDD2_489_,
DDD3_489_,
INTERVAL4_489_,
NUMERO5_489_,
NUMERO6_489_,
PREFIXO7_489_,
FATURADE8_489_,
PRESTADOR489_
from (select clinica0_.HANDLE as HANDLE489_,
clinica0_.DDD1 as DDD2_489_,
clinica0_.DDD2 as DDD3_489_,
clinica0_.INTERVALOAGENDA as INTERVAL4_489_,
clinica0_.NUMERO1 as NUMERO5_489_,
clinica0_.NUMERO2 as NUMERO6_489_,
clinica0_.PREFIXO1 as PREFIXO7_489_,
clinica0_.FATURADEPARCELAMENTO as FATURADE8_489_,
clinica0_.PRESTADOR as PRESTADOR489_
from CLI_CLINICA clinica0_)
where rownum <= 1
Excepción
ORA-00904: "CLINICA0_"."FATURADEPARCELAMENTO": invalid identifier
Observaciones interesantes:
- Es más probable que afecte a entidades más grandes (que tienen un mayor número de propiedades), pero también afecta a entidades más pequeñas ocasionalmente;
- El SQL generado siempre tiene el mismo número de columnas que las propiedades mapeadas;
- Las columnas en el SQL están en el mismo orden que las propiedades mapeadas en la clase de mapeo;
- La columna incorrecta reemplazará a una existente;
- La columna incorrecta es una columna válida en una entidad asignada diferente;
- No hay relación entre la entidad afectada y la que tiene la columna incorrecta;
Otros detalles:
- Versión de .NET: 4.0
- Versión de NHibernate: 3.3.3.400
- Asignación por código: NHibernate.Mapping.ByCode
- Configuración por código: NHibernate.Cfg
Asignaciones de carga
var mapper = new ModelMapper();
foreach (var assembly in resolver.GetAssemblies()) // resolver is a class that gets all the assemblies for the current application
mapper.AddMappings(assembly.GetExportedTypes());
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
return mapping;
Configuración de SessionFactory
var configure = new Configuration();
configure.DataBaseIntegration(x =>
{
x.Dialect<Oracle10gDialect>(); // Custom class
x.ConnectionString = ConnectionString;
x.BatchSize = 100;
x.Driver<OracleMultiQueryDataClientDriver>(); // Custom class
x.MaximumDepthOfOuterJoinFetching = 10;
x.Timeout = 250;
x.PrepareCommands = true;
x.HqlToSqlSubstitutions = "true ''S'', false ''N'', yes ''S'', no ''N''";
x.LogFormattedSql = true;
x.LogSqlInConsole = true;
x.AutoCommentSql = true;
x.IsolationLevel = IsolationLevel.ReadCommitted;
x.ConnectionProvider<ConnectionProvider>(); // Custom class
});
configure.Properties.Add(new KeyValuePair<string, string>("hibernate.command_timeout", "250"));
configure.Proxy(x => x.ProxyFactoryFactory<NHibernate.Bytecode.DefaultProxyFactoryFactory>());
configure.LinqToHqlGeneratorsRegistry<LinqToHqlGeneratorsRegistry>();
configure.CurrentSessionContext<NHibernate.Context.WebSessionContext>();
var mapping = GetMappings(); // Method showed above
mapping.autoimport = false;
configure.AddMapping(mapping);
var listener = new AuditEventListener();
configure.EventListeners.PostInsertEventListeners = new IPostInsertEventListener[] { listener };
configure.EventListeners.PostUpdateEventListeners = new IPostUpdateEventListener[] { listener };
configure.SessionFactory().GenerateStatistics();
return configure;
Hice la misma pregunta en el foro de Grupos de Google de usuarios de NHibernate, y alguien cree que han resuelto la causa raíz (y también han propuesto una solución):
https://groups.google.com/forum/#!topic/nhusers/BZoBoyWQEvs
El código del problema está en PropertyPath.Equals (PropertyPath) que intenta determinar la igualdad utilizando solo el código hash. Esto funciona bien para bases de código más pequeñas como Object.GetHashCode () predeterminado devuelve un índice de objeto secuencial. Sin embargo, después de la recolección de basura, estos índices se reutilizan cuando se eliminan los objetos finalizados y se crean nuevos objetos ... lo que hace que más de un objeto obtenga el mismo código hash ... Una vez que se inicia la recolección de basura, los caminos de propiedad tienen la oportunidad de compartir el mismo código hash, lo que significa que finalmente mezclarán sus personalizadores para las propiedades que colisionan, por lo tanto, los nombres de columnas incorrectos ...
Si desea corregir este error, puede aplicar un parche al código fuente de NH:
Si tiene su propia copia de la fuente NH, puede corregir el error cambiando NHibernate / Mapping / ByCode / PropertyPath.cs línea # 66 desde:
return hashCode == other.GetHashCode();
A:
return hashCode == other.GetHashCode() && ToString() == other.ToString();
Consulte el Grupo de Google para obtener más detalles sobre el problema.
parece que los "pagos con tarjeta de crédito" FATURADEPARCELAMENTO es una propiedad de su objeto "prestamista" PRESTADOR, si este es el caso, debe ser una referencia y NO una propiedad en el mapeo. Espero que ayude o al menos te apunte en la dirección correcta
la referencia tomaría el lugar de su línea Propiedad (x => x.HandlePrestador, map => map.Column ("PRESTADOR")); y sería algo cercano a References (x => x.HandlePrestador)
Revise su registro de consulta para ver qué tipo de consulta está ejecutando, en su SQL desde allí usted, puede detectar el problema.