Как запросить сложные отношения n: m для получения IEnumerable в C # - PullRequest
1 голос
/ 18 марта 2019

В моей базе данных sql есть отношение многие ко многим (n: m), как показано ниже: table relationship

Для моего веб-приложения asp.net (с EF и подходом к базе данных) мне нужно извлечь все записи (с C # и linq) таблицы Quality для всех групп, которые являются членами одной ассоциации s что данная группа является членом.

Ниже я привел пример данных, чтобы продемонстрировать необходимый результат запроса для данной группы с G_Id = 1

sample data

Из данных примера мы видим, что данная группа является членом ассоциации A_Id = 1 и A_Id = 2. Поэтому нам нужно посмотреть, какие другие группы являются членами этих ассоциаций. В приведенных данных мы видим, что группа с G_Id = 2 также является членом ассоциации A_Id = 1, а группа с G_Id = 3 также является членом A_Id = 2. Таким образом, мы должны собрать все записи из таблицы Quality, где G_Id = 1, G_Id = 2 и G_Id = 3, а результат запроса:

Query result

Я смог написать работающее действие контроллера C # IEnumerable (см. Ниже) для получения всех Качественных записей для одной группы, но не смог создать запрос, который также учитывает записи связанных групп .

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

Если бы кто-нибудь мог помочь мне с построением правильного запроса, я был бы благодарен.

См. Ниже структуру класса:

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

Ответы [ 2 ]

1 голос
/ 18 марта 2019

Вы можете использовать вложенные запросы следующим образом:

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 Server.

Вы можете попробовать это здесь: http://sqlfiddle.com/#!18/6dcc6/2

Чтобы запросить вашБаза данных SQL с C # вы можете использовать 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());
}

Может содержать ошибки.

1 голос
/ 18 марта 2019

Насколько я понимаю, вам нужны все групповые качества, которые входят во все родительские ассоциации какой-то конкретной группы, поэтому:

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 , чтобы включить связанные сущности. В противном случае вы можете получить связанное отдельно, совершая отдельные звонки.

...