Основное решение EventLog GS12/GS.EventLog/GS.EventLog
суббота, 12 октября 2013 г.
Microsoft.Bcl.Build - Warning Message in VS
Предупреждение 2 All projects referencing GS.Trade.TradeContext.csproj must install nuget package Microsoft.Bcl.Build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317569. BackTest01
Improved Package Restore
RATE THIS
22 Aug 2013 1:29 PM
As we’ve previously explained we receive various reports on how our NuGet packages don’t play nicely with NuGet’s package restore feature. In this blog post I’ll talk about an update we shipped to our infrastructure packageMicrosoft.Bcl.Build that reduces the impact.
The Issue
To recap, some of our packages need to participate in the build in order to work properly. Examples of tasks that require build participation include generating binding redirects or selecting the appropriate native dependency based on the CPU architecture the build is producing.
Build participation is implemented by adding an import to an MSBuild .targets file. Generally, build participation isn’t optional which is why we decided not to use NuGet’s feature to add a target file import. NuGet will add the import with a condition that checks whether the .targets file already exists, i.e. whether the NuGet package was already restored.
Both solutions aren’t ideal:
- A non-optional import ensures that building will fail when packages are missing because the imported target file can’t be found. However, this also means that you can’t open the solution in Visual Studio (see picture below). Also, the error message isn’t really actionable as it doesn’t include any instructions on how to fix the issue.
- An optional import allows the project to load and build. However, even if you have package restore enabled, the first build may not work because by the time the project was build, the targets file was still missing. This can result in build breaks or – even worse – the build may succeed but with incorrect outputs.
The imported project "<solution-path>\packages\Microsoft.Bcl.Build.1.0.8\tools\Microsoft.Bcl.Build.targets" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk.
The Improvement
We’ve updated Microsoft.Bcl.Build to use a different approach. The new version will use a conditional import similar to what NuGet’s automatic import feature does. This will always allow the project to load in Visual Studio.
However, Microsoft.Bcl.Build also adds a target to your project that will run after the build is finished. This target checks whether the current build restored packages and if so fail the build with an actionable error message:
The build restored NuGet packages. Build the project again to include these packages in the build. For more information, seehttp://go.microsoft.com/fwlink/?LinkID=317568.
Building a second time will fix this error. Please note that this error will only appear if packages were missing so it’s not like you always have to build twice.
This solution doesn’t address build server / continuous integration (CI) scenarios. In order to successfully use package restore on the build server, you have two options:
- Check-in the .targets file.
- Explicitly run NuGet package restore prior to building your project/solution.
NuGet 2.7
Prior to version 2.7, NuGet makes package restore a bit more complicated than most people would like it to be:
- First of all you need to manually enable it for the solution. This will add a NuGet.exe, NuGet.config and NuGet.targets file to your solution and you are expected to check those files in.
- Explicitly running package restore for the solution isn’t a single step. You need to run a command for each project, such as
nuget.exe install .\Project1\packages.config
.
NuGet 2.7 makes this a lot easier:
- You no longer need to enable package restore explicitly – when building in VS all packages are restored automatically.
- Running package restore on a build machine is now super easy. You only need to check-in NuGet.exe (nothing else) and you can put it wherever you want. It can even be in a well-known location on your build server and excluded from every solution if desired. Prior to the build you simply run
nuget.exe restore path\to\my\solution.sln
.
Also, the NuGet team is talking to all major providers of build/CI servers (incl. TFS) so that at some point the second step can be handled automatically by the build servers. For more details, have a look at the NuGet 2.7 release notes and thenew Package Restore documentation.
Summary
The new version of Microsoft.Bcl.Build will ensure that solutions containing our packages will load successfully even if packages aren’t restored yet. This affects all .NET NuGet packages that depend on it, such as Microsoft.Net.Http,Microsoft.Bcl, and Microsoft.Bcl.Async.
Microsoft.Bcl.Build will give an actionable error message in cases the package was missing by asking you to build again.
When coupled with NuGet 2.7 where package restore is automatic in Visual Studio and isn’t implemented through MSBuild, the experience is transparent and smooth. However, this doesn’t address build server scenarios yet so you still need run
nuget.exe restore solution.sln
prior to build, or check-in the .targets file if preferred.GS.EventLog
GS.EventLog: D:\vspr12\GS12\GS.EventLog\GS.EventLog\
GS.EventLogClients: D:\vspr12\GS12\GS.EventLogClients
GS.EventLogClients: D:\vspr12\GS12\GS.EventLogClients
пятница, 11 октября 2013 г.
Margin Update 01
Сделал модель WebClient для Web Api
WebClient2 -> GenWebClient<T> -> TickerWebClient
Также доделал ЭвентЛог. Ввел метод Init(string uri) - где происходит инициализация всех логов.
Все правильные Логи в файле: EventLogs.xml в проекте MarginUpdate.
В дальнейшем необходимо переделать WebEventLog в соответствие с новой моделью WebClient2.
WebClient2 -> GenWebClient<T> -> TickerWebClient
Также доделал ЭвентЛог. Ввел метод Init(string uri) - где происходит инициализация всех логов.
Все правильные Логи в файле: EventLogs.xml в проекте MarginUpdate.
В дальнейшем необходимо переделать WebEventLog в соответствие с новой моделью WebClient2.
четверг, 10 октября 2013 г.
Parameter Binding in Web API
http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
Parameter Binding in ASP.NET Web API
When Web API calls a method on a controller, it must set values for the parameters, a process called binding. This article describes how Web API binds parameters, and how you can customize the binding process.
By default, Web API uses the following rules to bind parameters:
- If the parameter is a “simple” type, Web API tries to get the value from the URI. Simple types include the .NETprimitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plusany type with a type converter that can convert from a string. (More about type converters later.)
- For complex types, Web API tries to read the value from the message body, using a media-type formatter.
For example, here is a typical Web API controller method:
HttpResponseMessage Put(int id, Product item) { ... }
The id parameter is a "simple" type, so Web API tries to get the value from the request URI. The item parameter is a complex type, so Web API uses a media-type formatter to read the value from the request body.
To get a value from the URI, Web API looks in the route data and the URI query string. The route data is populated when the routing system parses the URI and matches it to a route. For more information, see Routing and Action Selection.
In the rest of this article, I'll show how you can customize the model binding process. For complex types, however, consider using media-type formatters whenever possible. A key principle of HTTP is that resources are sent in the message body, using content negotiation to specify the representation of the resource. Media-type formatters were designed for exactly this purpose.
Using [FromUri]
To force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter. The following example defines a
GeoPoint
type, along with a controller method that gets the GeoPoint
from the URI.public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public ValuesController : ApiController
{
public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
The client can put the Latitude and Longitude values in the query string and Web API will use them to construct a
GeoPoint
. For example:http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989
Using [FromBody]
To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter:
public HttpResponseMessage Post([FromBody] string name) { ... }
In this example, Web API will use a media-type formatter to read the value of name from the request body. Here is an example client request.
POST http://localhost:5076/api/values HTTP/1.1
User-Agent: Fiddler
Host: localhost:5076
Content-Type: application/json
Content-Length: 7
"Alice"
When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. In this example, the content type is "application/json" and the request body is a raw JSON string (not a JSON object).
At most one parameter is allowed to read from the message body. So this will not work:
// Caution: Will not work!
public HttpResponseMessage Post([FromBody] int id, [FromBody] string name) { ... }
The reason for this rule is that the request body might be stored in a non-buffered stream that can only be read once.
Type Converters
You can make Web API treat a class as a simple type (so that Web API will try to bind it from the URI) by creating aTypeConverter and providing a string conversion.
The following code shows a
GeoPoint
class that represents a geographical point, plus a TypeConverter that converts from strings to GeoPoint
instances. The GeoPoint
class is decorated with a [TypeConverter] attribute to specify the type converter. (This example was inspired by Mike Stall’s blog post How to bind to custom objects in action signatures in MVC/WebAPI.)[TypeConverter(typeof(GeoPointConverter))]
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
public static bool TryParse(string s, out GeoPoint result)
{
result = null;
var parts = s.Split(',');
if (parts.Length != 2)
{
return false;
}
double latitude, longitude;
if (double.TryParse(parts[0], out latitude) &&
double.TryParse(parts[1], out longitude))
{
result = new GeoPoint() { Longitude = longitude, Latitude = latitude };
return true;
}
return false;
}
}
class GeoPointConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
{
GeoPoint point;
if (GeoPoint.TryParse((string)value, out point))
{
return point;
}
}
return base.ConvertFrom(context, culture, value);
}
}
Now Web API will treat
GeoPoint
as a simple type, meaning it will try to bind GeoPoint
parameters from the URI. You don't need to include [FromUri] on the parameter.public HttpResponseMessage Get(GeoPoint location) { ... }
The client can invoke the method with a URI like this:
http://localhost/api/values/?location=47.678558,-122.130989
Model Binders
A more flexible option than a type converter is to create a custom model binder. With a model binder, you have access to things like the HTTP request, the action description, and the raw values from the route data.
To create a model binder, implement the IModelBinder interface. This interface defines a single method,BindModel:
bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext);
Here is a model binder for
GeoPoint
objects.public class GeoPointModelBinder : IModelBinder
{
// List of known locations.
private static ConcurrentDictionary<string, GeoPoint> _locations
= new ConcurrentDictionary<string, GeoPoint>(StringComparer.OrdinalIgnoreCase);
static GeoPointModelBinder()
{
_locations["redmond"] = new GeoPoint() { Latitude = 47.67856, Longitude = -122.131 };
_locations["paris"] = new GeoPoint() { Latitude = 48.856930, Longitude = 2.3412 };
_locations["tokyo"] = new GeoPoint() { Latitude = 35.683208, Longitude = 139.80894 };
}
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(GeoPoint))
{
return false;
}
ValueProviderResult val = bindingContext.ValueProvider.GetValue(
bindingContext.ModelName);
if (val == null)
{
return false;
}
string key = val.RawValue as string;
if (key == null)
{
bindingContext.ModelState.AddModelError(
bindingContext.ModelName, "Wrong value type");
return false;
}
GeoPoint result;
if (_locations.TryGetValue(key, out result) || GeoPoint.TryParse(key, out result))
{
bindingContext.Model = result;
return true;
}
bindingContext.ModelState.AddModelError(
bindingContext.ModelName, "Cannot convert value to Location");
return false;
}
}
A model binder gets raw input values from a value provider. This design separates two distinct functions:
- The value provider takes the HTTP request and populates a dictionary of key-value pairs.
- The model binder uses this dictionary to populate the model.
The default value provider in Web API gets values from the route data and the query string. For example, if the URI is
http://localhost/api/values/1?location=48,-122
, the value provider creates the following key-value pairs:- id = "1"
- location = "48,122"
(I'm assuming the default route template, which is "api/{controller}/{id}".)
The name of the parameter to bind is stored in the ModelBindingContext.ModelName property. The model binder looks for a key with this value in the dictionary. If the value exists and can be converted into a
GeoPoint
, the model binder assigns the bound value to the ModelBindingContext.Model property.
Notice that the model binder is not limited to a simple type conversion. In this example, the model binder first looks in a table of known locations, and if that fails, it uses type conversion.
Setting the Model Binder
There are several ways to set a model binder. First, you can add a [ModelBinder] attribute to the parameter.
public HttpResponseMessage Get([ModelBinder(typeof(GeoPointModelBinder))] GeoPoint location)
You can also add a [ModelBinder] attribute to the type. Web API will use the specified model binder for all parameters of that type.
[ModelBinder(typeof(GeoPointModelBinder))]
public class GeoPoint
{
// ....
}
Finally, you can add a model-binder provider to the HttpConfiguration. A model-binder provider is simply a factory class that creates a model binder. You can create a provider by deriving from the ModelBinderProviderclass. However, if your model binder handles a single type, it’s easier to use the built-inSimpleModelBinderProvider, which is designed for this purpose. The following code shows how to do this.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var provider = new SimpleModelBinderProvider(
typeof(GeoPoint), new GeoPointModelBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
// ...
}
}
With a model-binding provider, you still need to add the [ModelBinder] attribute to the parameter, to tell Web API that it should use a model binder and not a media-type formatter. But now you don’t need to specify the type of model binder in the attribute:
public HttpResponseMessage Get([ModelBinder] GeoPoint location) { ... }
Value Providers
I mentioned that a model binder gets values from a value provider. To write a custom value provider, implement the IValueProvider interface. Here is an example that pulls values from the cookies in the request:
public class CookieValueProvider : IValueProvider
{
private Dictionary<string, string> _values;
public CookieValueProvider(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException("actionContext");
}
_values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var cookie in actionContext.Request.Headers.GetCookies())
{
foreach (CookieState state in cookie.Cookies)
{
_values[state.Name] = state.Value;
}
}
}
public bool ContainsPrefix(string prefix)
{
return _values.Keys.Contains(prefix);
}
public ValueProviderResult GetValue(string key)
{
string value;
if (_values.TryGetValue(key, out value))
{
return new ValueProviderResult(value, value, CultureInfo.InvariantCulture);
}
return null;
}
}
You also need to create a value provider factory by deriving from the ValueProviderFactory class.
public class CookieValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
return new CookieValueProvider(actionContext);
}
}
Add the value provider factory to the HttpConfiguration as follows.
public static void Register(HttpConfiguration config)
{
config.Services.Add(typeof(ValueProviderFactory), new CookieValueProviderFactory());
// ...
}
Web API composes all of the value providers, so when a model binder calls ValueProvider.GetValue, the model binder receives the value from the first value provider that is able to produce it.
Alternatively, you can set the value provider factory at the parameter level by using the ValueProvider attribute, as follows:
public HttpResponseMessage Get(
[ValueProvider(typeof(CookieValueProviderFactory))] GeoPoint location)
This tells Web API to use model binding with the specified value provider factory, and not to use any of the other registered value providers.
HttpParameterBinding
Model binders are a specific instance of a more general mechanism. If you look at the [ModelBinder] attribute, you will see that it derives from the abstract ParameterBindingAttribute class. This class defines a single method,GetBinding, which returns an HttpParameterBinding object:
public abstract class ParameterBindingAttribute : Attribute
{
public abstract HttpParameterBinding GetBinding(HttpParameterDescriptor parameter);
}
An HttpParameterBinding is responsible for binding a parameter to a value. In the case of [ModelBinder], the attribute returns an HttpParameterBinding implementation that uses an IModelBinder to perform the actual binding. You can also implement your own HttpParameterBinding.
For example, suppose you want to get ETags from
if-match
and if-none-match
headers in the request. We'll start by defining a class to represent ETags.public class ETag
{
public string Tag { get; set; }
}
We’ll also define an enumeration to indicate whether to get the ETag from the
if-match
header or the if-none-match
header.public enum ETagMatch
{
IfMatch,
IfNoneMatch
}
Here is an HttpParameterBinding that gets the ETag from the desired header and binds it to a parameter of type ETag:
public class ETagParameterBinding : HttpParameterBinding
{
ETagMatch _match;
public ETagParameterBinding(HttpParameterDescriptor parameter, ETagMatch match)
: base(parameter)
{
_match = match;
}
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext, CancellationToken cancellationToken)
{
EntityTagHeaderValue etagHeader = null;
switch (_match)
{
case ETagMatch.IfNoneMatch:
etagHeader = actionContext.Request.Headers.IfNoneMatch.FirstOrDefault();
break;
case ETagMatch.IfMatch:
etagHeader = actionContext.Request.Headers.IfMatch.FirstOrDefault();
break;
}
ETag etag = null;
if (etagHeader != null)
{
etag = new ETag { Tag = etagHeader.Tag };
}
actionContext.ActionArguments[Descriptor.ParameterName] = etag;
var tsc = new TaskCompletionSource<object>();
tsc.SetResult(null);
return tsc.Task;
}
}
The ExecuteBindingAsync method does the binding. Within this method, add the bound parameter value to theActionArgument dictionary in the HttpActionContext.
If your ExecuteBindingAsync method reads the body of the request message, override theWillReadBody property to return true. The request body might be an unbuffered stream that can only be read once, so Web API enforces a rule that at most one binding can read the message body.
To apply a custom HttpParameterBinding, you can define an attribute that derives fromParameterBindingAttribute. For
ETagParameterBinding
, we’ll define two attributes, one for if-match
headers and one for if-none-match
headers. Both derive from an abstract base class.public abstract class ETagMatchAttribute : ParameterBindingAttribute
{
private ETagMatch _match;
public ETagMatchAttribute(ETagMatch match)
{
_match = match;
}
public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
{
if (parameter.ParameterType == typeof(ETag))
{
return new ETagParameterBinding(parameter, _match);
}
return parameter.BindAsError("Wrong parameter type");
}
}
public class IfMatchAttribute : ETagMatchAttribute
{
public IfMatchAttribute()
: base(ETagMatch.IfMatch)
{
}
}
public class IfNoneMatchAttribute : ETagMatchAttribute
{
public IfNoneMatchAttribute()
: base(ETagMatch.IfNoneMatch)
{
}
}
Here is a controller method that uses the
[IfNoneMatch]
attribute.public HttpResponseMessage Get([IfNoneMatch] ETag etag) { ... }
Besides ParameterBindingAttribute, there is another hook for adding a custom HttpParameterBinding. On theHttpConfiguration object, the ParameterBindingRules property is a collection of anomymous functions of type (HttpParameterDescriptor -> HttpParameterBinding). For example, you could add a rule that any ETag parameter on a GET method uses
ETagParameterBinding
with if-none-match
:config.ParameterBindingRules.Add(p =>
{
if (p.ParameterType == typeof(ETag) &&
p.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Get))
{
return new ETagParameterBinding(p, ETagMatch.IfNoneMatch);
}
else
{
return null;
}
});
The function should return
null
for parameters where the binding is not applicable.IActionValueBinder
The entire parameter-binding process is controlled by a pluggable service, IActionValueBinder. The default implementation of IActionValueBinder does the following:
- Look for a ParameterBindingAttribute on the parameter. This includes [FromBody], [FromUri], and[ModelBinder], or custom attributes.
- Otherwise, look in HttpConfiguration.ParameterBindingRules for a function that returns a non-nullHttpParameterBinding.
- Otherwise, use the default rules that I described previously.
- If the parameter type is “simple”or has a type converter, bind from the URI. This is equivalent to putting the [FromUri] attribute on the parameter.
- Otherwise, try to read the parameter from the message body. This is equivalent to putting [FromBody]on the parameter.
If you wanted, you could replace the entire IActionValueBinder service with a custom implementation.
Additional Resources
Mike Stall wrote a good series of blog posts about Web API parameter binding:
- How Web API does Parameter Binding
- MVC Style parameter binding for Web API
- How to bind to custom objects in action signatures in MVC/Web API
- How to create a custom value provider in Web API
- Web API Parameter binding under the hood
By Mike Wasson, Mike Wasson is a programmer-writer at Microsoft.
Подписаться на:
Сообщения (Atom)