我有以下列方式构建的3个表:
CREATE TABLE [User](
Id int NOT NULL,
Name varchar(50)
PRIMARY KEY (Id)
)
CREATE TABLE [Role](
Id int NOT NULL,
UserId int NOT NULL,
Name varchar(50),
PRIMARY KEY (Id),
FOREIGN KEY (UserId) REFERENCES [User](Id)
)
CREATE TABLE [Description](
Id int NOT NULL,
RoleId int NOT NULL,
Name varchar(50)
FOREIGN KEY (RoleId) REFERENCES [Role](Id)
)
正如你所看到的那样,它是嵌套两次的一对多关系。在代码中,我有以下类来表示它们:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Role> Roles { get; set; }
}
public class Role
{
public int Id { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
public IEnumerable<Description> Descriptions { get; set; }
}
public class Description
{
public int Id { get; set; }
public int RoleId { get; set; }
public string Name { get; set; }
}
现在我需要查询用户并获取随附的所有字段。我已经找到了使用QueryMultiple这样做的方法:
var queryOne = "SELECT Id, Name FROM [User] WHERE Id = 1";
var queryTwo = "SELECT r.Id, r.UserId, r.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId WHERE u.Id = 1";
var queryThree = "SELECT d.Id, d.RoleId, d.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId INNER JOIN [Description] d ON r.Id = d.RoleId WHERE u.Id = 1";
var conn = new SqlConnection();
using (var con = conn)
{
var result = con.QueryMultiple(queryOne + " " + queryTwo + " " + queryThree);
var users = result.Read<User>().FirstOrDefault();
var roles = result.Read<Role>();
var descriptions = result.Read<Description>();
if (users != null && roles != null)
{
users.Roles = roles;
Console.WriteLine("User: " + users.Name);
foreach (var role in users.Roles)
{
Console.WriteLine("Role: " + role.Name);
if (descriptions != null)
{
role.Descriptions = descriptions.Where(d => d.RoleId == role.Id);
foreach (var roleDescription in role.Descriptions)
{
Console.WriteLine("Description: " + roleDescription.Name);
}
}
}
}
}
结果是:
用户:鲍勃 作用:测试仪
描述:测试仪第一个描述
描述:测试仪第二描述
描述:测试仪第三描述
职责:经理
描述:经理第一描述
说明:经理第二说明
描述:经理第三描述
作用:程序员
描述:程序员第一描述
描述:程序员第二描述
描述:程序员第三个描述
主要问题: 虽然上面的代码工作,但感觉太乱了。我想知道是否有更好/更简单的方法来实现这一目标?
奖励积分: 还可以建议一种比使用内部联接更好的查询方法。我的目标是提高绩效。
编辑:
我也提出了选项二,但我再也认为它不是一个好的解决方案。使用选项2,我创建了一个第4个对象,它将包含3个对象的结果,例如:
public class Combination
{
public int UserId { get; set; }
public string UserName { get; set; }
public int RoleId { get; set; }
public string RoleName { get; set; }
public int DescriptionId { get; set; }
public string DescriptionName { get; set; }
}
然后我按照这样处理:
var queryFour = "SELECT u.Id as 'UserId', u.Name as 'UserName', r.Id as 'RoleId', r.Name as 'RoleName', d.Id as 'DescriptionId', d.Name as 'DescriptionName' FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId INNER JOIN [Description] d ON r.Id = d.RoleId WHERE u.Id = 1";
var conn = new SqlConnection();
using (var con = conn)
{
var myUser = new User();
var result = con.Query<Combination>(queryFour);
if (result != null)
{
var user = result.FirstOrDefault();
myUser.Id = user.UserId;
myUser.Name = user.UserName;
var roles = result.GroupBy(x => x.RoleId).Select(x => x.FirstOrDefault());
var myRoles = new List<Role>();
if (roles != null)
{
foreach (var role in roles)
{
var myRole = new Role
{
Id = role.RoleId,
Name = role.RoleName
};
var descriptions = result.Where(x => x.RoleId == myRole.Id);
var descList = new List<Description>();
foreach (var description in descriptions)
{
var desc = new Description
{
Id = description.DescriptionId,
RoleId = description.RoleId,
Name = description.DescriptionName
};
descList.Add(desc);
}
myRole.Descriptions = descList;
myRoles.Add(myRole);
}
}
myUser.Roles = myRoles;
}
Console.WriteLine("User: " + myUser.Name);
foreach (var myUserRole in myUser.Roles)
{
Console.WriteLine("Role: " + myUserRole.Name);
foreach (var description in myUserRole.Descriptions)
{
Console.WriteLine("Description: " + description.Name);
}
}
}
两种方法的结果输出相同,第二种方法使用1个查询而不是3个。
编辑2:需要考虑的事项,这3张表的数据经常更新。
编辑3:
private static void SqlTest()
{
using (IDbConnection connection = new SqlConnection())
{
var queryOne = "SELECT Id FROM [TestTable] With(nolock) WHERE Id = 1";
var queryTwo = "SELECT B.Id, B.TestTableId FROM [TestTable] A With(nolock) INNER JOIN [TestTable2] B With(nolock) ON A.Id = B.TestTableId WHERE A.Id = 1";
var queryThree = "SELECT C.Id, C.TestTable2Id FROM [TestTable3] C With(nolock) INNER JOIN [TestTable2] B With(nolock) ON B.Id = C.TestTable2Id INNER JOIN [TestTable] A With(nolock) ON A.Id = B.TestTableId WHERE A.Id = 1";
var gridReader = connection.QueryMultiple(queryOne + " " + queryTwo + " " + queryThree);
var user = gridReader.Read<Class1>().FirstOrDefault();
var roles = gridReader.Read<Class2>().ToList();
var descriptions = gridReader.Read<Class3>().ToLookup(d => d.Id);
user.Roles= roles;
user.Roles.ForEach(r => r.Properties = descriptions[r.Id].ToList());
}
}
答案 0 :(得分:1)
另一种方法(使用.Net Core的工作代码段):
创建视图myview 如 选择u.Id作为&#39; UserId&#39;,u.Name as&#39; UserName&#39;,r.Id as&#39; RoleId&#39;, r.Name as&#39; RoleName&#39;,d .Id as&#39; DescriptionId&#39;,d.Name as&#39; DescriptionName&#39; 来自用户u INNER JOIN角色r ON u.Id = r.UserId INNER JOIN描述d ON r.Id = d.RoleId;
然后您可以使用清洁查询,如下所示:
public List<Combination> GetData(int userId)
{
String query = "select * from myview" + " where userId = " + userId + ";";
using (System.Data.Common.DbConnection _Connection = database.Connection)
{
_Connection.Open();
return _Connection.Query<Combination>(query).ToList();
}
}
您的处理代码如下所示:
[注意:这可以进一步增强。]
public static void process (List<Combination> list)
{
User myUser = new User(); myUser.Id = list[0].UserId; myUser.Name = list[0].UserName;
var myroles = new List<Role>(); var r = new Role(); string currentRole = list[0].RoleName;
var descList = new List<Description>(); var d = new Description();
// All stuff done in a single loop.
foreach (var v in list)
{
d = new Description() { Id = v.DescriptionId, RoleId = v.RoleId, Name = v.DescriptionName };
if (currentRole == v.RoleName)
{
r = new Role() { Id = v.RoleId, Name = v.RoleName, UserId = v.UserId, Descriptions = descList };
descList.Add(d);
}
else
{
myroles.Add(r);
descList = new List<Description>(); descList.Add(d);
currentRole = v.RoleName;
}
}
myroles.Add(r);
myUser.Roles = myroles;
Console.WriteLine("User: " + myUser.Name);
foreach (var myUserRole in myUser.Roles)
{
Console.WriteLine("Role: " + myUserRole.Name);
foreach (var description in myUserRole.Descriptions)
{
Console.WriteLine("Description: " + description.Name);
}
}
}
从性能角度来看,Inner Joins比其他形式的查询更好,如子查询,Co相关子查询等。但最终所有内容都归结为SQL执行计划。
答案 1 :(得分:1)
您的第一个选项可以简化为以下内容。它删除了深层控制结构嵌套。您可以将其推广到更深的嵌套,而无需添加更多嵌套/复杂性。
var queryOne = "SELECT Id, Name FROM [User] WHERE Id = 1";
var queryTwo = "SELECT r.Id, r.UserId, r.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId WHERE u.Id = 1";
var queryThree = "SELECT d.Id, d.RoleId, d.Name FROM [User] u INNER JOIN [Role] r ON u.Id = r.UserId INNER JOIN [Description] d ON r.Id = d.RoleId WHERE u.Id = 1";
var conn = new SqlConnection();
using (var con = conn)
{
var gridReader = con.QueryMultiple(queryOne + " " + queryTwo + " " + queryThree);
var user = gridReader.Read<User>().FirstOrDefault();
if (user == null)
{
return;
}
var roles = gridReader.Read<Role>().ToList();
var descriptions = gridReader.Read<Description>().ToLookup(d => d.RoleId);
user.Roles = roles;
roles.ForEach(r => r.Descriptions = descriptions[r.Id]);
}
在性能方面,它的行为与您的第一个选项相同。
我不会选择您的第二个选项(或类似的基于一个选项):如果您有R角色,并且每个角色平均有D个描述,您将查询6 * R * D单元而不是2 + 3 * R + 3 * d。如果R和D很高,那么您将查询更多数据,与运行3个查询而不是1相比,反序列化的成本会更高。
答案 2 :(得分:0)
我回答你的问题:
摘要。你应该把事情分解成他们做的不同工作。 C#为您提供了函数和类,它们可以帮助您清理很多代码。
如果你可以查看一大块代码然后说&#34;这个块基本上是拿东西并用它来做一些结果...&#34;然后创建一个名为SomeResult DoBlah(SomeThing thing)
的函数
我的完整答案:
你可以做的一些事情会让这个更干净。您可以为自己做的最大的事情可能是分离出一些责任/担忧。你有很多不同的事情发生在一起,所有这些都在一个地方,依赖于一切都像它一样,永远。如果你改变某些东西,你将不得不改变所有地方的东西,以确保一切正常。这被称为&#34;耦合&#34;,耦合是坏的...嗯 - 凯。
在最广泛的层面上,您有三种不同的事情:1。您正在尝试将逻辑应用于用户和角色(即您希望获取角色中所有用户的信息。)2。您正在尝试从数据库中提取信息,并将其放入用户,角色和描述对象中。 3.您正在尝试显示有关用户,角色和描述的信息。
所以如果我是你,那我至少会有3门课。 \
Program
这是您的应用程序的驱动程序。它应该有一个主要功能,应该使用其他3个类。
DisplayManager
可以负责将信息写入控制台。它应该使用IEnumerable<User>
构造,并且应该有一个公共方法public void DisplayInfo()
,它将其用户列表写入控制台。
UserDataAccess
,可用于从数据库中提取用户列表。我希望它为public IEnumerable<User> GetUsersByRoleID(int roleID)
这样,您的主程序看起来像这样:
public static void Main(String[] args)
{
int roleID = 6;
UserDataAccess dataAccess = new UserDataAccess();
IEnumerable<User> usersInRole = dataAccess.GetUsersByRoleID(roleID);
DisplayManager displayManager = new DisplayManager(usersInRole);
displayManager.DisplayInfo();
}
老实说,这是我要做的简化版本,如果问题有更多功能,那么只需获取一个特定的信息。您应该研究SOLID原则。特别是&#34;单一责任&#34;和&#34;依赖倒置&#34;,这些原则可以真正清理一些&#34;凌乱&#34; /&#34;臭&#34;代码。
接下来,就实际数据访问而言,由于您使用的是dapper,因此您应该能够使用内置dapper功能来映射嵌套对象。我相信this link在这方面对你有所帮助。