如何查询复杂的 n:m 关系以在 C# 中获取 IEnumerable
How to query complex n:m relationship for getting an IEnumerable in C#
在我的 sql 数据库中,我有如下所示的多对多 (n:m) 关系:
对于我的 asp.net Web 应用程序(使用 EF 和数据库优先方法),我需要提取属于 table 质量的所有组的所有记录(使用 C# 和 linq)给定组所属的同一协会s。
下面我给出了示例数据来演示 G_Id = 1
的给定组所需的查询结果
从示例数据中我们看到给定的组是协会 A_Id = 1 和 A_Id = 2 的成员。因此我们需要查看哪些其他组是这些协会的成员。在给定的数据中,我们看到 G_Id = 2 的组也是关联 A_Id = 1 的成员,G_Id = 3 的组也是 [=43 的成员=] = 2。
因此,我们必须从 table 质量中收集所有记录,其中 G_Id = 1,G_Id = 2 和 G_Id = 3,查询结果为:
我能够编写一个有效的 C# IEnumerable 控制器操作(见下文)来获取一个组的所有 Quality 记录,但未能提出一个也说明的查询关联组的记录。
public IEnumerable<Quality> Get()
{
int g_Id = myAppPrincipal.G_Id;
var group = UnitOfWork.GetById<Group>(g_Id);
var groupQualities = group.Qualities;
// Qualities is a public virtual ICollection of Quality inside the class Group
IList<Quality> result = groupQualities.Select(entity => new Quality()
{
Q_Id = entity.Q_Id,
Description = entity.Description,
...
}).ToList();
return result;
}
如果有人可以帮助我构建正确的查询,我将不胜感激。
见下文class结构:
public class Entity
{
/// <summary>
/// Unique identifier
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id
{
get;
set;
}
/// <summary>
/// Concurrency check property
/// </summary>
[Timestamp]
public virtual byte[] Version
{
get;
set;
}
/// <summary>
/// Determines whether entity is new
/// </summary>
[Bindable(false)]
[ScaffoldColumn(false)]
public virtual bool IsNew
{
get
{
return Id == 0;
}
}
public override string ToString()
{
return this.DumpToString();
}
}
public class Association: Entity
{
public string Description { get; set; }
}
public class Assoc_Group: Entity
{
public int A_ID { get; set; }
public int G_ID { get; set; }
[IgnoreDataMember]
public override byte[] Version
{
get;
set;
}
}
public class Group: Entity
{
public Group()
{
Qualities = new List<Quality>();
}
public string Description { get; set; }
public virtual ICollection<Quality> Qualities { get; set; }
}
public class Quality: Entity
{
public int? G_ID { get; set; }
public string Description { get; set; }
public virtual Group Group { get; set; }
[IgnoreDataMember]
public override byte[] Version
{
get;
set;
}
}
和相应的映射:
public class AssociationMap : EntityTypeConfiguration<Association>
{
public AssociationMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
Property(t => t.Description)
.IsRequired();
// Table & Column Mappings
ToTable("Association");
Property(t => t.Id).HasColumnName("A_ID");
Property(t => t.Description).HasColumnName("Description");
Ignore(t => t.Version);
}
}
class Assoc_GroupMap : EntityTypeConfiguration<Assoc_Group>
{
public Assoc_GroupMap()
{
ToTable("Assoc_Group");
Property(t => t.Id).HasColumnName("AG_ID");
Property(t => t.A_ID).HasColumnName("A_ID");
Property(t => t.G_ID).HasColumnName("G_ID");
Ignore(property => property.Version);
}
}
public class GroupMap : EntityTypeConfiguration<Group>
{
public GroupMap()
{
// Primary Key
HasKey(t => t.Id);
// Table & Column Mappings
ToTable("Group");
Property(t => t.Id).HasColumnName("G_ID");
Ignore(t => t.Version);
}
}
public class QualityMap : EntityTypeConfiguration<Quality>
{
public QualityMap()
{
// Primary Key
HasKey(t => t.Id);
// Table & Column Mappings
ToTable("Quality");
Property(t => t.Id).HasColumnName("Q_ID");
Property(t => t.G_ID).HasColumnName("G_ID");
...
// Relationships
HasOptional(t => t.Group)
.WithMany(t => t.Qualities)
.HasForeignKey(d => d.G_ID);
}
}
据我了解,您需要属于某个特定组的所有父协会的所有组品质,因此:
public IEnumerable<Quality> Get()
{
int g_Id = myAppPrincipal.G_Id;
var group = UnitOfWork.GetById<Group>(g_Id);
var associatedGroups = group.Assoc_Group.Groups;
var qualities = new List<Quality>();
foreach (var assoc in associatedGroups.Select(t => t.Association).Distinct())
{
qualities.AddRange(assoc.Assoc_Groups.SelectMany(t => t.group.Qualities).ToList());
}
var result = qualities.Distinct();
return result;
}
在上述情况下,我希望 GetById 也能获取关联实体,如果没有,则必须更改为 GetById包括关联实体的方法。否则,您可以通过单独调用来单独获取关联。
您可以像这样使用嵌套查询:
DECLARE @GivenGroup AS Int = 1
SELECT DISTINCT Q_Id
FROM Quality q
WHERE q.G_Id IN
(
SELECT DISTINCT G_Id
FROM Assoc_Group ag
WHERE ag.A_Id IN
(
SELECT a.A_Id
FROM Association a
INNER JOIN Assoc_Group ag ON a.A_Id = ag.A_Id
WHERE ag.G_Id = @GivenGroup
)
)
至少 SQL 服务器。
您可以在这里尝试:http://sqlfiddle.com/#!18/6dcc6/2
要使用 C# 查询您的 SQL 数据库,您可以使用 SqlClient
var SQL = new StringBuilder();
// Build SQL here. use SQL.AppendLine("SELECT ...")
var cn = new
System.Data.SqlClient.SqlConnection(your_sql_connection_string);
var da = new System.Data.SqlClient.SqlDataAdapter(SQL.ToString(), cn);
var datatbl = new DataTable();
da.SelectCommand.Parameters.Add("@GivenGroup", SqlDbType.Int).Value = 1; // use parameterization always.
try {
da.SelectCommand.Connection.Open();
da.Fill(datatbl);
}
catch (Exception ex)
{
// error handling here
}
finally
{
cn.Close();
cn.Dispose();
da.Dispose();
}
// read from datatable
foreach (DataRow row in datatbl.Rows)
{
Console.WriteLine(row["G_Id"].ToString());
}
可能有错误。
在我的 sql 数据库中,我有如下所示的多对多 (n:m) 关系:
对于我的 asp.net Web 应用程序(使用 EF 和数据库优先方法),我需要提取属于 table 质量的所有组的所有记录(使用 C# 和 linq)给定组所属的同一协会s。
下面我给出了示例数据来演示 G_Id = 1
的给定组所需的查询结果从示例数据中我们看到给定的组是协会 A_Id = 1 和 A_Id = 2 的成员。因此我们需要查看哪些其他组是这些协会的成员。在给定的数据中,我们看到 G_Id = 2 的组也是关联 A_Id = 1 的成员,G_Id = 3 的组也是 [=43 的成员=] = 2。 因此,我们必须从 table 质量中收集所有记录,其中 G_Id = 1,G_Id = 2 和 G_Id = 3,查询结果为:
我能够编写一个有效的 C# IEnumerable 控制器操作(见下文)来获取一个组的所有 Quality 记录,但未能提出一个也说明的查询关联组的记录。
public IEnumerable<Quality> Get()
{
int g_Id = myAppPrincipal.G_Id;
var group = UnitOfWork.GetById<Group>(g_Id);
var groupQualities = group.Qualities;
// Qualities is a public virtual ICollection of Quality inside the class Group
IList<Quality> result = groupQualities.Select(entity => new Quality()
{
Q_Id = entity.Q_Id,
Description = entity.Description,
...
}).ToList();
return result;
}
如果有人可以帮助我构建正确的查询,我将不胜感激。
见下文class结构:
public class Entity
{
/// <summary>
/// Unique identifier
/// </summary>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int Id
{
get;
set;
}
/// <summary>
/// Concurrency check property
/// </summary>
[Timestamp]
public virtual byte[] Version
{
get;
set;
}
/// <summary>
/// Determines whether entity is new
/// </summary>
[Bindable(false)]
[ScaffoldColumn(false)]
public virtual bool IsNew
{
get
{
return Id == 0;
}
}
public override string ToString()
{
return this.DumpToString();
}
}
public class Association: Entity
{
public string Description { get; set; }
}
public class Assoc_Group: Entity
{
public int A_ID { get; set; }
public int G_ID { get; set; }
[IgnoreDataMember]
public override byte[] Version
{
get;
set;
}
}
public class Group: Entity
{
public Group()
{
Qualities = new List<Quality>();
}
public string Description { get; set; }
public virtual ICollection<Quality> Qualities { get; set; }
}
public class Quality: Entity
{
public int? G_ID { get; set; }
public string Description { get; set; }
public virtual Group Group { get; set; }
[IgnoreDataMember]
public override byte[] Version
{
get;
set;
}
}
和相应的映射:
public class AssociationMap : EntityTypeConfiguration<Association>
{
public AssociationMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
Property(t => t.Description)
.IsRequired();
// Table & Column Mappings
ToTable("Association");
Property(t => t.Id).HasColumnName("A_ID");
Property(t => t.Description).HasColumnName("Description");
Ignore(t => t.Version);
}
}
class Assoc_GroupMap : EntityTypeConfiguration<Assoc_Group>
{
public Assoc_GroupMap()
{
ToTable("Assoc_Group");
Property(t => t.Id).HasColumnName("AG_ID");
Property(t => t.A_ID).HasColumnName("A_ID");
Property(t => t.G_ID).HasColumnName("G_ID");
Ignore(property => property.Version);
}
}
public class GroupMap : EntityTypeConfiguration<Group>
{
public GroupMap()
{
// Primary Key
HasKey(t => t.Id);
// Table & Column Mappings
ToTable("Group");
Property(t => t.Id).HasColumnName("G_ID");
Ignore(t => t.Version);
}
}
public class QualityMap : EntityTypeConfiguration<Quality>
{
public QualityMap()
{
// Primary Key
HasKey(t => t.Id);
// Table & Column Mappings
ToTable("Quality");
Property(t => t.Id).HasColumnName("Q_ID");
Property(t => t.G_ID).HasColumnName("G_ID");
...
// Relationships
HasOptional(t => t.Group)
.WithMany(t => t.Qualities)
.HasForeignKey(d => d.G_ID);
}
}
据我了解,您需要属于某个特定组的所有父协会的所有组品质,因此:
public IEnumerable<Quality> Get()
{
int g_Id = myAppPrincipal.G_Id;
var group = UnitOfWork.GetById<Group>(g_Id);
var associatedGroups = group.Assoc_Group.Groups;
var qualities = new List<Quality>();
foreach (var assoc in associatedGroups.Select(t => t.Association).Distinct())
{
qualities.AddRange(assoc.Assoc_Groups.SelectMany(t => t.group.Qualities).ToList());
}
var result = qualities.Distinct();
return result;
}
在上述情况下,我希望 GetById 也能获取关联实体,如果没有,则必须更改为 GetById包括关联实体的方法。否则,您可以通过单独调用来单独获取关联。
您可以像这样使用嵌套查询:
DECLARE @GivenGroup AS Int = 1
SELECT DISTINCT Q_Id
FROM Quality q
WHERE q.G_Id IN
(
SELECT DISTINCT G_Id
FROM Assoc_Group ag
WHERE ag.A_Id IN
(
SELECT a.A_Id
FROM Association a
INNER JOIN Assoc_Group ag ON a.A_Id = ag.A_Id
WHERE ag.G_Id = @GivenGroup
)
)
至少 SQL 服务器。
您可以在这里尝试:http://sqlfiddle.com/#!18/6dcc6/2
要使用 C# 查询您的 SQL 数据库,您可以使用 SqlClient
var SQL = new StringBuilder();
// Build SQL here. use SQL.AppendLine("SELECT ...")
var cn = new
System.Data.SqlClient.SqlConnection(your_sql_connection_string);
var da = new System.Data.SqlClient.SqlDataAdapter(SQL.ToString(), cn);
var datatbl = new DataTable();
da.SelectCommand.Parameters.Add("@GivenGroup", SqlDbType.Int).Value = 1; // use parameterization always.
try {
da.SelectCommand.Connection.Open();
da.Fill(datatbl);
}
catch (Exception ex)
{
// error handling here
}
finally
{
cn.Close();
cn.Dispose();
da.Dispose();
}
// read from datatable
foreach (DataRow row in datatbl.Rows)
{
Console.WriteLine(row["G_Id"].ToString());
}
可能有错误。