使用LINQ to Entities与集合属性的最佳方法

8
假设我有以下模型:

城市和火车站

如你所见,每个城市都有一个或多个火车站。
我还有以下类型:
public class CityDTO
{
    public string CityName { get; set; }
    public string StateName { get; set; }
    public List<string> Trainstations { get; set; }
}

现在,LINQ 的酷之处在于我可以查询实体框架并返回我的类型的新集合:
    public List<CityDTO> GetCities()
    {
        using (var db = new CityDataContext())
        {

            var cities = db.Cities;
            var trainstations = db.TrainStations;

            var query =
            (from city in cities
             select new CityDTO
             {
                 CityName = city.Name,
                 StateName = city.State

             }).ToList();

            return query;
        }
    }

问题是:我能否在同一个LINQ查询中返回一组火车站名称,以添加到我的CityDTO类中?
   public List<CityDTO> GetCities()
    {
        using (var db = new CityDataContext())
        {

            var cities = db.Cities;
            var trainstations = db.TrainStations;

            var query =
            (from city in cities
             join trainstation in trainstations
             on city.CityId
             equals trainstation.CityId into orderGroup
             select new CityDTO
             {
                 CityName = city.Name,
                 StateName = city.State
                 //assign List<string> of trainstation names to CityDTO.Trainstations

             }).ToList();

            return query;
        }
    }

上述Join的另一个问题是它会多次返回相同的城市名称(对应其每个火车站),而我只需要每个城市一个CityDTO实例。

这个最好用两个LINQ语句来完成吗?一个用于获取城市列表,第二个用于获取每个城市的火车站列表?

3个回答

5

@haim770,你的答案帮助我找到了正确的方向。仅供参考,我还需要更改两个东西。

当我只添加选择投影仪时,会出现以下运行时错误。

enter image description here

所以,Select方法期望投影的类型为IEnumerable,因此我可以这样做,一切都很好:
public class CityDTO
{
    public string CityName { get; set; }
    public string StateName { get; set; }
    public IEnumerable<string> Trainstations { get; set; } //changed from List<> to IEnumerable<> 
}

现在这个很好地工作:
    public List<CityDTO> GetCities()
    {
        using (var db = new CitysDataEntities())
        {
            var cities = db.Cities;

            var query =
            (from city in cities
             select new CityDTO
                 {
                    CityName = city.Name,
                    Trainstations = city.TrainStations.Select(ts => ts.Name)  //now this works

                 }).ToList();

            return query;
        }
    }

然而,经过进一步阅读,我决定使用IQueryable,它也有一个Select方法。
原因是IQueryable.Select位于System.Linq下,而IEnumerable.Select位于System.Collections命名空间下。这很重要,因为IQueryable.Select被优化为在服务器上执行所有过滤并仅将结果返回给客户端,而IEnumerable.Select在过滤之前首先将对象加载到客户端。
因此,结果就是这样的。 IEnumerable Vs IQueryable
public class CityDTO
{
    public string CityName { get; set; }
    public string StateName { get; set; }
    public IQueryable<string> Trainstations { get; set; } // changed to IQueryable
}

并且

    public List<CityDTO> GetCities()
    {
        using (var db = new CitysDataEntities())
        {
            var cities = db.Cities;

            var query =
            (from city in cities
             select new CityDTO
                 {
                    CityName = city.Name,
                     Trainstations = city.TrainStations.Select(ts => ts.Name).AsQueryable() // return AsQueryable

                 }).ToList();

            return query;
        }
    }

现在,当我添加筛选器时,它们将在服务器端应用。

MSDN - Queryable.Select

MSDN - Enumerable.Select


3
这里的“Join”操作是隐式的,因为从“City”到“TrainStations”有导航属性,所以EF将自动为您检索站点。
(from city in cities
 select new CityDTO
     {
     CityName = city.Name,
     StateName = city.State,
     Trainstations = city.TrainStations.Select(ts => ts.Name).ToList()
     })

上面的代码使用Select()函数将火车站列表投影为基于Name属性的string列表。
参见MSDN

0
使用AutoMapper将集合属性投影到IList。
Queryable.ProjectTo<UserListDto>().ToArray();

实体

public class User
{
    public string Name { get; set; }

    public virtual ICollection<UserRole> Roles { get; set; }  
}

Dto

[AutoMapFrom(typeof(User))]
public class UserListDto
{
    public string Name { get; set; }

    public IList<UserRoleOutput> Roles { get; set; }

    [AutoMapFrom(typeof(UserRole))]
    public class UserRoleOutput
    {
        public int RoleId { get; set; }
    }
}

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