2011-12-20 10 views
7

最近修正されたバグの多くは、エンティティフレームワークを使用してロードされたオブジェクトのナビゲーションプロパティにアクセスするときにnull参照の結果になります。私は自分の方法をどのように設計しているのかには欠陥がなければならないと確信しています。ここに例があります...EFナビゲーションプロパティへのアクセス時にNullReferenceExceptionを避ける

タスクには多くの役割が含まれており、各役割はユーザーを参照しています。

私はユーザーを誤ってこの ように私の状況を照会し、ロードしていないだろうことを考えると
public class Role 
{ 
    public int Id; 
    public int User_Id; 
    public string Type; 
} 

public class User 
{ 
    public int Id 
    public string Name; 
}  

public class Task 
{ 
    public int Id; 
    public string Name; 
    public string Status; 
    public List<Role> Roles; 
} 

...

var task = context.Tasks.Include(x=>x.Roles).FirstOrDefault; 

そして私は、このメソッドを呼び出す...

public void PrintTask(Task task) 
{ 
    Console.WriteLine(task.Name); 
    Console.WriteLine(task.Status); 

    foreach(var r in task.Roles) 
    { 
     Console.WriteLine(r.User.Name); //This will throw NRE because User wasn't loaded 
    } 
} 

私はこのメソッドをRolesとUserを読み込むつもりで構築しているかもしれませんが、次に私が使用するときには両方が必要であることを忘れるかもしれません。理想的には、メソッド定義で必要なデータがわかるはずですが、タスクとロールの両方を渡しても、私はまだロール - >ユーザーがいません。

これらの関係を参照する適切な方法と、この印刷方法のようなものに読み込まれていることを確認してください。私はより良いデザインに興味があるので、 "Use Lazy Loading"は私が探している答えではありません。

ありがとうございます!

編集:私は、私はこのようなタスクをロードすることができます知っている

...

私が知りたいのは何
var task = context.Tasks.Include(x=>x.Roles.Select(z=>z.User)).FirstOrDefault(); 

私は戻ってきたときに、それを使用するように私は私の方法を設計行う方法です6ヶ月後に私のエンティティにどのデータをロードする必要があるのか​​分かりますか?メソッド定義は、それを使用するのに必要なものを示していません。または、これらのNullReferenceに対してブロックする方法。より良いデザインが必要です。

+0

[の可能な重複を.NETでのNullReferenceExceptionとは何ですか?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net) –

+0

これは、他の 'NullReferenceException'問題と同じです。 –

答えて

1

非常に良い質問です。ここでは、NREの回避を強制するのではなく、呼び出し元にIncludeのものが必要な手掛かりを提供します。

最初の選択肢は、エンティティの保証された財産。むしろ、両方のエンティティを渡すために、発信者を強制:

public void PrintTask(Task task, User taskUser) 
{ 
    // ... 
} 

別のオプションは、それが必要とされるものについての手掛かり発信者をするような、あなたのメソッドのパラメータの名前です:

public void PrintTask(Task taskWithUser) 
{ 
    // ... 
} 
+0

ありがとうございます。私はあなたに、これに対する純粋な解決策がないという私の考えを立証したと思います。私は可能な限り簡単に財産へのアクセスを保ち、できるだけ個別に渡しています。 – BZink

2

Select拡張メソッドを使用して、熱心な負荷Usersを使用することができます。

var task = context.Tasks.Include(x => x.Roles) 
      .Include(x => x.Roles.Select(r => r.User)) 
      .FirstOrDefault(); 

編集:

SQL ServerのCE/Expressデータベースを使用してNRE

  • 統合テストを避けるために私は考えることができるいくつかの方法があります。ユニットテスト が偽のコンテキストで正しく動作しません。
  • エンティティを消費した場所の近くに読み込みます。したがって、 Includeはエンティティの使用場所の近くにあります。
  • エンティティを通過させずにDTO/ViewModelsを上位レイヤに渡す。
+0

申し訳ありませんが、私はユーザーを読み込む方法を知っていたことを明確にしていませんでした。実際には、2つではなく、1つのIncludeステートメントでこれを行うことができます。私が扱っているのは、参照が必要なことが明らかでないメソッドを使用することです。したがって、エンティティを間違ってロードしてメソッドを使用すると、null参照が返されます。 – BZink

+0

@BZink更新の回答 – Eranga

1

Userが遅延し、これはあなたが別のIncludeで修正する必要があります古典select N + 1問題であることけれどもあなたのループだけノートにロードする必要があります。

私は、根本的な問題は、どちらかである。この特定のRoleUserを持っている、またはこの特定のRoleUserはそのNameにnullセットを持っているしないと思います。ループ内でヌルを確認する必要があります

foreach(var r in task.Roles) 
{ 
    if (r.User != null) 
     Console.WriteLine(r.User.Name ?? "Name is null"); 
}