DbContext SqlQuery返回意外结果

时间:2014-07-15 16:15:33

标签: c# sql entity-framework code-first dbcontext

我有两个实体类UserConnection,而User有一个Connection的集合。

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 Query,它检索用户的连接列表

// 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 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:0)

由于您在这里没有做任何特别的事情(甚至没有任何投影),您可以直接从上下文返回实体,包括您想要的导航属性:

e.g。鉴于这些实体:

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

您的数据库中User - &gt;之间应存在一对多关系连接

为了获得特定用户的所有连接,或所有用户的所有连接,或者您可以在用户/连接上使用过滤器/聚合等来考虑的任何查询组合,同时保留关系,您只需使用{{1来自.Include()

中的QueryableExtensions

如果您有兴趣,签名也是如此:

System.Data.Entity

关于EF这是一件好事,您可以根据需要急切加载子实体,而不必弄乱实体设计或添加不包含导航属性的新实体类

所以基本上归结为:

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

不要忘记延期执行 - 如果您不知道延迟执行是什么,以及如何在EF中构建查询值得查看 - 一般的经验法则是;击中数据库的查询越少,您在C#中执行的操作越少(对于像聚合这样的数据库样式操作),您的应用程序的执行速度就越快 - 因此请确保您不必枚举结果,直到您完成为止。完全构建了查询,或者您将使用LINQ to对象在.NET端执行所有数字运算,而不是在SQL应该使用它!

所以调用using(YourDbContext context = new YourDbContext()) { var query = context.Users.Include(user => user.Connections); // Do stuff with query } 或枚举上述查询会得到结果

EF会使用您选择的提供程序(可能是.ToList())将您所做的任何事情翻译成正确的SQL方言。你不需要编写任何SQL ......

您可能会执行的其他一些查询示例:

System.Data.SqlClient

这些都不需要你编写任何SQL - 在运行这些查询时放置SQL跟踪以查看EF的作用,并且通常它会比你更好地编写查询:)(只有在有时它不会通常当你做一些愚蠢的事情时)

修改

好的,我看到你试图过滤用户和通过线路返回的连接,在这种情况下,您需要显式加载导航属性作为单独的查询或使用投影进行过滤

e.g。

明确加载

// 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")

这确实会产生两个查询

<强>投影

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属性的匿名类型。如果您需要通过某种域边界发送它,您也可以始终投射到已知类型

这将是针对数据源的单个查询