在运行时映射一对一的关系

本文关键字:关系 一对一 映射 运行时 | 更新日期: 2023-09-27 18:01:17

我正在尝试升级一个旧的CMS使用NHibernate,不能从原来的数据库结构阻止太多。这是引起问题的位。假设我有以下两个表:

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 {
}

通常,Meta通常被映射为Article类上的组件(或一对一关系)属性。然而,在应用程序中,我正在构建一个管理员可以启用/禁用适用于文章的组件。此外,我希望他们扩展应用程序,以添加自己的组件,而不触及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。Id = Meta。元数据。关键词"%Some Word%"

是否可以映射Component方法,以便它执行内部连接来获取Meta ?这个概念似乎很简单,但我不知道从哪里开始。我真的很感激你的帮助。

谢谢

在运行时映射一对一的关系

鉴于此:

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; }
}

很抱歉,你不能获取不属于实体的东西。因此,从您的示例中,不可能从文章实体中获取Meta。

所以如果你想获取文章的其他信息,你只需要将文章加入他们,然后在你的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;在查询时,这将立即加入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%'

要保持一篇文章只有一个Meta,在Meta的引用上放一个Unique:

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');

在你的NHibernate映射文件中,你可以指定获取类型。一对一xml的规范在NHibernate文档中。注意,编号5有一个Join和Select选项,默认为Select。

您对下面的解决方案感兴趣吗?

你可以创建一个受保护的映射到你的组件,并从这个公共泛型方法访问这些组件。

这样你就可以选择急切/惰性加载你的组件。

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));
    }
}