2011-07-05 11 views
3

NHibernateを使用するために古いCMSをアップグレードしようとしているので、元のデータベース構造を大幅に妨げることはできません。ここに問題を引き起こしているビットがあります。実行時に1対1の関係をマップする

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

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

私が作成した以下のクラス:通常のメタは、通常、コンポーネント(または1対1の関係)としてマップされます

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

財産上の私は、次の2つのテーブルを持っていると言います記事クラス。しかし、私は管理者を構築するアプリケーションでは、記事に適用されるコンポーネントを有効/無効にすることができます。また、私は彼らがArticleクラスに触れることなく独自のコンポーネントを追加するためにアプリケーションを拡張したいと思います。

私はArticleクラスに対してプロパティを追加することはできません。今、理想的に私のコードでは、私が言うことができるしたいと思います:

次のSQL(または類似)を生成する
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; 

を:

SELECT * FROM記事INNERメタON Articles.Id =メタを登録しよう.ArticleId Meta.Keywords LIKE '%Some Word%'

Metaを取得するために内部結合を行うようにComponentメソッドをマップすることは可能ですか?このコンセプトはかなりシンプルなようですが、どこから始めるのか分かりません。私は本当に助けに感謝します。

ありがとう

+0

「...管理者はコンポーネントを有効/無効にすることができます...」とはどういう意味ですか? –

答えて

0

この考える:

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

私の知る限り、あなたは、エンティティの一部ではない何かを取得することはできません。あなたの例から、Articleエンティティから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%' 

もう1つのアプローチは、メタから始まり、記事を取得します。

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

はメタの参照にユニークを置く、唯一のメタを有する物品を保持するには:

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

結果のクエリ:クエリに、これはすぐに記事に参加します、何の遅延ロード、すなわちん

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

あなたの答えをありがとう。 2番目のオプションは私に強いタイピングを与えますが、もし私が別のコンポーネントにMeta2と答えたなら、それは落ちます。私は最初のアプローチでアイデアが好きです(linqクエリは単純で、追加のコンポーネントにジョインを追加することができます)。しかし、それは私が理想的に好きなものより少し多くの作業を必要とします。私はそれが強く型付けされるようにビューの追加モデルを作成しなければなりません。私がarticle.Component ().Description(Articleクラス/マッピングがそれに応じて変更できることに注意してください)と言うことができる方法があれば、それは素晴らしいことでしょう。とにかくあなたの助けを歓迎しない場合。 – nfplee

+0

良いアイデアがあります:-)もし誰かがLinq-NHで実現可能なら、これでは不十分です: 'Fetch(a => a.Component ())'投影法には別名が必要ですコンパイラによって)、これをニックピックするには、これはAPI 'Fetch(a => new {art = a、meta = a.Component ()})'になります。彼らがそのアイディアを受け入れることができるなら、NHibernateから誰かに電子メールを送ろうとしてください。 –

+0

OKです。お元気です。 – nfplee

0

あなたのNHibernateマッピングファイルで、フェッチタイプを指定できます。 one-to-one xmlの仕様はNHibernate Documentationにあります。 5番はJoinとSelectというオプションがあり、デフォルトではselectになっています。

+0

これはどのようにしてコンポーネント()をマップするのに役立ちますか? – nfplee

+0

xmlはテーブル間のマッピングの詳細を決定します。コンポーネントはあなたのオブジェクトの一種に過ぎません。 Articleクラスのメタ(Meta of Compontent )というプロパティをArticleクラスに配置し、そのプロパティと構成XMLの内部のメタ(1対1)の関係を定義する必要があります。 – Zoidberg

+0

まだ混乱しています。あなたは、より詳細な答えを提供してくれますか? – nfplee

0

次の解決策がありますか?

コンポーネントへの保護されたマッピングを作成し、このパブリックジェネリックメソッドからアクセスすることができます。

このようにして、コンポーネントを熱心に/怠惰に読み込むことができます。

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

こんにちは、これは有望そうです。私はどのように私はコンポーネントをマップするかはかなり分かりません。私の推測では、どのコンポーネントが記事に当てはまるかをデータベースにテーブルが必要です。あなたの考えを教えてください。ありがとう – nfplee