【问题标题】:How do I represent an option calculated column with EF Code First?如何使用 EF Code First 表示选项计算列?
【发布时间】:2011-07-15 05:01:42
【问题描述】:

我有一个要查询的条目表,但在某些情况下,我会有其他可用信息。

例如,如果我有一个people 的表,我希望能够按名称搜索,但我也希望存储一些坐标并根据它们的位置进行搜索,而且还要在对象模型中公开该距离.因此,例如,假设我的 people 表如下所示:

PersonId int
Name nvarchar(100)
Address nvarchar(100)
Latitude float(10,6)
Longitude float(10,6)

而实体类是这样定义的:

public class Person
{
     public int PersonId { get; set; }
     public sting Name { get; set; }
     public float Latitude { get; set; }
     public float Longitude { get; set; }
}

然后我可以使用以下方法轻松找到一个人:

var people = from p in myDb.People where p.Name.Contains("joe");

现在,我创建了名为CalculateDistance 的用户定义函数来处理此距离计算。所以我的 SQL 看起来像这样:

String sql = "SELECT *, dbo.CalculateDistance(" + location.X + ", " + location.Y + ", Latitude, Longitude) AS Distance FROM people ORDER BY Distance

如何在代码中表示这一点?我尝试在类中添加这样的属性:

public virtual float Distance { get; set; }

但随后名称查询失败,因为没有距离列。我还尝试扩展 Person 类:

public class PersonWithDistance: Person {
    public float Distance { get; set; }
}

但这导致映射的生成方式出现更多问题。

实现这样的事情的正确方法是什么?我是否必须为距离查询的结果创建一个完全独立的类?

【问题讨论】:

    标签: linq entity-framework entity-framework-4 code-first ef-code-first


    【解决方案1】:

    这不是很好的设计。您应该将距离作为 Person 或某个辅助类的方法,并在您的应用程序中而不是在数据库中计算距离。它还需要将位置转移给该人。

    无论如何,如果您想添加一些未映射到数据库中列的属性,您必须将其排除在映射之外。这可以通过属性来完成:

    [NotMapped]
    public float Distance { get; set; }
    

    或通过流畅的 API:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    
        modelBuilder.Entity<Person>().Ignore(p => p.Distance);
    }
    

    【讨论】:

    • 从应用程序执行此操作的问题是我想按距离排序。如果我想显示最接近给定位置的前 10 名人员,则在代码中执行此操作需要从数据库中加载每条记录。我仍然对如何在我的对象中实际填充距离值感到困惑。如果我只使用 SqlQuery("select *, dbo.CalculateDistance(..." 我收到一条消息“对象类型 f__AnonymousType0` 不存在映射”
    【解决方案2】:

    我最终创建了一个 PersonResult 类,其中包含一个 IdDistance 和一个 Person 对象,如下所示:

    public class PersonResult {
         [Key]
         public int PersonId { get; set; }
         public double Distance { get; set; }
         public virtual Person Person { get; set; }
    }
    

    我从这样的存储过程中填充它:

    var results = myDb.PersonResults.SqlQuery("EXEC PeopleByDistance " + lat + ", " + lng);
    

    然后循环遍历结果以填充 Person 对象:

    foreach (PersonResultresult in results)
    {
        result.Person = myDb.People.Where(p => p.PersonId == result.PersonId).FirstOrDefault();
    }
    

    这似乎运作良好。虽然我不确定这是最好的处理方式。

    【讨论】:

    • 我使用计算列(也是因为我想排序)。您可以添加[DatabaseGenerated(DatabaseGeneratedOption.Computed)],EF 不会为该专栏打扰您。在 Code First 中,它甚至会为您创建列,然后使用 EF 迁移,您可以进行自定义迁移,删除列、添加函数并重新添加列ADD COLUMN Foo AS ([dbo].[CalculateDistance]([Column1], [Column2]))
    • @subkamran 你应该把这个作为答案
    猜你喜欢
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    相关资源
    最近更新 更多