如何使用EF Code First表示计算列的选项?

3

我有这样一种情况:我有一个条目表,将在其中进行查询,但在某些情况下,我将有更多的信息可用。

例如,如果我有一个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; }

但是,由于没有“Distance”列,名称查询失败。我还尝试扩展Person类:
public class PersonWithDistance: Person {
    public float Distance { get; set; }
}

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

实施这样的功能应该采用什么正确的方式?是否需要为距离查询结果创建完全独立的类?

2个回答

4

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

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

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

或者通过流畅的API:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Person>().Ignore(p => p.Distance);
}

2
从应用程序中进行操作的问题在于我想按距离排序。如果我想显示到给定位置最接近的前10个人,那么在代码中执行需要从数据库加载每条记录。我仍然不清楚如何实际填充对象中的Distance值。如果我只是使用SqlQuery("select *, dbo.CalculateDistance(...",我会收到一条消息,指出“不存在从对象类型<>f__AnonymousType0`的映射”。 - Joe Krill

1

最终我创建了一个 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();
}

看起来这样做很好。虽然我不确定这是否是处理它的最佳方式。


4
我使用了一个计算列(也因为我想排序)。你可以添加 [DatabaseGenerated(DatabaseGeneratedOption.Computed)],EF 将不会干扰你关于该列的事情。在 Code First 中,它甚至会为您创建该列,然后使用 EF Migrations 您可以进行自定义迁移,删除该列,添加函数,并重新添加该列 ADD COLUMN Foo AS ([dbo].[CalculateDistance]([Column1], [Column2])) - kamranicus
@subkamran,你应该把这个变成一个答案。 - mcalex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接