2014-07-15 96 views
0

我有兩個實體類UserConnectionUser具有Connection的集合。DbContext SqlQuery返回意外結果

class User 
{ 
    public string username {get; set;} 
    public ICollection<Connection> Connections {get; set;} 
} 

class Connection 
{ 
    public string ConnectionId {get; set;} 
    public string RoomName {get; set;} 
} 

我有了這個SQL查詢來檢索連接列表供用戶

// this code can be moved to the database as a stored procedure or view 
var sql = @"SELECT Users.UserName, Users.UserPix, 
           Connections.ConnectionId, Connections.RoomName, Connections.DateCreated 
      FROM   Users CROSS JOIN Connections 
      WHERE  (Users.UserName = @p0) AND (Connections.RoomName = @p1)"; 

return _context.Users.SqlQuery(sql, username, roomName).FirstOrDefault(); 

它返回一個用戶對象有一個空連接列表,而不是填充連接與返回的數據數據庫。 我試過用內連接替換交叉連接,但仍然是相同的結果

我不知道如何修改sql查詢以便返回實際數據。我該怎麼做,或者有什麼我失蹤?

+2

,我不認爲你可以返回與SqlQuery類子對象,除非你使用延遲加載。 – DavidG

+0

我認爲sql查詢可以通過一種方式進行修改,使得完美的轉換可能像是子查詢或其他東西。 – pmbanugo

+2

我不這麼認爲。 – DavidG

回答

0

因爲你沒有做什麼特別的東西在這裏(甚至沒有任何預測),您可以直接從上下文中返回實體,包括您想要的導航屬性:

eg考慮到這些實體:

class User 
{ 
    public string username { get; set; } 
    public ICollection<Connection> Connections { get; set; } 
} 

class Connection 
{ 
    public string ConnectionId { get; set; } 
    public string RoomName { get; set; } 
} 

一到一對多的關係應用戶之間存在於數據庫中 - >連接

爲了獲得特定用戶的所有連接所有用戶的所有連接,或者,或者你能想到的與同時保留您只需在System.Data.Entity

簽名使用.Include()QueryableExtensions關係的用戶/連接過濾器/集料等查詢的任意組合是這樣,如果你有興趣:

public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class

大約是EF一件好事,你可以渴望按需加載的子實體,而不必惹實體設計或添加不包含導航性能的新實體類

所以基本上它歸結爲:

using(YourDbContext context = new YourDbContext()) 
{ 
    var query = context.Users.Include(user => user.Connections); 

    // Do stuff with query 
} 

不要忘了延遲執行 - 如果你不知道延遲執行是什麼,查詢是如何建立起來的EF這是值得它 - 在一般的經驗法則是;查詢數據庫的次數越少,在C#中執行的處理越少(對於聚合數據庫類型的操作)應用程序執行的速度越快 - 因此,確保在完全構建查詢之前不要枚舉結果或者你將要使用LINQ to對象在.NET端進行所有的數字處理,而不是在SQL中它應該是的!

所以調用.ToList()或枚舉上面的查詢會得到你的結果

EF會翻譯任何你已經使用你所選擇的供應商(可能System.Data.SqlClient)做了正確的SQL方言。你不需要寫任何SQL ...

查詢的其他一些例子,你可以這樣做:

// Get me users called Fred including their connection details 
context.Users.Include(x => x.Connections).Where(u => u.Username == "Fred") 

// Get me users that are currently connected to "Game Room" 
context.Users.Include(x => x.Connections).Where(u => u.Connections.Any(c => c.RoomName == "Game Room") 

無的,這需要你寫任何SQL - 把運行這些查詢時看到EF做什麼在SQL跟蹤,通常會寫查詢比你曾經可能:)(僅有時沒有,它通常是當你正在做一些愚蠢的)

編輯

好,我看你是想篩選的用戶和通過導線回來的連接,在這種情況下,您需要將導航屬性明確加載爲單獨的查詢,或使用投影進行過濾。

顯式加載

var user = context.Users.First(x => x.UserName == username); 

context.Entry(user).Collection(x => x.Connections).Query().Where(x => x.RoomName == roomName).Load(); 

這也導致兩個查詢

投影

var usersConnections = context.Users 
.Where(u => u.UserName == userName) 
.Select(u => new 
{ 
    User = u, 
    Connections = u.Connections.Where(c => c.RoomName == roomName) 
}); 

這導致含有User屬性一個匿名類型和Connections財產。您可以隨時投射到一個已知類型太多,如果你需要通過某種域邊界的

這將是對數據源的單一查詢發送此

+0

我正在使用顯式加載方法,因爲它返回用戶和連接,投影樣式返回您必須投影/變換到的用戶和連接列表您的預期類型。 – pmbanugo

+0

如果你想要創建一個匿名類型,你可以在投影期間新增類型,除非你滿意多個查詢的小性能損失(並且它可能會很小) – Charleh

+0

我在LinqPad上測試了這兩個查詢,帶投影的那個返回了'IEnumerable '和'IEnumerable '。 'User'集合具有填充正確值的連接列表,'Connection'列表具有相同的連接。如果我投影到一個命名類型,它將必須是一個類型的屬性爲'IEnumerable '和'IEnumerable ',然後挑出第一個元素'type.Users.FirstOrDefault()'返回一個用戶並且它是關聯的連接。我猜顯式加載會有非常小的性能影響,或者我可能會使用投影? – pmbanugo

0

要做到這一點,我首先檢索/檢查用戶是否存在,那麼我找回它們的連接,並將其添加到連接列表中的用戶

public User GetUserAndConnections(string username, string roomname) 
{ 
    var user = _context.Users.Find(username); 
    if (user != null) 
    { 
     var connections = 
      _context.Users.Where(u => u.UserName == username) 
       .SelectMany(x => x.Connections.Where(p => p.RoomName == roomName)) 
       .ToList(); 
     user.AddExistingConnections(connections); 
    } 

    return user; 
} 
+0

爲什麼不直接使用實體框架和導航屬性呢?你爲什麼要加入?交叉連接從右表中爲左表中的每一行返回一行。你想要一個左連接 - 但實際上你不想要一個你想要實體框架爲你實現的連接 – Charleh

+0

在編寫針對數據源的簡單的非特定於提供者的特定SQL時,你基本上錯過了ORM的全部要點。你可以用'context.Users.Include(x => x.Connections)' – Charleh

+1

@Charleh得到相同的結果你可以舉個例子。你甚至可以編輯答案 – pmbanugo