UNITY v.3 - BehaviorInterception using namespace matching - unity-container

How can I apply a behavior to all interface in a specific namespace?
I know how to apply a behavior to a concrete interface like IMyBlFacade,
but I don't want to do that for all interfaces separately, but in one shot.
Is implementing a ICallHandler obsolete when using custom IInterfaceBehaviors?
As I understand both build up a pipeline for interception.
What is the benefit of using ootb callhandlers and custom callhandlers over IInterfacebehaviors?
I don't want it to be like this:
unity.RegisterType<IMyService, MyService>(
new ContainerControlledLifetimeManager(),
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<OutputInterceptionBehavior>());
rather like this (pseudo code):
unity.addInterceptor<InterfaceInterceptor>()
.addMachingRule<namespace>("mynamespace")
.addBehaviors(...);

So it is partly possible using Unity's RegistrationByConvention.
As far as I have understood, you can only do simple mappings.
For more complex mappings, for example using various InjectionMembers, you have to map them manually.
You have to inherit from RegistrationConvention to build your own convention implementation.
public class UnityRegistrationByConventions : RegistrationConvention
{
private readonly IUnityContainer _container;
List<string> _assemblyNameFilter;
List<string> _namespaceFilterForClasses;
public UnityRegistrationByConventions(IUnityContainer container, List<string> assemblyNameFilter = null, List<string> namespaceFilterForClasses = null)
{
_container = container;
_assemblyNameFilter = assemblyNameFilter;
_namespaceFilterForClasses = namespaceFilterForClasses;
}
public override Func<Type, IEnumerable<Type>> GetFromTypes()
{
return WithMappings.FromMatchingInterface;
}
public override Func<Type, IEnumerable<InjectionMember>> GetInjectionMembers()
{
return (t => new List<InjectionMember>(){
new Interceptor<InterfaceInterceptor>(),
new InterceptionBehavior<LoggingInterceptionBehavior>(), // 1
new InterceptionBehavior<ExceptionInterceptionBehavior>(), // 2
new InterceptionBehavior<CachingInterceptionBehavior>(), // 3
new InterceptionBehavior<ValidationInterceptionBehavior>()} as IEnumerable<InjectionMember>);
}
public override Func<Type, LifetimeManager> GetLifetimeManager()
{
return t => WithLifetime.Transient(t);
}
public override Func<Type, string> GetName()
{
return (type => (this._container.Registrations.Select(x => x.RegisteredType)
.Any(r => type.GetInterfaces().Contains(r) == true) == true) ? WithName.TypeName(type) : WithName.Default(type));
}
public override IEnumerable<Type> GetTypes()
{
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => _assemblyNameFilter.Contains(a.FullName.Split(',')[0]));
List<Type> allClasses = new List<Type>();
foreach (var assembly in allAssemblies)
{
var classArray = assembly.GetTypes().Where(t => t.IsPublic &&
!t.IsAbstract &&
t.IsClass == true &&
_namespaceFilterForClasses.Contains(t.Namespace));
if (classArray != null && classArray.Count() > 0)
allClasses.AddRange(classArray);
}
return allClasses;
}
}
and apply the convention like this
var rby = new UnityRegistrationByConventions(unityContainer, assFilter, classNamespaceFilter);

Related

ApplicationInsights request customDimensions

Is it possible to add values to the customDimensions property of records in requests in ApplicationInsights? How?
I've found that logger.BeginScope can add them to traces -- the value must be Dictionary<string, object> (Passing Dictionary<string, string> does not work.)
Adding the properties to Activity.Current.AddTag has no effect.
Nor does adding properties to TelemetryClient.Properties.
(This is deprecated but nobody seems to know what to do instead.)
The answer is to use a custom ITelemetryInitializer like this
internal class TelemetryInitializer : ITelemetryInitializer
{
private static readonly string[] _Headers = new string[] { "Referer" /*sic */, "X-Forwarded-For", };
private readonly IHttpContextAccessor _httpContextAccessor;
public TelemetryInitializer(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Initialize(ITelemetry item)
{
ISupportProperties tcp = item as ISupportProperties;
if (item == null || _httpContextAccessor.HttpContext == null) { return; }
LogTags tags = new LogTags();
tags.AddTag("BuildName", BuildNameAttribute.GetBuildName());
tags.AddTag("Method", _httpContextAccessor.HttpContext.Request.Method);
tags.AddTag("Host", _httpContextAccessor.HttpContext.Request.Host.Value);
// RequestPath is added automaticallt.
tags.AddTags(_httpContextAccessor.HttpContext.Request.Headers.Where(z => _Headers.Contains(z.Key)).SelectMany(z => z.Value.Select(y => new KeyValuePair<string, string>(z.Key, y))));
tags.AddTags(_httpContextAccessor.HttpContext.Request.Query.SelectMany(z => z.Value.Select(y => new KeyValuePair<string, string>(z.Key, y))));
if (_httpContextAccessor.HttpContext.Request.HasFormContentType && _httpContextAccessor.HttpContext.Request.Form != null)
{
tags.AddTags(_httpContextAccessor.HttpContext.Request.Form.Where(z => z.Key.ToLower().Contains("password")).SelectMany(z => z.Value.Select(y => new KeyValuePair<string, string>(z.Key, y.Length > 21 ? y.Substring(0, 21) + "..." : y))));
tags.AddTags(_httpContextAccessor.HttpContext.Request.Form.Files.Select(z => new KeyValuePair<string, string>("File", $"{z.ContentType} '{z.FileName}' {z.Length}")));
}
if (_httpContextAccessor.HttpContext.User.Identity?.IsAuthenticated ?? false)
{
tags.AddTags(_httpContextAccessor.HttpContext.User.Claims.Select(z => new KeyValuePair<string, string>(z.Type, z.Value)));
}
else
{
tags.AddTag("authentication", "none");
}
foreach (KeyValuePair<string, object> tag in tags.GetAllTags())
{
tcp.Properties[tag.Key] = tag.Value.ToString();
}
}
}
and
public static ILoggingBuilder AddLogging(this ILoggingBuilder builder, bool isDevelopment)
{
builder.ClearProviders();
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
//builder.Services.AddApplicationInsightsTelemetry("key");
//Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.TelemetryChannel.DeveloperMode = true;
if (isDevelopment)
{
builder.AddBetterConsoleFormatter();
//builder.AddHttpRequestLogger(); // Just can't get this to work :(
//builder.AddBetterConsoleLogger(); // The singleton doesn't work very well and I don't understand why correlation IDs are omitted/
//builder.AddConsole();
builder.AddDebug();
}
else
{
// Add ApplicationInsights
builder.AddApplicationInsights();
builder.Services.AddApplicationInsightsTelemetry();
builder.Services.AddSingleton<ITelemetryInitializer, TelemetryInitializer>();
}
The IHttpContextAccessor deals with all that nastyness -- I was surprised to find that this is a singleton rather than scoped.
It turns out that this is not called until late on in the request pipeline which means that the auth middleware has run and therefore the .User is populated.

Extending existing ABP controllers

I am using version 3.3.2 of the ABP Framework. How can I add new methods to an existing controller? I want to extend the IdentityUserController. Following the docs I am creating my own implementation as following:
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IdentityUserController))]
public class MyIdentityUserController : IdentityUserController
{
public MyIdentityUserController(IIdentityUserAppService userAppService) : base(userAppService)
{
}
public override Task<PagedResultDto<IdentityUserDto>> GetListAsync(GetIdentityUsersInput input)
{
return base.GetListAsync(input);
}
[HttpGet]
[Route("my-method")]
public Task<string> MyMethod()
{
return Task.FromResult("Works");
}
}
The overrides actually work but my custom method is not visible in Swagger and when I try to access it with Postman it is not accessible either. Any ideas how I can extend existing controllers? I don't want to create a whole new controller since I have a combination with overrides and new methods. I would like to keep everything together.
First, set IncludeSelf = true — we will use this to determine whether to replace the existing controller with the extended controller, and ASP.NET Core will resolve your controller by class.
Optionally, add [ControllerName("User")] from IdentityUserController since it is not inherited:
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IdentityUserController), IncludeSelf = true)]
[ControllerName("User")]
public class MyIdentityUserController : IdentityUserController
Option 1
Subclass AbpServiceConvention and override RemoveDuplicateControllers to remove the existing controller(s) instead of your extended controller:
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr.IncludeSelf)
{
var existingControllerModels = application.Controllers
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType))
.ToArray();
derivedControllerModels.AddRange(existingControllerModels);
Logger.LogInformation($"Removing the controller{(existingControllerModels.Length > 1 ? "s" : "")} {exposeServicesAttr.ServiceTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(existingControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}");
continue;
}
Full code of subclass:
public class MyAbpServiceConvention : AbpServiceConvention
{
public MyAbpServiceConvention(
IOptions<AbpAspNetCoreMvcOptions> options,
IConventionalRouteBuilder conventionalRouteBuilder)
: base(options, conventionalRouteBuilder)
{
}
protected override void RemoveDuplicateControllers(ApplicationModel application)
{
var derivedControllerModels = new List<ControllerModel>();
foreach (var controllerModel in application.Controllers)
{
if (!controllerModel.ControllerType.IsDefined(typeof(ExposeServicesAttribute), false))
{
continue;
}
if (Options.IgnoredControllersOnModelExclusion.Contains(controllerModel.ControllerType))
{
continue;
}
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr.IncludeSelf)
{
var existingControllerModels = application.Controllers
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType))
.ToArray();
derivedControllerModels.AddRange(existingControllerModels);
Logger.LogInformation($"Removing the controller{(existingControllerModels.Length > 1 ? "s" : "")} {exposeServicesAttr.ServiceTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")} from the application model since {(existingControllerModels.Length > 1 ? "they are" : "it is")} replaced by the controller: {controllerModel.ControllerType.AssemblyQualifiedName}");
continue;
}
var baseControllerTypes = controllerModel.ControllerType
.GetBaseClasses(typeof(Controller), includeObject: false)
.Where(t => !t.IsAbstract)
.ToArray();
if (baseControllerTypes.Length > 0)
{
derivedControllerModels.Add(controllerModel);
Logger.LogInformation($"Removing the controller {controllerModel.ControllerType.AssemblyQualifiedName} from the application model since it replaces the controller(s): {baseControllerTypes.Select(c => c.AssemblyQualifiedName).JoinAsString(", ")}");
}
}
application.Controllers.RemoveAll(derivedControllerModels);
}
}
Option 2
Implement IApplicationModelConvention to add your extended controller to IgnoredControllersOnModelExclusion and remove the existing controller:
public class ExtendedControllerApplicationModelConvention : IApplicationModelConvention
{
private readonly Lazy<IOptions<AbpAspNetCoreMvcOptions>> _lazyOptions;
public ExtendedControllerApplicationModelConvention (IServiceCollection services)
{
_lazyOptions = services.GetRequiredServiceLazy<IOptions<AbpAspNetCoreMvcOptions>>();
}
public void Apply(ApplicationModel application)
{
var controllerModelsToRemove = new List<ControllerModel>();
var ignoredControllersOnModelExclusion = _lazyOptions.Value.Value.IgnoredControllersOnModelExclusion;
foreach (var controllerModel in application.Controllers)
{
var exposeServicesAttr = ReflectionHelper.GetSingleAttributeOrDefault<ExposeServicesAttribute>(controllerModel.ControllerType);
if (exposeServicesAttr != null && exposeServicesAttr.IncludeSelf)
{
ignoredControllersOnModelExclusion.AddIfNotContains(controllerModel.ControllerType);
var existingControllerModels = application.Controllers
.Where(cm => exposeServicesAttr.ServiceTypes.Contains(cm.ControllerType));
controllerModelsToRemove.AddIfNotContains(existingControllerModels);
}
}
application.Controllers.RemoveAll(controllerModelsToRemove);
}
}
In your module, insert ExtendedServiceApplicationModelConvention before AbpServiceConventionWrapper:
public override void ConfigureServices(ServiceConfigurationContext context)
{
// ...
Configure<MvcOptions>(options =>
{
var abpServiceConvention = options.Conventions.OfType<AbpServiceConventionWrapper>().First();
options.Conventions.InsertBefore(abpServiceConvention, new ExtendedControllerApplicationModelConvention (context.Services));
});
}
I created a test project using the same version of ABP v3.3.2 and managed to get this working.
You can override the original methods in a new class that inherits from the original IdentityUserController, but you need to create your own controller to 'add' new methods to it. If you create a new controller that includes the same class attributes as IdentityUserController then it will appear like it has been extended.
[RemoteService(Name = IdentityRemoteServiceConsts.RemoteServiceName)]
[Area("identity")]
[ControllerName("User")]
[Route("api/identity/users")]
[ExposeServices(typeof(MyIdentityUserController))]
public class MyIdentityUserController : AbpController, IApplicationService, IRemoteService
{
[HttpGet("my-method")]
public Task<string> MyMethod()
{
return Task.FromResult("Works");
}
}

Moq how to do a setup when the item could be any of a type but must have a particular property set

I'm trying to setup for a method which will run:
dataAccess.Setup(x => x.GetCars(It.IsAny<CarFilter>()));
Ok, so I'm saying to setup the method GetCars when it is passed any object of type CarFilter.
I want to make this a little more specific.
I want to make it so it will setup when it is passed any object of type CarFilter when the property on CarFilter Color = "Red".
Does anybody know how to write this?
You can put a condition on the match using It.Is(...)
public class Car {
public string Color { get; set; }
}
public interface IFoo {
void DoIt(Car car);
}
[TestMethod]
public void PassesWhenRed() {
var mockFoo = new Mock<IFoo>();
var redCar = new Car { Color = "Red" };
mockFoo.Object.DoIt(redCar);
mockFoo.Verify(mk=>mk.DoIt(It.Is<Car>(car => car.Color == "Red"))); // Passes
}
[TestMethod]
public void FailsWhenNotRed() {
var mockFoo = new Mock<IFoo>();
var blueCar = new Car { Color = "Blue" };
mockFoo.Object.DoIt(blueCar);
mockFoo.Verify(mk=>mk.DoIt(It.Is<Car>(car => car.Color == "Red"))); // Fails
}
Ended up needing:
dataAccess.Setup(x => x.GetCars(It.Is<CarFilter>(z => z.Color == "Red")));

How to map lists with ValueInjector

I am using ASP.NET MVC 3.
Can someone please help me clarify what's happening here:
var person = new PersonRepository().Get();
var personViewModel = new PersonViewModel();
personViewModel.InjectFrom<LoopValueInjection>(person)
.InjectFrom<CountryToLookup>(person);
I have a grid on my Index view. Each row is an instance of a CategoryViewModel. So what I do is to get a list of all the categories and then map each Category to a CategoryViewModel, and then pass this list of CategoryViewModels to the view. Hou would I do a mapping like that?
IEnumerable<Category> categoryList = categoryService.GetAll();
I thought the following would work but it doesn't:
// Mapping
IList<CategoryViewModel> viewModelList = new List<CategoryViewModel>();
viewModelList.InjectFrom(categoryList);
AFAIK value injecter doesn't support automatic collection mapping like AutoMapper but you could use a simple LINQ expression and operate on each element:
IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
.Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
.ToList();
//source list
IEnumerable<string> items = new string[] { "1", "2" };
// target list
List<int> converted = new List<int>();
// inject all
converted.InjectFrom(items);
And the extension method:
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, IEnumerable<TFrom> from) where TTo : new()
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
return to;
}
ICollection<T> is the interface that got least features but a Add method.
Update
An example using more proper models:
var persons = new PersonRepository().GetAll();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(persons);
Update - Inject from different sources
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, params IEnumerable<TFrom>[] sources) where TTo : new()
{
foreach (var from in sources)
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
}
return to;
}
Usage:
var activeUsers = new PersonRepository().GetActive();
var lockedUsers = new PersonRepository().GetLocked();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(activeUsers, lockedUsers);
Use this function definition
public static object InjectCompleteFrom(this object target, object source)
{
if (target.GetType().IsGenericType &&
target.GetType().GetGenericTypeDefinition() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)) &&
source.GetType().IsGenericType &&
source.GetType().GetGenericTypeDefinition() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)))
{
var t = target.GetType().GetGenericArguments()[0];
var tlist = typeof(List<>).MakeGenericType(t);
var addMethod = tlist.GetMethod("Add");
foreach (var sourceItem in source as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(sourceItem);
addMethod.Invoke(target, new[] { e });
}
return target;
}
else
{
return target.InjectFrom(source);
}
}
For those like me who prefer shortest notations possible
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, ICollection<TOrig> source) where TTarget : new()
{
source.Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e));
return target;
}
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, params ICollection<TOrig>[] sources) where TTarget : new()
{
sources.ToList().ForEach(s => s.ToList().Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e)));
return target;
}
Create a generic list mapper:
public class ValueMapper
{
public static TResult Map<TResult>(object item) where TResult : class
{
return item == null ? null : Mapper.Map<TResult>(item);
}
public static IEnumerable<TResult> MapList<TResult>(IEnumerable<object> items) where TResult : class
{
return items?.Select(i => Mapper.Map<TResult>(i));
}
}
Now you can reference the ValueMapper class wherever you want, and call both Map and MapList
var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);

Moq SetupProperty not working

Can someone tell me whey the following code blows when the moq.SetupProperty fails in the code below:
[TestMethod]
public void SimulatorService_Returns_HighScores()
{
IScoreService scoreService = new ScoreService(MockScoreRepository.GetMockScoreRepository());
Assert.IsNotNull(scoreService);
var highScores = scoreService.GetHighScores();
Assert.IsTrue(highScores.Count > 0);
}
public static class MockScoreRepository
{
public static ScoreEntry GetMockScoreEntry(int seed)
{
var moq = new Mock<ScoreEntry>();
moq.SetupProperty(s => s.UserID, seed);
moq.SetupProperty(s => s.Score, 10 * seed);
moq.SetupProperty(s => s.EntryDate, DateTime.Now);
return moq.Object;
}
public static IScoreRepository GetMockScoreRepository()
{
var scores = new List<ScoreEntry>();
for (var i = 0; i < 20; i++)
{
scores.Add(GetMockScoreEntry(i));
}
var repository = new Mock<IScoreRepository>();
repository.Setup(r => r.GetScores()).Returns(scores.AsQueryable());
return repository.Object;
}
}
The first thing I'd check is to make sure that the properties of ScoreEntry that you're setting have accessible setters. In other words, make sure your setters have public access or that you at least have a setter for each property you're attempting to set through moq.

Resources