OpenCV/EmguCV Version 2 to OpenCV/EmguCV Version 3 conversion - emgucv

Granted I am learning OpenCV/EmguCV and actually prefer EmguCV as the wrapper is quite nice.
But, lots of the examples/tutorials found out on the web are for version 2.X and do not compile under version 3.0. Things like Contour is not supported in version 3. Is there a document/web site that anyone can point me to so I spend less time porting and more time actually learning and doing?

I actually just answered this question here: Emgu CV 3 findContours and hierarchy parameter of type Vec4i equivalent?
As you mentioned, Contours are not correctly implemented in Emgu 3, but you can manually invoke the FindContours method and it will work:
/// <summary>
/// Find contours using the specific memory storage
/// </summary>
/// <param name="method">The type of approximation method</param>
/// <param name="type">The retrieval type</param>
/// <param name="stor">The storage used by the sequences</param>
/// <returns>
/// Contour if there is any;
/// null if no contour is found
/// </returns>
public static VectorOfVectorOfPoint FindContours(this Image<Gray, byte> image, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple,
Emgu.CV.CvEnum.RetrType type = RetrType.List) {
//Check that all parameters are valid.
VectorOfVectorOfPoint result = new VectorOfVectorOfPoint();
if (method == Emgu.CV.CvEnum.ChainApproxMethod.ChainCode) {
throw new ColsaNotImplementedException("Chain Code not implemented, sorry try again later");
}
CvInvoke.FindContours(image, result, null, type, method);
return result;
}
And to use those contours:
VectorOfVectorOfPoint contours = canvass2.FindContours(ChainApproxMethod.ChainApproxSimple, RetrType.Tree);
int contCount = contours.Size;
for (int i = 0; i < contCount; i++) {
using (VectorOfPoint contour = contours[i]) {
segmentRectangles.Add(CvInvoke.BoundingRectangle(contour));
if (debug) {
finalCopy.Draw(CvInvoke.BoundingRectangle(contour), new Rgb(255, 0, 0), 5);
}
}
}
I just went through the same conversion process, so I can answer a lot of the issues if you can be specific to what other problems you have had.

Related

Load Tests, trying to generate random names but getting same names for many virtual users

I'm using Visual Studio Performance Tests. I want to generate a random name before each of my requests. I'm using this WebTestRequestPlugin for that:
using System;
using System.ComponentModel;
using System.Linq;
using Microsoft.VisualStudio.TestTools.WebTesting;
namespace TransCEND.Tests.Performance.Plugins
{
public class RandomStringContextParameterWebRequestPlugin : WebTestRequestPlugin
{
[Description("Name of the Context Paramter that will sotre the random string.")]
[DefaultValue("RandomString")]
public string ContextParameter { get; set; }
[Description("Length of the random string.")]
[DefaultValue(10)]
public int Length { get; set; }
[Description("Prefix for the random string.")]
[DefaultValue("")]
public string Prefix { get; set; }
private readonly string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private Random _random = new Random();
public RandomStringContextParameterWebRequestPlugin()
{
ContextParameter = "RandomString";
Prefix = "";
Length = 10;
}
public override void PreRequestDataBinding(object sender, PreRequestDataBindingEventArgs e)
{
e.WebTest.Context[ContextParameter] = CreateNewRandomString();
base.PreRequestDataBinding(sender, e);
}
private string CreateNewRandomString()
{
var randomString = new string(Enumerable.Repeat(_chars, Length).Select(s => s[_random.Next(s.Length)]).ToArray()).ToLower();
return $"{Prefix}{randomString}";
}
}
}
My problem is that when I start a load test with multiple virtual users, the preRequest code runs for the first few users immediately, rewriting the RandomName context parameter on every run. So when my requests are actually running, they are using the same random name, causing a conflict in my back-end code.
My question is how can I generate random names for each of my requests even when the user load is high?
I think the problem is that the standard random number routines are not thread safe. Thus each virtual user (VU) gets the same random seed value and hence the same random numbers. See here and here for fuller explanations.
The code for CreateNewRandomString is not shown in the question but it probably uses the basic C# random number code which has the problem described above. The solution is to use a safer random number. This question provides some ideas on better random number generators.
I have used code based on the following in several performance tests:
public static class RandomNumber
{
private static Random rand = new Random(DateTime.Now.Millisecond);
private static object randLock = new object();
/// <summary>
/// Generate a random number.
/// </summary>
/// <param name="maxPlus1">1 more than the maximum value wanted.</param>
/// <returns>Value between 0 and maxPlus1-1 inclusive. Ie 0 .le. returned value .lt. maxPlus1</returns>
public static int Next(int maxPlus1)
{
int result;
lock (randLock)
{
result = rand.Next(maxPlus1);
}
return result;
}
}
It should be simple to add a string creation method to the above code, something that generates the wanted string within a lock{ ... } statement.
The part of the question that states "rewriting the RandomName context parameter on every run. So when my requests are actually running, they are using the same random name" is misunderstanding what is happening. Each VU gets its own set of CPs, it is just that the random numbers are the same.

Accord.NET Comparing two images to determine similarity

I would like your advice as to why the code might be becoming unresponsive and how to fix it.
I am using Accord.NET to compare images. The first stage of my project is to compare two images, an observed image and a model image, and determine how similar they are; the second, is to compare an observed image against my whole database to determine what the observed image most likely is based on how the models have been categorized. Right now I am focusing on the first. I initially tried using ExhaustiveTemplateMatching.ProcessImage() but it didn't fit my need. Now, I am using SURF. Here is my code as is:
public class ProcessImage
{
public static void Similarity(System.IO.Stream model, System.IO.Stream observed,
out float similPercent)
{
Bitmap bitModel = new Bitmap(model);
Bitmap bitObserved = new Bitmap(observed);
// For method Difference, see http://www.aforgenet.com/framework/docs/html/673023f7-799a-2ef6-7933-31ef09974dde.htm
// Inspiration for this process: https://www.youtube.com/watch?v=YHT46f2244E
// Greyscale class http://www.aforgenet.com/framework/docs/html/d7196dc6-8176-4344-a505-e7ade35c1741.htm
// Convert model and observed to greyscale
Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
// apply the filter to the model
Bitmap greyModel = filter.Apply(bitModel);
// Apply the filter to the observed image
Bitmap greyObserved = filter.Apply(bitObserved);
int modelPoints = 0, matchingPoints = 0;
/*
* This doesn't work. Images can have different sizes
// For an example, https://thecsharper.com/?p=94
// Match
var tm = new ExhaustiveTemplateMatching(similarityThreshold);
// Process the images
var results = tm.ProcessImage(greyModel, greyObserved);
*/
using (SpeededUpRobustFeaturesDetector detector = new SpeededUpRobustFeaturesDetector())
{
List<SpeededUpRobustFeaturePoint> surfModel = detector.ProcessImage(greyModel);
modelPoints = surfModel.Count();
List<SpeededUpRobustFeaturePoint> surfObserved = detector.ProcessImage(greyObserved);
KNearestNeighborMatching matcher = new KNearestNeighborMatching(5);
var results = matcher.Match(surfModel, surfObserved);
matchingPoints = results.Length;
}
// Determine if they represent the same points
// Obtain the pairs of associated points, we determine the homography matching all these pairs
// Compare the results, 0 indicates no match so return false
if (matchingPoints <= 0)
{
similPercent = 0.0f;
}
similPercent = (matchingPoints * 100) / modelPoints;
}
}
So far I get to obtain the list of points but then when matching the code seems to become unresponsive.
I am calling the above code from an ASP.NET web page after the user posts a bitmap. Here is the code if it may help:
public ActionResult Compare(int id)
{
ViewData["SampleID"] = id;
return View();
}
[HttpPost]
public ActionResult Compare(int id, HttpPostedFileBase uploadFile)
{
Sample model = _db.Sample_Read(id);
System.IO.Stream modelStream = null;
float result = 0;
_db.Sample_Stream(model.FileId, out modelStream);
ImgProc.ProcessImage.Similarity(modelStream, uploadFile.InputStream,
out result);
ViewData["SampleID"] = id;
ViewData["match"] = result;
return View();
}
The page itself is rather simple, a hidden field, an file type input and a submit.
Problem was my PC. After some time processing the calculation finishes.
Thanks,
For KNearestNeighborMatching to decide, it is necessary to put
Accord.Imaging and Accord.Vision.

Draw a JUNG graph having more than two vertices between a pair of nodes

Ok so here is a clearer explanation :
I have now understood that I need to use sparse ou SparseMultigraph type to be able to have bidirectional edges so I have changed my GraphML class as such :
class GraphML
{
public GraphML(String filename) throws ParserConfigurationException, SAXException, IOException
{
//Step 1 we make a new GraphML Reader. We want a directed Graph of type node and edge.
GraphMLReader<SparseMultigraph<node, edge>, node, edge> gmlr =
new GraphMLReader<SparseMultigraph<node, edge>, node, edge>(new VertexFactory(), new EdgeFactory());
//Next we need a Graph to store the data that we are reading in from GraphML. This is also a directed Graph
// because it needs to match to the type of graph we are reading in.
final SparseMultigraph<node, edge> graph = new SparseMultigraph<node, edge>();
gmlr.load(filename, graph);
// gmlr.load(filename, graph); //Here we read in our graph. filename is our .graphml file, and graph is where we
// will store our graph.
BidiMap<node, String> vertex_ids = gmlr.getVertexIDs(); //The vertexIDs are stored in a BidiMap.
Map<String, GraphMLMetadata<node>> vertex_color = gmlr.getVertexMetadata(); //Our vertex Metadata is stored in a map.
Map<String, GraphMLMetadata<edge>> edge_meta = gmlr.getEdgeMetadata(); // Our edge Metadata is stored in a map.
// Here we iterate through our vertices, n, and we set the value and the color of our nodes from the data we have
// in the vertex_ids map and vertex_color map.
for (node n : graph.getVertices())
{
n.setValue(vertex_ids.get(n)); //Set the value of the node to the vertex_id which was read in from the GraphML Reader.
n.setColor(vertex_color.get("d0").transformer.transform(n)); // Set the color, which we get from the Map, vertex_color.
//Let's print out the data so we can get a good understanding of what we've got going on.
System.out.println("ID: "+n.getID()+", Value: "+n.getValue()+", Color: "+n.getColor());
}
// Just as we added the vertices to the graph, we add the edges as well.
for (edge e : graph.getEdges())
{
e.setValue(edge_meta.get("d1").transformer.transform(e)); //Set the edge's value.
System.out.println("Edge ID: "+e.getID());
}
TreeBuilder treeBuilder = new TreeBuilder(graph);
// create a simple graph for the demo:
//First we make a VisualizationViewer, of type node, edge. We give it our Layout, and the Layout takes a graph in it's constructor.
//VisualizationViewer<node, edge> vv = new VisualizationViewer<node, edge>(new FRLayout<node, edge>(graph));
VisualizationViewer<node, edge> vv = new VisualizationViewer<node, edge>(new TreeLayout<node, edge>(treeBuilder.getTree()));
//Next we set some rendering properties. First we want to color the vertices, so we provide our own vertexPainter.
vv.getRenderContext().setVertexFillPaintTransformer(new vertexPainter());
//Then we want to provide labels to our nodes, Jung provides a nice function which makes the graph use a vertex's ToString function
//as it's way of labelling. We do the same for the edge. Look at the edge and node classes for their ToString function.
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller<node>());
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller<edge>());
// Next we do some Java stuff, we create a frame to hold the graph
final JFrame frame = new JFrame();
frame.setTitle("GraphMLReader for Trees - Reading in Attributes"); //Set the title of our window.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Give a close operation.
//Here we get the contentPane of our frame and add our a VisualizationViewer, vv.
frame.getContentPane().add(vv);
//Finally, we pack it to make sure it is pretty, and set the frame visible. Voila.
frame.pack();
frame.setVisible(true);
}
}
And then changed my tree builder class constructor to SparseMultigraph :
public class TreeBuilder
{
DelegateForest<node,edge> mTree;
TreeBuilder(SparseMultigraph<node, edge> graph)
{
mTree = new DelegateForest<node, edge>();
for (node n : graph.getVertices())
{
mTree.addVertex(n);
}
for (edge e : graph.getEdges())
{
mTree.addEdge(e, graph.getSource(e),graph.getDest(e));
}
}
public DelegateForest<node, edge> getTree()
{
return mTree;
}
}
when I run my Main class :
public class Main
{
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException
{
String filename = "attributes.graphml";
if(args.length > 0)
filename = args[0];
new GraphML(filename);
}
}
I don't get an error but edges are not present (node are their in the graph but not properly displayed).
Thanks
Zied

LINQ to Entities does not recognize the method 'Boolean Contains[Decimal]

I am new to LINQ, so I am pretty confused here. I have a database and try to run following code.
IQueryable<decimal> location_ids = (from m in _db.Admins
where m.UserId.Equals("c5d3dc0e-81e6-4d6b-a9c3-faa802e10b7d")
select m.LocationId);
if (!location_ids.Contains(new Decimal(conf.umisteni.Budova.ID)))
On the if statement I get an error I don't understand, nor do I know, how to solve it:
System.NotSupportedException: LINQ to Entities does not recognize the method 'Boolean Contains[Decimal](System.Linq.IQueryable`1[System.Decimal], System.Decimal)' method, and this method cannot be translated into a store expression.
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.DefaultTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
Any ideas?
Using Linq-to-Objects IEnumerable will let you use Contains(Decimal) on the result of the query.
IEnumerable<decimal> location_ids = (from m in _db.Admins
where m.UserId.Equals("c5d3dc0e-81e6-4d6b-a9c3-faa802e10b7d")
select m.LocationId);
However, why not just expand the where clause:
IEnumerable<decimal> location_ids = (from m in _db.Admins
where m.UserId.Equals("c5d3dc0e-81e6-4d6b-a9c3-faa802e10b7d") && (m.LocationId == conf.umisteni.Budova.ID)
select m.LocationId);
if (!location_ids.Any())
Linq 2 sql cannot translate the ids.Contains() method to sql.
You could do the following:
if(!location_ids.ToList().Contains(new Decimal(conf.umisteni.Budova.ID)))
This will trigger the sql query, put them in objects and do the contains locally.
Another solution would be to put the conf.umisteni.Budova.Id in the Where clauses (with an equals, not a contains) and then add .any
if((from m in _db.Admins
where m.UserId.Equals(conf.umisteni.Budova.ID.ToString())
select m.LocationId).Any())
This would only be a good idea if you don't need any of the keys afterwards off course.
Here is a helper method that provides all of the goodness of .Contains() in the context of Linq to Entities
public static class LinqToEntitiesUtil
{
/// <summary>
/// Extension method that enables .Contains(obj) like functionality for Linq to Entities.
///
/// Source: http://www.velocityreviews.com/forums/t645784-linq-where-clause.html
/// </summary>
/// <typeparam name="TElement">The element being evaluated by the Where clause</typeparam>
/// <typeparam name="TValue">The value to match</typeparam>
/// <param name="valueSelector">Lamda for selecting matching values</param>
/// <param name="values">IEnumerable of the values</param>
/// <returns>Expression consumable by Linq to Entities that reflects semantics of .Contains(value)</returns>
/// <remarks>
/// Usage:
///
/// Replace expression like
///
/// where ChildrenIDs.Contains(items.CategoryID)
///
/// with
///
/// .Where((BuildContainsExpression<Item, int>(item => item.CategoryID, ChildrenIDs))
///
/// NOTE: If the item collection is large, the SQL query will be as well.
/// </remarks>
static public Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
}
I just got bit by something similar. Your System.Data.dll might not be the same version. The method was supported on my dev machine, but then I got runtime errors when I deployed to a test machine. The System.Data.dll version was older. Backwards compatible methods are a good fix. But I would still want to track down the discrepancy, some patch must be applied that didn't on the other environments. Who knows what other issues this will cause down the road if left unchecked.

Best way to implement request throttling in ASP.NET MVC?

We're experimenting with various ways to throttle user actions in a given time period:
Limit question/answer posts
Limit edits
Limit feed retrievals
For the time being, we're using the Cache to simply insert a record of user activity - if that record exists if/when the user does the same activity, we throttle.
Using the Cache automatically gives us stale data cleaning and sliding activity windows of users, but how it will scale could be a problem.
What are some other ways of ensuring that requests/user actions can be effectively throttled (emphasis on stability)?
Here's a generic version of what we've been using on Stack Overflow for the past year:
/// <summary>
/// Decorates any MVC route that needs to have client requests limited by time.
/// </summary>
/// <remarks>
/// Uses the current System.Web.Caching.Cache to store each client request to the decorated route.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ThrottleAttribute : ActionFilterAttribute
{
/// <summary>
/// A unique name for this Throttle.
/// </summary>
/// <remarks>
/// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-192.168.0.1"
/// </remarks>
public string Name { get; set; }
/// <summary>
/// The number of seconds clients must wait before executing this decorated route again.
/// </summary>
public int Seconds { get; set; }
/// <summary>
/// A text message that will be sent to the client upon throttling. You can include the token {n} to
/// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
/// </summary>
public string Message { get; set; }
public override void OnActionExecuting(ActionExecutingContext c)
{
var key = string.Concat(Name, "-", c.HttpContext.Request.UserHostAddress);
var allowExecute = false;
if (HttpRuntime.Cache[key] == null)
{
HttpRuntime.Cache.Add(key,
true, // is this the smallest data we can have?
null, // no dependencies
DateTime.Now.AddSeconds(Seconds), // absolute expiration
Cache.NoSlidingExpiration,
CacheItemPriority.Low,
null); // no callback
allowExecute = true;
}
if (!allowExecute)
{
if (String.IsNullOrEmpty(Message))
Message = "You may only perform this action every {n} seconds.";
c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) };
// see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
}
}
}
Sample usage:
[Throttle(Name="TestThrottle", Message = "You must wait {n} seconds before accessing this url again.", Seconds = 5)]
public ActionResult TestThrottle()
{
return Content("TestThrottle executed");
}
The ASP.NET Cache works like a champ here - by using it, you get automatic clean-up of your throttle entries. And with our growing traffic, we're not seeing that this is an issue on the server.
Feel free to give feedback on this method; when we make Stack Overflow better, you get your Ewok fix even faster :)
Microsoft has a new extension for IIS 7 called Dynamic IP Restrictions Extension for IIS 7.0 - Beta.
"The Dynamic IP Restrictions for IIS 7.0 is a module that provides protection against denial of service and brute force attacks on web server and web sites. Such protection is provided by temporarily blocking IP addresses of the HTTP clients who make unusually high number of concurrent requests or who make large number of requests over small period of time."
http://learn.iis.net/page.aspx/548/using-dynamic-ip-restrictions/
Example:
If you set the criteria to block after X requests in Y milliseconds or X concurrent connections in Y milliseconds the IP address will be blocked for Y milliseconds then requests will be permitted again.
We use the technique borrowed from this URL http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx, not for throttling, but for a poor man's Denial Of Service (D.O.S). This is also cache-based, and may be similar to what you are doing. Are you throttling to prevent D.O.S. attacks? Routers can certainly be used to reduce D.O.S; do you think a router could handle the throttling you need?
It took me some time to work out an equivalent for .NET 5+ (formerly .NET Core), so here's a starting point.
The old way of caching has gone and been replaced by Microsoft.Extensions.Caching.Memory with IMemoryCache.
I separated it out a bit more, so here's what you need...
The Cache Management Class
I've added the whole thing here, so you can see the using statements.
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using System;
using System.Threading;
namespace MyWebApplication
{
public interface IThrottleCache
{
bool AddToCache(string key, int expriryTimeInSeconds);
bool AddToCache<T>(string key, T value, int expriryTimeInSeconds);
T GetFromCache<T>(string key);
bool IsInCache(string key);
}
/// <summary>
/// A caching class, based on the docs
/// https://learn.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-6.0
/// Uses the recommended library "Microsoft.Extensions.Caching.Memory"
/// </summary>
public class ThrottleCache : IThrottleCache
{
private IMemoryCache _memoryCache;
public ThrottleCache(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public bool AddToCache(string key, int expriryTimeInSeconds)
{
bool isSuccess = false; // Only a success if a new value gets added.
if (!IsInCache(key))
{
var cancellationTokenSource = new CancellationTokenSource(
TimeSpan.FromSeconds(expriryTimeInSeconds));
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSize(1)
.AddExpirationToken(
new CancellationChangeToken(cancellationTokenSource.Token));
_memoryCache.Set(key, DateTime.Now, cacheEntryOptions);
isSuccess = true;
}
return isSuccess;
}
public bool AddToCache<T>(string key, T value, int expriryTimeInSeconds)
{
bool isSuccess = false;
if (!IsInCache(key))
{
var cancellationTokenSource = new CancellationTokenSource(
TimeSpan.FromSeconds(expriryTimeInSeconds));
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(DateTimeOffset.Now.AddSeconds(expriryTimeInSeconds))
.SetSize(1)
.AddExpirationToken(
new CancellationChangeToken(cancellationTokenSource.Token));
_memoryCache.Set<T>(key, value, cacheEntryOptions);
isSuccess = true;
}
return isSuccess;
}
public T GetFromCache<T>(string key)
{
return _memoryCache.Get<T>(key);
}
public bool IsInCache(string key)
{
var item = _memoryCache.Get(key);
return item != null;
}
}
}
The attribute itself
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Net;
namespace MyWebApplication
{
/// <summary>
/// Decorates any MVC route that needs to have client requests limited by time.
/// Based on how they throttle at stack overflow (updated for .NET5+)
/// https://stackoverflow.com/questions/33969/best-way-to-implement-request-throttling-in-asp-net-mvc/1318059#1318059
/// </summary>
/// <remarks>
/// Uses the current System.Web.Caching.Cache to store each client request to the decorated route.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ThrottleByIPAddressAttribute : ActionFilterAttribute
{
/// <summary>
/// The caching class (which will be instantiated as a singleton)
/// </summary>
private IThrottleCache _throttleCache;
/// <summary>
/// A unique name for this Throttle.
/// </summary>
/// <remarks>
/// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-192.168.0.1"
/// </remarks>
public string Name { get; set; }
/// <summary>
/// The number of seconds clients must wait before executing this decorated route again.
/// </summary>
public int Seconds { get; set; }
/// <summary>
/// A text message that will be sent to the client upon throttling. You can include the token {n} to
/// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
/// </summary>
public string Message { get; set; } = "You may only perform this action every {n} seconds.";
public override void OnActionExecuting(ActionExecutingContext c)
{
if(_throttleCache == null)
{
var cache = c.HttpContext.RequestServices.GetService(typeof(IThrottleCache));
_throttleCache = (IThrottleCache)cache;
}
var key = string.Concat(Name, "-", c.HttpContext.Request.HttpContext.Connection.RemoteIpAddress);
var allowExecute = _throttleCache.AddToCache(key, Seconds);
if (!allowExecute)
{
if (String.IsNullOrEmpty(Message))
Message = "You may only perform this action every {n} seconds.";
c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) };
// see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
}
}
}
}
Startup.cs or Program.cs - Register the services with DI
This example uses Startup.cs/ConfigureServices - Put the code somewhere after AddControllersWithViews).
For a project created in .NET6+ I think you'd add the equivalent between builder.Services.AddRazorPages(); and var app = builder.Build(); in program.cs. services would be builder.Services.
If you don't get the placement of this code right, the cache will be empty every time you check it.
// The cache for throttling must be a singleton and requires IMemoryCache to be set up.
// Place it after AddControllersWithViews or AddRazorPages as they build a cache themselves
// Need this for IThrottleCache to work.
services.AddMemoryCache(_ => new MemoryCacheOptions
{
SizeLimit = 1024, /* TODO: CHECK THIS IS THIS THE RIGHT SIZE FOR YOU! */
CompactionPercentage = .3,
ExpirationScanFrequency = TimeSpan.FromSeconds(30),
});
services.AddSingleton<IThrottleCache, ThrottleCache>();
Example Usage
[HttpGet, Route("GetTest")]
[ThrottleByIPAddress(Name = "MyControllerGetTest", Seconds = 5)]
public async Task<ActionResult<string>> GetTest()
{
return "Hello world";
}
To help understand caching in .NET 5+, I've also made a caching console demo.
Since the highly voted answers to this question are too old, I am sharing the latest solution which worked for me.
I tried using the Dynamic IP restrictions as given in an answer on this page but when I tried to use that extension, I found that this extension has been discontinued by Microsoft and on the download page they have clearly written the below message.
Microsoft has discontinued the Dynamic IP Restrictions extension and this download is no longer available.
So I researched further and found that the Dynamic IP Restrictions is now by default included in IIS 8.0 and above. The below information is fetched from the Microsoft Dynamic IP Restrictions page.
In IIS 8.0, Microsoft has expanded the built-in functionality to include several new features:
Dynamic IP address filtering, which allows administrators to
configure their server to block access for IP addresses that exceed
the specified number of requests.
The IP address filtering features now allow administrators to specify
the behavior when IIS blocks an IP address, so requests from
malicious clients can be aborted by the server instead of returning
HTTP 403.6 responses to the client.
IP filtering now feature a proxy mode, which allows IP addresses to
be blocked not only by the client IP that is seen by IIS but also by
the values that are received in the x-forwarded-for HTTP header
For step by step instructions to implement Dynamic IP Restrictions, please visit the below link:
https://learn.microsoft.com/en-us/iis/get-started/whats-new-in-iis-8/iis-80-dynamic-ip-address-restrictions
I hope it helps someone stuck in a similar problem.
Created ThrottlingTroll - my take on throttling/rate limiting in ASP.NET Core.
It is similar to Stefan Prodan's AspNetCoreRateLimit and ASP.NET 7's Rate Limiting Middleware, but has advantages:
Both ingress and egress throttling (egress means that your specially configured HttpClient won't make more than N requests per second and will instead produce 429 status code by itself).
Distributed rate counter stores (including, but not limited to Redis).
Dynamic (re)configuration - allows to adjust limits without restarting the service.
Propagating 429 statuses from egress to ingress.
Check out more in the repo.

Resources