Отношение один к одному во время выполнения - PullRequest
3 голосов
/ 05 июля 2011

Я пытаюсь обновить старую CMS для использования NHibernate и не могу сильно отойти от исходной структуры базы данных.Вот бит, который вызывает проблему.Скажем, у меня есть следующие 2 таблицы:

Articles: 
- Id (PK, Identity)
- Title 
- Content 

Meta: 
- ArticleId (PK, FK to Articles)
- Description 
- Keywords 

Я создал следующие классы:

public class Article { 
  public virtual int Id { get; set; } 
  public virtual string Title { get; set; } 
  public virtual string Content { get; set; } 
} 

public class Meta : IComponent { 
  public virtual string Description { get; set; } 
  public virtual string Keywords { get; set; } 
}

public interface IComponent {
}

Обычно мета обычно отображается как компонент (или отношение один к одному) имущество по статье класс.Однако в приложении я создаю администратор может включить / отключить компоненты, которые применяются к статьям.Также я хотел бы, чтобы они расширили приложение, добавляя свои собственные компоненты, не касаясь класса Article.

По этим причинам я не могу добавить свойство против класса Article.Теперь, в идеале, в моем коде я хотел бы иметь возможность сказать:

var articles = session.Query<Article>()
    .Fetch(a = a.Component<Meta>())
    .Where(a => a.Component<Meta>().Keywords.Contains("Some Word"))
    .ToList();

// This wouldn't generate an extra SQL statement
var keywords = articles[0].Component<Meta>().Keywords;

, который будет генерировать следующий SQL (или аналогичный):

SELECT * FROM Articles INNER JOIN Meta ON Articles.Id = Meta.ArticleId ГДЕ Meta.Keywords LIKE '% Some Word%'

Можно ли отобразить метод Component, чтобы он выполнял внутреннее соединение для получения мета.Концепция кажется довольно простой, но я понятия не имею, с чего начать.Буду очень признателен за помощь.

Спасибо

Ответы [ 3 ]

0 голосов
/ 17 июля 2011

Учитывая это:

public class Article
{
    public virtual int ArticleId { get; set; }
    public virtual string Title { get; set; }
    public virtual string Content { get; set; }
}


public class Meta : IComponent
{
    public virtual Article Article { get; set; }

    public virtual int MetaId { get; set; }
    public virtual string Description { get; set; }
    public virtual string Keywords { get; set; }
}

AFAIK, вы не можете выбрать то, что не является частью сущности. Итак, из вашего примера, невозможно получить Meta из сущности Article.

Так что, если вы хотите получить другую информацию о Статье, вам просто нужно присоединить к ней Статью, а затем спроецировать полные данные в вашем Linq, например:

var articles =
        from a in s.Query<Article>()
        join m in s.Query<Meta>() on a equals m.Article
        where m.Keywords.Contains("Some Word")
        select new { a, m };

foreach(var x in articles)
    Console.WriteLine("{0} {1}", x.a.Title, x.m.Description);

Результирующий запрос:

select *
from [Article] article0_, [Meta] meta1_ 
where meta1_.ArticleId = article0_.ArticleId 
    and meta1_.Keywords like '%Some Word%'

Другой подход, начните с Meta, затем загрузите Article; по запросу это немедленно присоединится к статье, то есть без ленивой загрузки:

var artB =
        from m in s.Query<Meta>().Fetch(x => x.Article)
        where m.Keywords.Contains("Some Word")
        select m;

foreach (var x in artB)
    Console.WriteLine("{0} {1}", x.Article.Title, x.Description);

Результирующий запрос:

select *
from [Meta] meta0_ 
left outer join [Article] article1_ on meta0_.ArticleId = article1_.ArticleId 
where meta0_.Keywords like '%Some Word%'

Чтобы в Статье была только одна мета, укажите уникальную ссылку на мету:

create table Article
(
ArticleId int identity(1,1) not null primary key,
Title varchar(100) not null,
Content varchar(100) not null
);

create table Meta
(
    -- this prevents an Article having two Meta
ArticleId int not null references Article(ArticleId) unique, 

MetaId int identity(1,1) not null primary key,

Description varchar(100) not null,
Keywords varchar(100) not null
);

insert into Article(Title,Content) values('Great','Yeah')

insert into Meta(ArticleId, Description, Keywords) values(1,'Oh','Some Word');
0 голосов
/ 19 июля 2011

Будет ли интересно следующее решение?

Вы можете создать защищенное сопоставление для ваших компонентов и получить к ним доступ с помощью этого общедоступного универсального метода.

Таким образом, вы можете загружать и загружать ваши компоненты.

public class Article
{
    protected virtual ICollection<IComponent> Components { get; set; }

    public virtual T Component<T>() where T : IComponent
    {
        return Components.FirstOrDefault(c=>c.Type==typeof(T));
    }
}
0 голосов
/ 05 июля 2011

В вашем файле сопоставления NHibernate вы можете указать тип выборки. Спецификация для одного-к-одному XML находится в Документация NHibernate . Обратите внимание, что номер 5 имеет опцию «Присоединиться и выбрать», и по умолчанию он выбран.

...