Skip to content

Commit

Permalink
add package enhancements (#44)
Browse files Browse the repository at this point in the history
* move http context to correct namespace

* fix test namespaces

* update code style

* add roslyn and fix some hints
  • Loading branch information
BenjaminAbt authored Apr 1, 2024
1 parent 2a344bc commit 6c18506
Show file tree
Hide file tree
Showing 33 changed files with 950 additions and 876 deletions.
94 changes: 54 additions & 40 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,41 +1,55 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup Label=".NET Dependencies">
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackageVersion Include="NaughtyStrings" Version="2.4.1" />
</ItemGroup>
<ItemGroup Label="ASP.NET Core Dependencies">
<PackageVersion Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
</ItemGroup>
<ItemGroup Label="Libraries for comparison">
<PackageVersion Include="UAParser" Version="3.1.47" />
<PackageVersion Include="DeviceDetector.NET" Version="4.3.0" />
<PackageVersion Include="Ng.UserAgentService" Version="1.1.0" />
</ItemGroup>
<ItemGroup Label="Tests / Performance">
<PackageVersion Include="BenchmarkDotNet" Version="0.13.11" />
<PackageVersion Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.11" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="FluentAssertions" Version="7.0.0-alpha.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23503-02" />
<PackageVersion Include="xunit" Version="2.6.3" />
<PackageVersion Include="xunit.runner.console" Version="2.6.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="coverlet.msbuild" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
</ItemGroup>
</Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>

<ItemGroup Label=".NET Dependencies">
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageVersion Include="NaughtyStrings" Version="2.4.1" />
</ItemGroup>

<ItemGroup Label="Libraries for comparison">
<PackageVersion Include="UAParser" Version="3.1.47" />
<PackageVersion Include="DeviceDetector.NET" Version="6.3.3" />
<PackageVersion Include="Ng.UserAgentService" Version="3.0.0" />
</ItemGroup>

<ItemGroup Label="Tests / Performance">
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.12" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="FluentAssertions" Version="7.0.0-alpha.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="xunit" Version="2.7.0" />
<PackageVersion Include="xunit.runner.console" Version="2.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="coverlet.msbuild" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
</ItemGroup>

<ItemGroup Label="Roslyn">
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageVersion>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@
using MyCSharp.HttpUserAgentParser.DependencyInjection;
using MyCSharp.HttpUserAgentParser.Providers;

namespace MyCSharp.HttpUserAgentParser.AspNetCore.DependencyInjection
namespace MyCSharp.HttpUserAgentParser.AspNetCore.DependencyInjection;

/// <summary>
/// Dependency injection extensions for ASP.NET Core environments
/// </summary>
public static class HttpUserAgentParserDependencyInjectionOptionsExtensions
{
/// <summary>
/// Dependency injection extensions for ASP.NET Core environments
/// Registers <see cref="HttpUserAgentParserAccessor"/> as <see cref="IHttpUserAgentParserAccessor"/>.
/// Requires a registered <see cref="IHttpUserAgentParserProvider"/>
/// </summary>
public static class HttpUserAgentParserDependencyInjectionOptionsExtensions
public static HttpUserAgentParserDependencyInjectionOptions AddHttpUserAgentParserAccessor(
this HttpUserAgentParserDependencyInjectionOptions options)
{
/// <summary>
/// Registers <see cref="HttpUserAgentParserAccessor"/> as <see cref="IHttpUserAgentParserAccessor"/>.
/// Requires a registered <see cref="IHttpUserAgentParserProvider"/>
/// </summary>
public static HttpUserAgentParserDependencyInjectionOptions AddHttpUserAgentParserAccessor(
this HttpUserAgentParserDependencyInjectionOptions options)
{
options.Services.AddSingleton<IHttpUserAgentParserAccessor, HttpUserAgentParserAccessor>();
return options;
}
options.Services.AddSingleton<IHttpUserAgentParserAccessor, HttpUserAgentParserAccessor>();
return options;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;

namespace MyCSharp.HttpUserAgentParser;
namespace MyCSharp.HttpUserAgentParser.AspNetCore;

/// <summary>
/// Static extensions for <see cref="HttpContext"/>
Expand All @@ -16,9 +16,7 @@ public static class HttpContextExtensions
public static string? GetUserAgentString(this HttpContext httpContext)
{
if (httpContext.Request.Headers.TryGetValue("User-Agent", out StringValues value))
{
return value;
}

return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,36 @@
using Microsoft.AspNetCore.Http;
using MyCSharp.HttpUserAgentParser.Providers;

namespace MyCSharp.HttpUserAgentParser.AspNetCore
namespace MyCSharp.HttpUserAgentParser.AspNetCore;

/// <summary>
/// User Agent parser accessor. Implements <see cref="IHttpContextAccessor.HttpContext"/>
/// </summary>
/// <remarks>
/// Creates a new instance of <see cref="HttpUserAgentParserAccessor"/>
/// </remarks>
public class HttpUserAgentParserAccessor(IHttpUserAgentParserProvider httpUserAgentParser)
: IHttpUserAgentParserAccessor
{
private readonly IHttpUserAgentParserProvider _httpUserAgentParser = httpUserAgentParser;

/// <summary>
/// User Agent parser accessor
/// User agent of current <see cref="IHttpContextAccessor"/>
/// </summary>
public interface IHttpUserAgentParserAccessor
{
/// <summary>
/// User agent value
/// </summary>
string? GetHttpContextUserAgent(HttpContext httpContext);

/// <summary>
/// Returns current <see cref="HttpUserAgentInformation"/>
/// </summary>
HttpUserAgentInformation? Get(HttpContext httpContext);
}
public string? GetHttpContextUserAgent(HttpContext httpContext)
=> httpContext.GetUserAgentString();

/// <summary>
/// User Agent parser accessor. Implements <see cref="IHttpContextAccessor.HttpContext"/>
/// Returns current <see cref="HttpUserAgentInformation"/> of current <see cref="IHttpContextAccessor"/>
/// </summary>
public class HttpUserAgentParserAccessor : IHttpUserAgentParserAccessor
public HttpUserAgentInformation? Get(HttpContext httpContext)
{
private readonly IHttpUserAgentParserProvider _httpUserAgentParser;

/// <summary>
/// Creates a new instance of <see cref="HttpUserAgentParserAccessor"/>
/// </summary>
public HttpUserAgentParserAccessor(IHttpUserAgentParserProvider httpUserAgentParser)
string? httpUserAgent = GetHttpContextUserAgent(httpContext);
if (string.IsNullOrEmpty(httpUserAgent))
{
_httpUserAgentParser = httpUserAgentParser;
return null;
}

/// <summary>
/// User agent of current <see cref="IHttpContextAccessor"/>
/// </summary>
public string? GetHttpContextUserAgent(HttpContext httpContext)
=> httpContext.GetUserAgentString();

/// <summary>
/// Returns current <see cref="HttpUserAgentInformation"/> of current <see cref="IHttpContextAccessor"/>
/// </summary>
public HttpUserAgentInformation? Get(HttpContext httpContext)
{
string? httpUserAgent = GetHttpContextUserAgent(httpContext);
if (string.IsNullOrEmpty(httpUserAgent))
{
return null;
}

return _httpUserAgentParser.Parse(httpUserAgent);
}
return _httpUserAgentParser.Parse(httpUserAgent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright © myCSharp.de - all rights reserved

using Microsoft.AspNetCore.Http;

namespace MyCSharp.HttpUserAgentParser.AspNetCore;

/// <summary>
/// User Agent parser accessor
/// </summary>
public interface IHttpUserAgentParserAccessor
{
/// <summary>
/// User agent value
/// </summary>
string? GetHttpContextUserAgent(HttpContext httpContext);

/// <summary>
/// Returns current <see cref="HttpUserAgentInformation"/>
/// </summary>
HttpUserAgentInformation? Get(HttpContext httpContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,26 @@
<None Include="readme.md" Pack="true" PackagePath="" />
<None Include="LICENSE.txt" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup Label="NuGet Roslyn">
<PackageReference Include="Roslynator.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Formatting.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\MyCSharp.HttpUserAgentParser\MyCSharp.HttpUserAgentParser.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,65 +1,58 @@
// Copyright © myCSharp.de - all rights reserved

using System;
using Microsoft.Extensions.Caching.Memory;
using MyCSharp.HttpUserAgentParser.Providers;

namespace MyCSharp.HttpUserAgentParser.MemoryCache
namespace MyCSharp.HttpUserAgentParser.MemoryCache;

/// <inheritdoc/>
/// <summary>
/// Creates a new instance of <see cref="HttpUserAgentParserMemoryCachedProvider"/>.
/// </summary>
/// <param name="options">The options used to set expiration and size limit</param>
public class HttpUserAgentParserMemoryCachedProvider(
HttpUserAgentParserMemoryCachedProviderOptions options) : IHttpUserAgentParserProvider
{
private readonly Microsoft.Extensions.Caching.Memory.MemoryCache _memoryCache = new(options.CacheOptions);
private readonly HttpUserAgentParserMemoryCachedProviderOptions _options = options;

/// <inheritdoc/>
public class HttpUserAgentParserMemoryCachedProvider : IHttpUserAgentParserProvider
public HttpUserAgentInformation Parse(string userAgent)
{
private readonly Microsoft.Extensions.Caching.Memory.MemoryCache _memoryCache;
private readonly HttpUserAgentParserMemoryCachedProviderOptions _options;

/// <summary>
/// Creates a new instance of <see cref="HttpUserAgentParserMemoryCachedProvider"/>.
/// </summary>
/// <param name="options">The options used to set expiration and size limit</param>
public HttpUserAgentParserMemoryCachedProvider(HttpUserAgentParserMemoryCachedProviderOptions options)
{
_memoryCache = new Microsoft.Extensions.Caching.Memory.MemoryCache(options.CacheOptions);
_options = options;
}
CacheKey key = this.GetKey(userAgent);

/// <inheritdoc/>
public HttpUserAgentInformation Parse(string userAgent)
return _memoryCache.GetOrCreate(key, static entry =>
{
CacheKey key = this.GetKey(userAgent);

return _memoryCache.GetOrCreate(key, static entry =>
{
CacheKey key = (entry.Key as CacheKey)!;
entry.SlidingExpiration = key.Options.CacheEntryOptions.SlidingExpiration;
entry.SetSize(1);
CacheKey key = (entry.Key as CacheKey)!;
entry.SlidingExpiration = key.Options.CacheEntryOptions.SlidingExpiration;
entry.SetSize(1);
return HttpUserAgentParser.Parse(key.UserAgent);
});
}
return HttpUserAgentParser.Parse(key.UserAgent);
});
}

[ThreadStatic]
private static CacheKey? s_tKey;
[ThreadStatic]
private static CacheKey? s_tKey;

private CacheKey GetKey(string userAgent)
{
CacheKey key = s_tKey ??= new CacheKey();
private CacheKey GetKey(string userAgent)
{
CacheKey key = s_tKey ??= new CacheKey();

key.UserAgent = userAgent;
key.Options = _options;
key.UserAgent = userAgent;
key.Options = _options;

return key;
}
return key;
}

private class CacheKey : IEquatable<CacheKey> // required for IMemoryCache
{
public string UserAgent { get; set; } = null!;
private class CacheKey : IEquatable<CacheKey> // required for IMemoryCache
{
public string UserAgent { get; set; } = null!;

public HttpUserAgentParserMemoryCachedProviderOptions Options { get; set; } = null!;
public HttpUserAgentParserMemoryCachedProviderOptions Options { get; set; } = null!;

public bool Equals(CacheKey? other) => this.UserAgent == other?.UserAgent;
public override bool Equals(object? obj) => this.Equals(obj as CacheKey);
public bool Equals(CacheKey? other) => this.UserAgent == other?.UserAgent;
public override bool Equals(object? obj) => this.Equals(obj as CacheKey);

public override int GetHashCode() => this.UserAgent.GetHashCode();
}
public override int GetHashCode() => this.UserAgent.GetHashCode();
}
}
Loading

0 comments on commit 6c18506

Please sign in to comment.