2013-03-14 1 views
2

Я пытаюсь использовать формулу для сопоставления свойства типа ICollection, однако, независимо от того, какой метод я использую для определения типа внутри отображения, nHibernate вызывает ошибку.Формула карты для IEnumerable bycode

No parameterless constructor defined for this object.

Вот файл сопоставления

this.Property(
    x => x.AllChildIds, 
    m => 
    { 
     m.Type<NHibernate.Type.ListType>(); 
     m.Access(Accessor.Field); 
     m.Formula(@"(WITH [Child] ([Id], [ParentId]) 
      AS (SELECT [hs0].[Id], 
         [hs0].[ParentId] 
       FROM [Client].[dbo].[HierarchySet] [hs0] (NOLOCK) 
       WHERE [hs0].[ParentId] IN (SELECT [hs1].[Id] 
            FROM [Client].[dbo].[HierarchySet] [hs1] (NOLOCK) 
            WHERE [hs1].[Id] = Id /* @p0 */) 
       UNION ALL 


       SELECT [Children].[Id], 
         [Children].[ParentId] 
       FROM [Client].[dbo].[HierarchySet](NOLOCK) AS [Children] 
         JOIN [Child] 
         ON [Children].[ParentId] = [Child].[Id]) 


     SELECT [Child].[Id] 
     FROM [Child] 
    )"); 
}); 

А вот мой класс

private readonly ICollection<long> allChildIds; 
public virtual IEnumerable<long> AllChildIds { get { return this.allChildIds; } } 

Если изменить тип в файле отображения на

m.Type<NHibernate.Type.GenericListType<NHibernate.Type.Int64Type>>();

Затем я получаю ошибку Could not determine type for: System.Collections.Generic.IEnumerable

Я знаю, что SQL сложный, но верно, что на него не должно повлиять?

Редактировать Мои NHibernate конфигурации сеанса

private static Configuration ConfigureNHibernate() 
{ 
    var configration = new Configuration(); 

    configration.SessionFactoryName("SessionFactoryName"); 
    configration.DataBaseIntegration(db => 
            { 
             db.Dialect<MsSql2005Dialect>(); 
             db.Driver<SqlClientDriver>(); 

             db.IsolationLevel = IsolationLevel.ReadUncommitted; 
             db.ConnectionStringName = "database"; 
             db.BatchSize = 20; 
             db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote; 
            }); 

    if (ConfigurationManager.AppSettings["nhibernate-cache"] != null) 
    { 
    configration.Cache(
         x => 
         { 
          x.DefaultExpiration = 300; 
          x.UseMinimalPuts = true; 
          x.RegionsPrefix = "client-"; 
          x.Provider<SysCacheProvider>(); 
          x.UseQueryCache = true; 
         }); 
    } 

    var mapper = new ModelMapper(); 
    mapper.AddMappings(typeof(MessageInMap).Assembly.GetTypes()); 
    var domainMapping = mapper.CompileMappingForAllExplicitlyAddedEntities(); 

    configration.AddMapping(domainMapping); 

    configration.AddAuxiliaryDatabaseObject(new SimpleAuxiliaryDatabaseObject(@" 
          CREATE VIEW [Children] 
          AS 
          WITH [Child] ([Id], [ParentId]) 
          AS (
            SELECT 
              [hs0].[Id], 
              [hs0].[ParentId] 
            FROM 
              [isnapshot.Client].[dbo].[HierarchySet] (NOLOCK) AS [hs0] 

            UNION ALL 

            SELECT 
              [Children_].[Id], 
              [Children_].[ParentId] 
            FROM 
              [isnapshot.Client].[dbo].[HierarchySet] (NOLOCK) AS [Children_] 
            JOIN [Child] ON [Children_].[ParentId] = [Child].[Id] 
          ) 
          GO", "DROP VIEW [Children]")); 

    return configration; 
} 
+0

Что произойдет, если опустить спецификацию типа? В соответствии с [документацией] (http://nhforge.org/doc/nh/en/index.html#mapping-declaration-property) это должно быть хорошо: «Если вы не укажете тип, NHibernate будет использовать отражение по названному свойству, чтобы угадать правильный тип NHibernate ». – bigge

+0

Если я не укажу тип, то ошибка изменится на ICollection 'Не удалось определить тип для: System.Collections.Generic.ICollection' – JConstantine

+0

ОК, это к сожалению. Вы пытались исключить, что запрос является частью проблемы? Работает ли он для более простых запросов (либо с тем же, либо с другим типом результата) – bigge

ответ

1

Вы должны отобразить его в качестве набора (сумки, список и т.д.) компонентов, а не в собственность. Затем используйте sub-select для его извлечения.

Мой первый предположение, чтобы отобразить его следующим образом

c.Set(m => m.ChildrenIds, 
    x => 
     { 
      x.Access(Accessor.Field); 
      x.Key(k => k.Column("ParentId")); 
      x.Subselect(@"WITH [Child] ([Id], [ParentId]) 
AS 
(
    SELECT 
     [hs0].[Id], 
     [hs0].[ParentId] 
    FROM 
     [HierarchySet] (NOLOCK) AS [hs0] 

    UNION ALL 

    SELECT 
     [Children].[Id], 
     [Children].[ParentId] 
    FROM 
     [HierarchySet] (NOLOCK) AS [Children] 
    JOIN 
     [Child] ON [Children].[ParentId] = [Child].[Id] 
) 

SELECT * 
FROM [Child]"); 
     }, 
    x => x.Element(e => e.Column("Id"))); 

Однако это не работает, так как WITH пункт не хочет быть внутри подвыборки. Сгенерированный запрос:

SELECT 
    childrenid0_.ParentId as ParentId0_, 
    childrenid0_.Id as Id0_ 
FROM (WITH [Child] ([Id], [ParentId]) 
AS 
(
    SELECT 
     [hs0].[Id], 
     [hs0].[ParentId] 
    FROM 
     [HierarchySet] (NOLOCK) AS [hs0] 

    UNION ALL 

    SELECT 
     [Children].[Id], 
     [Children].[ParentId] 
    FROM 
     [HierarchySet] (NOLOCK) AS [Children] 
    JOIN 
     [Child] ON [Children].[ParentId] = [Child].[Id] 
) 

SELECT * 
FROM [Child]) childrenid0_ 
WHERE [email protected];@p0 = 2 [Type: Int32 (0)] 

Таким образом, в этом случае единственное решение - создать представление и сопоставить его.

Добавить объект базы данных конфигурации

configuration.AddAuxiliaryDatabaseObject(new SimpleAuxiliaryDatabaseObject(@" 
    CREATE VIEW [Children] 
    AS 
    WITH [Child] ([Id], [ParentId]) 
    AS (
     SELECT 
      [hs0].[Id], 
      [hs0].[ParentId] 
     FROM 
      [HierarchySet] (NOLOCK) AS [hs0] 

     UNION ALL 

     SELECT 
      [Children].[Id], 
      [Children].[ParentId] 
     FROM 
      [HierarchySet] (NOLOCK) AS [Children] 
     JOIN [Child] ON [Children].[ParentId] = [Child].[Id] 
    ) 

    SELECT * 
    FROM [Child]", "DROP VIEW [Children]")); 

При использовании функции SchemaExport/SchemaUpdate создать/обновить схему базы данных - NHibernate создаст вид. Если вы не используете функции SchemaExport/SchemaUpdate, вам необходимо создать представление вручную.

CREATE VIEW [Children] 
AS 
    WITH [Child] ([Id], [ParentId]) 
    AS (
     SELECT 
      [hs0].[Id], 
      [hs0].[ParentId] 
     FROM 
      [HierarchySet] (NOLOCK) AS [hs0] 

     UNION ALL 

     SELECT 
      [Children].[Id], 
      [Children].[ParentId] 
     FROM 
      [HierarchySet] (NOLOCK) AS [Children] 
     JOIN [Child] ON [Children].[ParentId] = [Child].[Id] 
    ) 

    SELECT * 
    FROM [Child] 

Отображение:

c.Set(m => m.ChildrenIds, 
    x => 
     { 
      x.Access(Accessor.Field); 
      x.Key(k => k.Column("ParentId")); 
      x.Table("Children"); // or x.Subselect("select * from Children") 
     }, 
    x => x.Element(e => e.Column("Id"))); 
+0

Это похоже на правильное решение, однако при поиске результатов у него возникают трудности с поиском представления. Я попытался указать схему в сопоставлении, а также префикс представления create с именем базы данных. Когда я пытаюсь получить доступ к полю, он говорит, что не удалось инициализировать сбор, '' Недопустимое имя объекта «Дети». «Где именно создается представление? – JConstantine

+0

Очень странно, поскольку он отлично работает для меня :(В моем случае создается представление под .dbo. – hazzik

+0

Можете ли вы попробовать заменить 'x.Table (« Дети »)' на 'x.Subselect (" select * from Children ")'? – hazzik