NHibernate - неправильные столбцы в запросах
Я получаю периодически возникающую проблему с NHibernate, когда он генерирует запрос для сущности, но заменяет один из столбцов столбцом из совершенно другой (и не связанной) сущности.
Он заменяет только один столбец и обычно решается путем перезапуска приложения (хотя иногда требуется несколько попыток).
- Приложение ASP.NET (.NET 4.0)
- SessionFactory, созданный во время Application_Start
- NHibernate 3.3.1 - Все сопоставления / настройки выполняются с помощью сопоставления по коду
- Использование критериев Nhibernate
Любой вклад в это будет высоко ценится!
сущность
public class LiquiditySourceItem : RunDataEntity, IEntity<int>
{
public virtual int Id { get; protected internal set; }
public virtual int IdentID { get; protected internal set; }
public virtual string Portfolio { get; protected internal set; }
public virtual string ProfitCentre { get; protected internal set; }
public virtual DateTime? MaturityDate { get; protected internal set; }
public virtual string Curr1 { get; protected internal set; }
public virtual string Curr2 { get; protected internal set; }
public virtual decimal Reval { get; protected internal set; }
public virtual string ContractType { get; protected internal set; }
public virtual string ContractType2 { get; protected internal set; }
public virtual string ContractCode { get; protected internal set; }
public virtual decimal AmountSignedTradeUnit { get; protected internal set; }
public virtual decimal Amount2Signed { get; protected internal set; }
public virtual decimal SpotDelta { get; protected internal set; }
public virtual string TradeRevalCurr { get; protected internal set; }
}
Entity Mapping
public LiquiditySourceItemMap()
{
Id(x => x.Id, map => map.Column("RowId"));
Property(x => x.IdentID, map => map.Column("IdentID"));
Property(x => x.Portfolio, map => map.Column("Portfolio"));
Property(x => x.ProfitCentre, map => map.Column("ProfitCentre"));
Property(x => x.MaturityDate, map => map.Column("Con_Expiry"));
Property(x => x.BuySell, map => map.Column("BS"));
Property(x => x.Curr1, map => map.Column("Curr1"));
Property(x => x.Curr2, map => map.Column("Curr2"));
Property(x => x.Reval, map => map.Column("Reval"));
Property(x => x.ContractType, map => map.Column("ContractType"));
Property(x => x.ContractType2, map => map.Column("ContractType2"));
Property(x => x.ContractCode, map => map.Column("ContractCode"));
Property(x => x.AmountSignedTradeUnit, map => map.Column("AmountSignedTradeUnit"));
Property(x => x.Amount2Signed, map => map.Column("Amount2Signed"));
Property(x => x.ValSpot, map => map.Column("Val_Spot"));
Property(x => x.SpotDelta, map => map.Column("SpotDelta"));
Property(x => x.TradeRevalCurr, map => map.Column("Traderevalcurr"));
Property(x => x.SourceReport, map => map.Column("SourceReport"));
ManyToOne(x => x.RunContext, map => map.Column("RunContextID"));
Table("Staging.vw_Liquidity");
}
Объект отчета
public class BusinessBreakdownStandardPosition : ReportRunDataEntity, IEntity<long>
{
public virtual long Id { get; set; }
public virtual decimal FinalNettingAmountUSD { get; set; }
public virtual decimal InitialChargeAmountUSD { get; set; }
public virtual BusinessBreakdownInitialPrr InitialPrr { get; set; }
public virtual IEnumerable<FinalInstrumentPosition> FinalInstrumentPositions { get; set; }
public virtual decimal CreditEventPaymentUSD { get; set; }
public virtual decimal ValuationChangeIncreaseUSD { get; set; }
public virtual decimal ValuationChangeDecreaseUSD { get; set; }
public virtual string ReportKey { get; set; }
public virtual decimal USDCharge { get; set; }
public virtual decimal USDChargeICG { get; set; }
public virtual string InstrumentType { get; set; }
}
Сопоставление объектов отчета
public class BusinessBreakdownStandardPositionMap : ClassMapping<BusinessBreakdownStandardPosition>
{
public BusinessBreakdownStandardPositionMap()
{
Id(x => x.Id,
m =>
{
m.Column("BusinessBreakdownStandardPositionID");
m.Generator(Generators.HighLow,
g =>
g.Params(
new
{
table = "dbo.HiValue",
max_lo = 10000,
Where = string.Format("EntityName = 'BusinessBreakdownStandardPosition'")
}));
});
Property(x => x.FinalNettingAmountUSD, map => map.Column("FinalNettingAmountUSD"));
Property(x => x.InitialChargeAmountUSD, map => map.Column("InitialAmountUSD"));
Property(x => x.CreditEventPaymentUSD);
Property(x => x.ValuationChangeDecreaseUSD);
Property(x => x.ValuationChangeIncreaseUSD);
Property(x => x.USDCharge);
Property(x => x.USDChargeICG);
Property(x=>x.InstrumentType);
ManyToOne(p => p.RunContext, map => map.Column("ReportRunContextID"));
ManyToOne(p => p.InitialPrr, m =>
{
m.Column("InitialPrrID");
m.Cascade(Cascade.All);
});
Property(x => x.ReportKey);
Bag(x => x.FinalInstrumentPositions, collectionMapping =>
{
collectionMapping.Table("Reporting.BusinessBreakdownFinalInstrumentPositionStandardPositionMap");
collectionMapping.Cascade(Cascade.All);
collectionMapping.Key(k => k.Column("StandardPositionID"));
}, mapping => mapping.ManyToMany(y => y.Column("FinalInstrumentPositionID")));
Table("Reporting.BusinessBreakdownStandardPosition");
}
}
SQL-запрос, сгенерированный NHibernate
SELECT
this_.RowId AS RowId47_0_,
this_.IdentID AS IdentID47_0_,
this_.Portfolio AS Portfolio47_0_,
this_.ProfitCentre AS ProfitCe4_47_0_,
this_.Con_Expiry AS Con5_47_0_,
this_.BS AS BS47_0_,
this_.Curr1 AS Curr7_47_0_,
this_.Curr2 AS Curr8_47_0_,
this_.Reval AS Reval47_0_,
this_.ContractType AS Contrac10_47_0_,
this_.ContractType2 AS Contrac11_47_0_,
this_.ContractCode AS Contrac12_47_0_,
this_.AmountSignedTradeUnit AS AmountS13_47_0_,
this_.Amount2Signed AS Amount14_47_0_,
this_.Val_Spot AS Val15_47_0_,
this_.SpotDelta AS SpotDelta47_0_,
this_.InitialAmountUSD AS Initial17_47_0_,
this_.RunContextID AS RunCont18_47_0_,
this_.SourceReport AS Sou19_47_0_
FROM Staging.vw_Liquidity this_
исключение
System.Data.SqlClient.SqlException (0x80131904): Invalid column name 'InitialAmountUSD'.
Как вы можете видеть, nhibernate заменил столбец LiquiditySourceItem 'Traderevalcurr' на 'InitialAmountUSD', который принадлежит сущности BusinessBreakdownStandardPosition. Эти сущности не имеют никакого отношения вообще. В противном случае SQL будет именно таким, как вы ожидаете (включая порядок столбцов).
наблюдения
- Неправильный столбец всегда является допустимым столбцом в другом сопоставленном объекте
- Неправильный столбец заменит существующий
- Эта проблема иногда возникает между другими организациями. Опять же, нет никакой связи между этими
Какие-нибудь мысли?
1 ответ
Я задал тот же вопрос на форуме групп Google NHibernate Users, и кто-то думает, что они выяснили основную причину (и также предложили решение):
https://groups.google.com/forum/
Код проблемы находится в PropertyPath.Equals(PropertyPath), который пытается определить равенство, используя только хэш-код. Это отлично работает для небольших баз кода, так как Object.GetHashCode() по умолчанию возвращает последовательный индекс объекта. Однако после сборки мусора эти индексы используются повторно, так как завершенные объекты удаляются и создаются новые объекты... что приводит к тому, что более одного объекта получают один и тот же хэш-код... Как только сборка мусора вступает в силу, пути свойств могут совместно использоваться. тот же самый хеш-код, который означает, что они в конечном счете перепутают свои настройщики для сталкивающихся свойств, таким образом, неправильные имена столбцов...
Если вы хотите исправить эту ошибку, вы можете исправить исходный код NH:
Если у вас есть собственная копия исходного кода NH, вы можете исправить ошибку, изменив строку #66 NHibernate / Mapping / ByCode / PropertyPath.cs из:
return hashCode == other.GetHashCode();
Для того, чтобы:
return hashCode == other.GetHashCode() && ToString() == other.ToString();
Пожалуйста, проверьте Google Group для получения полной информации о проблеме.