How to make this code DRY?
I'm writing RESTful service with C#/wcf 开发者_C百科and need to put filters on GET. Like how many records to return, maybe if I want to filter by something, etc. Consider this code:
[WebGet(UriTemplate = "/devices/{DeviceId}/positions")]
public List<GPSPosition> GetDevicePositions(string deviceId)
{
//Lookup device:
using (var context = new MobileModelContext(ContextManager.AccountGKey.Value))
{
var d = context.Devices.Where(aa => aa.DeviceId == deviceId).FirstOrDefault();
if (d == null)
{
outgoingResponse.StatusCode = HttpStatusCode.NotFound;
outgoingResponse.StatusDescription = "Device not found";
return null;
}
var query = from p in context.Positions
where p.DeviceKey.Equals(d.DeviceKey)
select new GPSPosition
{
PositionGKey = p.PositionGKey,
Latitude = p.Latitude,
Longitude = p.Longitude,
Speed = p.Speed,
Accuracy = p.Accuracy,
Altitude = p.Altitude,
GPSTime = p.GPSTime,
DeviceTime = p.DeviceTime
};
return query.ToList();
}
}
[WebGet(UriTemplate = "/devices/{DeviceId}/positions?RecordCount={RecordCount}")]
public List<GPSPosition> GetDevicePositions2(string deviceId, int recordCount)
{
//Lookup device:
using (var context = new MobileModelContext(ContextManager.AccountGKey.Value))
{
var d = context.Devices.Where(aa => aa.DeviceId == deviceId).FirstOrDefault();
if (d == null)
{
outgoingResponse.StatusCode = HttpStatusCode.NotFound;
outgoingResponse.StatusDescription = "Device not found";
return null;
}
var query = from p in context.Positions
where p.DeviceKey.Equals(d.DeviceKey)
select new GPSPosition
{
PositionGKey = p.PositionGKey,
Latitude = p.Latitude,
Longitude = p.Longitude,
Speed = p.Speed,
Accuracy = p.Accuracy,
Altitude = p.Altitude,
GPSTime = p.GPSTime,
DeviceTime = p.DeviceTime
};
return query.Take(recordCount).ToList();
}
}
Lot of repetition. I can move code into other function but still, I have 2 templates, I have 2 functions. Is there any way to make 1 template for /positions/ that will cover all possible "?" scenarios?
Take enumerates source and yields elements until count elements have been yielded or source contains no more elements.
Since Take(n)
returns at a maximum n items, but less if there are less available you can rewrite:
public List<GPSPosition> GetDevicePositions(string deviceId)
{
return GetDevicePositions2(deviceId, int.MaxValue)
}
which will then return all items.
QA Hint: context.Devices.Where(aa => aa.DeviceId == deviceId).FirstOrDefault();
can be shorten to context.Devices.Find(deviceId);
QA Hint: from p in context.Positions ...
you may want to create a view on your table and instead of ... select new GPSPosition { ... }
you would simply write regular context.PositionViews.Where(x => x.DeviceKey == d.DeviceKey).ToList();
QA Hint: You may want to use .AsNoTracking()
to optimize performance.
QA Hint: You may want to use optional parameters in your method declarations. For example:
public List<GPSPosition> GetDevicePositions(string deviceId, int limit = 20)
{
// you code here
}
Or if WCF doesn't support it just yet. Here is a work-around:
You can get the desired effect by omitting the Query string from the UriTemplate on your WebGet or WebInvoke attribute, and using WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters from within your handlers to inspect, set defaults, etc. on the query parameters.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=451296&wa=wsignin1.0
精彩评论