按实体计算和分组(不是实体的属性)

时间:2012-10-24 19:56:54

标签: c# entity-framework entity-framework-5

我有以下实体模型。为简洁起见,我在每个实体上省略了很多属性。

public sealed class Platform {
    /// <summary>
    /// Get and Set Platform's Unique Identifier.
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Determine if an Object is Equal to This Platform.
    /// </summary>
    /// <param name="obj">
    /// An object to compare.
    /// </param>
    /// <returns>
    /// A boolean true if the object is equal to this platform. A boolean false otherwise.
    /// </returns>
    public override bool Equals(object obj) {
        bool isObjectPlatform = obj is Platform;
        bool isObjectIdEqual = isObjectPlatform && (obj as Platform).Id == this.Id;

        return isObjectIdEqual;
    }

    /// <summary>
    /// Get Platform's Hash Code.
    /// </summary>
    /// <returns>
    /// The platform's hash code, equalling the platform's unique identifier.
    /// </returns>
    public override int GetHashCode() {
        return this.Id;
    }
}

public sealed class Capture {
    /// <summary>
    /// Get and Set Capture's Unique Identifier.
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Get and Set Capture's Platform.
    /// </summary>
    public Platform Platform { get; set; }

    /// <summary>
    /// Determine if an Object is Equal to This Capture.
    /// </summary>
    /// <param name="obj">
    /// An object to compare.
    /// </param>
    /// <returns>
    /// A boolean true if the object is equal to this capture. A boolean false otherwise.
    /// </returns>
    public override bool Equals(object obj) {
        bool isObjectCapture = obj is Capture;
        bool isObjectIdEqual = isObjectCapture && (obj as Capture).Id == this.Id;

        return isObjectIdEqual;
    }

    /// <summary>
    /// Get Capture's Hash Code.
    /// </summary>
    /// <returns>
    /// The capture's hash code, equalling the capture's unique identifier.
    /// </returns>
    public override int GetHashCode() {
        return this.Id;
    }
}

我基本上想要的是一个LINQ查询,当然由EF支持,这将使我获得由整个平台实体分组的捕获的数量。我希望能够返回对每个平台的引用,而不是其中一个属性,以及与之关联的每个 Capture 的计数。

我做了这个查询,效果很好:

var query = this._defaultContext.Captures
                .Include(m => m.Platform)
                .GroupBy(m => m.Platform.Id)
                .Select(m => new {
                    PlatformId = m.Key,
                    Count = m.Count()
                });

但正如您所看到的,我正在按平台的 Id 属性进行分组。我宁愿拥有的是:

var query = this._defaultContext.Captures
                .Include(m => m.Platform)
                .GroupBy(m => m.Platform)
                .Select(m => new {
                    Platform = m.Key,
                    Count = m.Count()
                });

这不起作用。它只是为数据库中的每条记录计数1。它看起来并不知道整个实体分组。我希望通过 GetHashCode Equals 方法实现区分每个平台,但没有运气。

任何人都遇到过这样的情景?有什么办法吗?或者我必须手动执行此操作。我很讨厌,因为它可能会导致某种N + 1查询。

提前致谢。

3 个答案:

答案 0 :(得分:1)

var query = this._defaultContext.Captures
                    .GroupBy(m => m.Platform.Id)
                    .Select(m => new {
                        Platform = m.FirstOrDefault().Platform,
                        Count = m.Count()
                    });

答案 1 :(得分:1)

我不确定你为什么采取这种方法。考虑在Platform和Capture之间实现双向导航(参见Configuring Relationships with the Fluent API),然后你可以将Captures和Counting变得简单。

public sealed class Platform
{
    public ICollection<Capture> Captures { get; set; }
    // the rest of the stuff
}

...

var query = this._defaultContext.Platforms.Include("Captures").Select(p => new { Platform = p, CaptureCount = p.Captures.Count() });

在转换为SQL时,这应该表达为:

SELECT Platform.Id, Platform.Name, COUNT(Captures.*)
FROM Platform LEFT OUTER JOIN Capture ON Capture.Platform_Id = Platform.Id
GROUP BY Platform.Id, Platform.Name

答案 2 :(得分:0)

如果您未指定,则{p> GroupBy默认使用EqualityComparer<T>.Default。反过来,EqualityComparer<T>.Default会查看T是否实现IEquatable<T>并使用它,如果是的话。

所以答案是实现IEquatable<T>

public sealed class Platform : IEquatable<Platform> {

    public override bool Equals(Platform obj) {
        return this.Equals((object)obj);
    }
    // rest of your code here 
}

无论其

我怀疑这可以翻译成SQL。您可能必须将导航属性从Platform定义为Captures列表,这将在一个查询中为您提供对象。

要指定IEqualityComparer,只需在类中实现该接口:

public class PlatformComparer : IEqualityComparer<Platform>
{
    public bool Equals(Platform p1, Platform p2)
    {
        if(p1 == null !! p2 == null) return false;
        return p1.Id == p2.Id;
    }
    public int GetHashCode(Platform p)
    {   
        if(p==null) throw new ArgumentNullExceltion("p");
        return p.Id.GetHashCode();
    }
}

并在GroupBy

中使用它
.GroupBy(m => m.Platform, new PlatformComparer())