我的对象看起来像这样:
class
{
int a;
object b;
IList<string> c;
}
所有字段都从数据库中填充,并且集合正在进行延迟初始化,这是可取的。 现在,我的问题是我想将此对象发送到Web服务。但由于该系列是懒散的,我无法做到。有人可以给我一个想法或方向或一些示例代码,我可以查看我的问题。
在通过网络发送之前,我想要一种强制初始化此列表的通用方法。此外,我有这样的多个对象,所以这样做的通用方法会很棒。现在,要做到这一点,我使用这种方法:
public static T Unlazy<T>(this T persistentCollection)
{
if (persistentCollection is IPersistentCollection)
{
IPersistentCollection collection = (IPersistentCollection)persistentCollection;
collection.SetCurrentSession(session.GetSessionImplementation());
collection.ForceInitialization();
}
}
但是由于某种原因,这会将对象引用设置为null异常
答案 0 :(得分:2)
没有灵丹妙药可以让'unlazy'成为一个集合。您很可能会触发SELECT + 1问题。你真的需要在对象图上执行一个急切的负载。
你可以在映射中执行此操作,但我建议您将映射中的所有内容保留为惰性。从数据库中提取时,最好的策略是覆盖此行为。如果使用HQL,则查询类似于
"from class
left join **fetch** class.b b
left join **fetch** class.c c"
如果您正在使用ICriteria,请使用SetFetchMode()。
执行List()时,您会注意到重复的根实体对象。这是预期的行为。如果这不是您想要的,最简单的解决方案是将所有内容加载到HashedSet中并枚举结果。
N.B。您只能急切地为每个实体加载一个集合。这是SQL的限制。
答案 1 :(得分:1)
public static T UnproxyObjectTreeWithNulls<T>(this T persistentObject, ISessionFactory sessionFactory) // Force Null Initialization
{
// Получаем настоящий (не proxy) тип элемента
var persistentType = persistentObject.GetUnproxiedType();
if (persistentType == null) // persistentObject is IPersistentCollection
//return persistentObject.UnproxyCollectionObjectTreeWithNulls(sessionFactory);
return persistentObject;
// Получаем NHibernate-метаданные класса
var classMetadata = sessionFactory.GetClassMetadata(persistentType);
// Iterate through each property and unproxy entity types
for (int i = 0; i < classMetadata.PropertyTypes.Length; i++)
{
var nhType = classMetadata.PropertyTypes[i];
var propertyName = classMetadata.PropertyNames[i];
var propertyInfo = persistentType.GetProperty(propertyName);
if (nhType.IsCollectionType)
{
var propertyValue = propertyInfo.GetValue(persistentObject, null);
if (propertyValue == null) continue; // пропускаем пустые свойства
if (propertyValue is IPersistentCollection)
{
propertyInfo.SetValue(persistentObject, null, null);
}
else
propertyValue.UnproxyCollectionObjectTreeWithNulls(sessionFactory);
}
else if (nhType.IsEntityType)
{
var propertyValue = propertyInfo.GetValue(persistentObject, null);
if (propertyValue == null) continue; // пропускаем пустые свойства
if (propertyValue is INHibernateProxy)
// Если это прокси объект, то мы зануляем его (и отменяем lazy-загрузку)
propertyInfo.SetValue(persistentObject, null, null);
else
// Иначе идем дальше по графу и зануляем поля внутри него
propertyInfo.SetValue(persistentObject, propertyValue.UnproxyObjectTreeWithNulls(sessionFactory), null);
}
}
return persistentObject;
}
/// <summary>
/// Инициализируем ненужные proxy-коллекции null'ами
/// </summary>
public static T UnproxyCollectionObjectTreeWithNulls<T>(this T persistentCollection, ISessionFactory sessionFactory)
{
if (!(persistentCollection is IPersistentCollection)) // если это IPersistentCollection, то мы должны занулить её
{
var c = persistentCollection as System.Collections.ICollection;
if (c == null) return persistentCollection;
foreach (var item in c)
item.UnproxyObjectTreeWithNulls(sessionFactory); // проделываем все тоже самое внутри элементов коллекции (а кто сказал что будет легко?)
}
return persistentCollection;
}
/// <summary>
/// Gets the underlying class type of a persistent object that may be proxied
/// </summary>
public static Type GetUnproxiedType<T>(this T persistentObject)
{
var proxy = persistentObject as INHibernateProxy;
if (proxy != null)
return proxy.HibernateLazyInitializer.PersistentClass;
var proxyCollection = persistentObject as IPersistentCollection;
if (proxyCollection != null)
return null;
return persistentObject.GetType();
}
祝你好运=)
答案 2 :(得分:0)
我不确定我是否正确理解了这个问题。你有一个服务器端的数据库 - 你使用NHibernate - 使用延迟加载。转移到客户端时,您的对象出现问题?
如果是这种情况,您需要做的是停止延迟加载。由于客户端无权访问数据库,因此无法从客户端加载延迟加载的数据。在将数据传输到客户端之前,您需要确保在服务器端加载数据。解决这个问题的最简单方法是停止延迟加载。如果不是,则需要确保在将保持对象返回到客户端之前加载了惰性数据。如果您有一个从数据库中获取数据的服务,执行某些操作并返回此数据,您也可以立即从数据库中加载数据。
希望这会有所帮助。
答案 3 :(得分:0)
简短的回答:尽管你可能比你想要的更多地让NHibernate的手感到痛苦(因为它让我感到痛苦),但我同意James L的观点,即渴望获取似乎是最好的方法。
根据Mauricio的链接,我们发现直接在WCF上传递NHibernate实体并不能很好地工作,因为你并没有真正传递WCF喜欢的POCO对象,而是使用NHibernate特定集合的代理对象。
出于这个原因,我们已成功使用Automapper将实体转换为相应的DTO,以便通过WCF传递。这样做的副作用是,它基本上不会让一切变得松懈,因为它会遍历对象图,以便将它放在另一个对象中。
虽然这有效,但我必须非常同意詹姆斯关于选择N + 1问题的警告;我们在我们的网络应用程序中发现,单个页面加载可能导致数百个单独的数据库调用。通过首先使用HQL或Criteria进行预先获取,您可以真正减少数量。
您只能急切地为每个实体加载一个集合。这是SQL的限制。
我不确定这是否属实,但我肯定会建议您在单独的查询中加载集合;我认为你可以在一个查询中加载它们,但是在底层SQL中它们将被留下外连接,这将导致笛卡尔积非常容易失控。
像往常一样,Ayende Rahien有described all of this very well already,以及如何使用Futures至少在一次往返中相对轻松地完成所有这些单独的收集查询的描述。