我正在建造的东西并不是很独特。简而言之,我正在用ASP在Azure上创建一个类似FourSquare的小服务。NET MVC 4(Web Api)和实体框架5(具有空间支持)。所以我使用的是SQL Azure,而不是像MongoDB或CouchDB这样的NoSQL数据库。部分原因是我对。net更加熟练/熟悉,部分原因是为了了解开发经验(重构、部署、测试),部分原因是为了了解它将如何与eg。node . js/MongoDB。
现在让我们看一些代码。
/// <summary>
/// Return the nearest locations relative from the given longitude/latitude
/// </summary>
/// <param name="longitude">Longitude</param>
/// <param name="latitude">Latitude</param>
/// <param name="maxresults">Optional maximum results, default is 2</param>
/// <param name="radius">Optional maximum radius in kilometres, default is 50 km</param>
/// <returns></returns>
public JsonEnvelope Get(string longitude, string latitude, int maxresults = 2, int radius = 50)
{
var pointTxt = string.Format("POINT({0} {1})", longitude, latitude);
var locations = (from s in locationEntityRepository.GetAll
orderby s.Coordinates.Distance(DbGeography.FromText(pointTxt))
where s.Coordinates.Distance(DbGeography.FromText(pointTxt)) / 1000 <= radius
select new Location
{
Id = s.Id,
Name = s.Name,
LocationType = s.LocationType,
Address = s.Address,
Longitude = s.Coordinates.Longitude.Value,
Latitude = s.Coordinates.Latitude.Value,
Distance = (s.Coordinates.Distance(DbGeography.FromText(pointTxt)).Value) / 1000
})
.Take(maxresults).ToList();
// Bad bad bad. But EF/Linq doesn't let us do Includes when using subqueries... Go figure
foreach (var location in locations)
{
location.Checkins = AutoMapper.
Mapper.
Map<List <Checkin>, List<LocationCheckinsJsonViewModel>>
(checkinRepository.GetCheckinsForLocation(location.Id).ToList());
}
// AutoMapper.Mapper.Map<Checkin, CheckinViewModel>(dbCheckin);
var jsonBuilder = new JsonResponseBuilder();
jsonBuilder.AddObject2Response("locations", locations);
return jsonBuilder.JsonEnvelope;
}
有几件事我想我需要澄清。locationEntityRepository.GetAll
是这样的
public IQueryable<LocationEntity> GetAll
{
get { return _context.Locations; }
}
public IQueryable<LocationEntity> GetAllIncluding(params Expression<Func<LocationEntity, object>>[] includeProperties)
{
IQueryable<LocationEntity> query = _context.Locations;
foreach (var includeProperty in includeProperties) {
query = query.Include(includeProperty);
}
// var tmp = query.ToList();
return query;
}
现在代码真的很臭。理想情况下,我希望能够使用GetAllIncluding(c => c.Checkins)
而不是GetAll
方法,并能够使用AutoMapper
在LINQ投影内进行映射。
我知道这是通过设计,包括+ LINQ/EF返回null设计时使用子查询。在LINQ/EF查询中使用automapper应该使用Project().To<>
,但在使用.ForMember
时不起作用。
因此,挑战是使代码更有效(更少的SQL和易于维护时,我的JSON结构的变化是必要的。记住,我们试图击败node.js/MongoDB在这里;)我是该麻烦一下,还是就这样算了?
我使用Repository模式做了类似的事情。
public IEnumerable<T> FindAll()
{
return _context.Set<T>().ToList();
}
public IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate)
{
return _context.Set<T>().Where(predicate);
}
,其中。set方法是
public IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
这允许您从数据库中获取某个类型的所有内容,或者仅获取满足特定条件的所有类型。
这留给你一个对象列表,或者如果你使用FirstOrDefault()一个单一的对象,你可以映射任何你想要的
三件事。
- 包含
System.Data.Entity
命名空间。include
方法实际上是该名称空间引入的扩展。更可取的是使用在EF 4.1及以上版本中可用的Lambda重载,因此,你必须在任何想调用它的地方添加命名空间。 -
删除第一个查询末尾的. tolist(),因为它立即执行查询,这违背了include的目的。
using System.Data.Entity public IQueryable<LocationEntity> GetAll { get { return _context.Locations; } }
为什么不用这个呢:
Context context=new Context();
Public List<LocationEntity> GetAll()
{
return context.LocationEntities.Include("includeProperty").ToList();
}
和上下文是这样的:
public class Context: DbContext
{
public Context()
{
base.Configuration.LazyLoadingEnabled = false;
base.Configuration.ProxyCreationEnabled = false;
base.Configuration.ValidateOnSaveEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
public DbSet<LocationEntity> LocationEntities{ get; set; }
}