I am trying to use Autofac specifying 2 metadata items. It seems to add these fine, however, I am not too clear on how to retrieve the service by specifying both of these items. I have metadata items for an Assembly Name and a Version. The registration snippet is as follows:
builder.RegisterAssemblyTypes(assembly).Where(
t => t.GetInterfaces().Any(i =>
plugin.Interface == i.Name)).As<IPlugin>()
.WithMetadata<IMetaDataName>(m => m.For(pn => pn.Name, name)
.WithMetadata<IMetaDataVersion>(m => m.For(pn => pn.Version, version)
The only documentation and examples I can find specify that I can use this syntax:
IEnumerable<Lazy<IPlugin, IMetaDataName>>
in the costructor of the class or in a class that resolves the service I am after. What I am after is a mechanism to let me do the following:
Get the service that has the MetaDataName == "Service1" AND MetaDataVersion == "1.4"
Currently I have concatenated the two together for 1 metadata type which works but feels clunky. Is there are better approach to this?
Thanks
Craig.
Rather than concatenate both interfaces into one, you can just create a separate concatenated interface for the importer.
The interface name used at registration time doesn't have to match the one used as a dependency - the property names are the only shared information at this point.
The metadata view type used in a dependency is really specific to the class receiving the dependency rather than the type providing it, so these can vary independently.
Know this isn't exactly the answer you were looking for but hope it helps.
Related
Can you apply the SuppressImplicitRequiredAttributeForNonNullableReferenceTypes option to only a particular path pattern, e.g., .../v3/...?
We've gone through the trouble of enabling nullable contexts throughout our code, and ensuring all our parameters have the correct nullability. Now we want to utilize that for API validation. But since we don't want break any of our exising API behavior, we only want to apply the implicit Required attribute behvaior on paths for particular API versions. I.e., v2 would NOT have the validation, but v3+ would.
Is there any way to do this?
I can show you the way, but you have to walk through it and complete the implementation!
Ok, to see how SuppressImplicitRequiredAttributeForNonNullableReferenceTypes works, let's check the source code first!
Here's when the parameter is being used in DataAnnotationsMetadataProvider class.
https://github.com/dotnet/aspnetcore/blob/3ea008c80d5cc63de7f90ddfd6823b7b006251ff/src/Mvc/Mvc.DataAnnotations/src/DataAnnotationsMetadataProvider.cs#L343
Now let's see where DataAnnotationsMetadataProvider is used! It is added as a ModelMetadataDetailsProviders to MvcOptions.ModelMetadataDetailsProviders.
https://github.com/dotnet/aspnetcore/blob/3ea008c80d5cc63de7f90ddfd6823b7b006251ff/src/Mvc/Mvc.DataAnnotations/src/DependencyInjection/MvcDataAnnotationsMvcOptionsSetup.cs#L54
So you would need to create your own CustomValidationMetadataProvider and add it to MvcOptions.
builder.Services.AddControllers(op =>
{
op.ModelMetadataDetailsProviders.Add(new CustomValidationMetadataProvider());
});
public class CustomValidationMetadataProvider : IValidationMetadataProvider
{
public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
// context.ValidationMetadata.IsRequired = ???
}
}
Here you can have your own logic to set context.ValidationMetadata.IsRequired. Unfortunately I'm not sure if you can access the request path here, but you do have access to attributes on the model. So theoretically you could add an attribute to the models on your v3.
There are a few things that I could suggest here:
If you on C# 8 you can try nullable properties/fields for places where you want to allow nullable values. (recommended simplest one)
You can use custom Parameter Binding (non-trivial approach). You can find more details here https://www.strathweb.com/2013/04/asp-net-web-api-parameter-binding-part-1-understanding-binding-from-uri/
You can disable standard model validation and provide your own (where you should be able to specify path) and again non-trivial approach.
I'm currently working on an API and the library I'm using needs to initialize most of its components. It is initialize by using .include.
A sample call would be: context.Load(context.Projects, proj => proj.Include(p => p.Name))
As much as possible, I don't want to include a lot of fields since it will take longer to execute the query. So one of my idea was to use GetType().GetProperty(), which looks like this:
context.Load(context.Projects, proj => proj.Include(
p => p.Name,
p => p.GetType().GetProperty(customFieldName)
));
My assumption was that when I do this I will get the property, but it seems it creates a different object. Is there any way I can do this?
Edit: The idea is that the customFieldName is given by the user, so for example the user gave Id I'm hoping to get the equivalence too p.Id in the Include statement. So I can return the value of that object to user
Entity framework also allows you to include properties based on their name as a string. In this way you can decide the property to include at runtime. So your code should now look something like this.
context.Load(context.Projects, proj => proj.Include("Name", customFieldName));
In case you are looking into nested properties (for ex: Name is a class with properties FirstName and LastName), you could use dot to specify the exact property to include ("Name.FirstName").
I'm working with .net 4.5 and MVC4.
I implemented Unity IoC as described in the following post: http://kennytordeur.blogspot.com/2011/05/aspnet-mvc-3-and-unity-using.html
But I would like to be able to have "register" my repository types using an external XML or within web.config. Is that possible?, samples would be greatly appreciated.
thanks
Unless there is a really strong reason to, you should register as much as possible in code. XML configuration is much more error prone, verbose and can become a maintenance nightmare very quickly. Instead of registering (all) your repository types in XML (which is possible with Unity), just put the assembly name containing that contains the repository types in the config and register them dynamically in code. This saves you from having to change the configuration every time you add a new repository implementation.
Here is an example.
In your configuration file, add a new appSetting with the name of the assembly:
<appSettings>
<add key="RepositoryAssembly" value="AssemblyName" />
</appSettings>
In your composition root, you can do the following:
var assembly = Assembly.LoadFrom(
ConfigurationManager.AppSettings["RepositoryAssembly"]);
// Unity misses a batch-registration feature, so you'll have to
// do this by hand.
var repositoryRegistrations =
from type in assembly.GetExportedTypes()
where !type.IsAbstract
where !type.IsGenericTypeDefinition
let repositoryInterface = (
from _interface in type.GetInterfaces()
where _interface.IsGenericType
where typeof(IRepository<>).IsAssignable(
_interface.GetGenericTypeDefinition())
select _interface)
.SingleOrDefault()
where repositoryInterface != null
select new
{
service = repositoryInterface,
implemention = type
};
foreach (var reg in repositoryRegistrations)
{
container.RegisterType(reg.service, reg.implementation);
}
The LINQ query has a lot of subtle defects (for instance, it lacks checks for generic type constraints), but it will work for the common scenarios. If you work with generic type constraints, you should definitely switch to a framework that has support for this, because this is something that's really hard to get right.
Is it possible to remove a mapping at run time? I can add one easily enough with:
ObjectFactory.Configure(ce =>
ce.ForRequestedType<IDateAdjuster>().
TheDefaultIsConcreteType<DateAdjusterForTest>());
and I can check to see if a mapping exists with
ObjectFactory.Model.HasDefaultImplementationFor<IDateAdjuster>()
but I can't seem to remove a default instance type. I tried this
var config = ObjectFactory.Model.PluginTypes
.FirstOrDefault(pt => pt.PluginType == typeof(IDateAdjuster));
if (config != null) {config.Default = null;}
But this isn't working.
What the interface does is hide away the DateTime.Now and DateTime.Today properties so that during testing, in particular integration and user acceptance, the system can be configured to shift the current date and time as it sees it.
What I was trying to do was check to see if there was a default implementation of the interface, if there was then I'd create and call it, if not then I'd just use DateTime.Now and DateTime.Today
As it seems that I can't tell structure map to remove the default type, so I've now added a new default implementation of the interface and extended the interface to have a property to indicate if it's active. The default implementation indicates that the interface is not active. So now I can always get an instance and then check if I want to use it.
The system has quite a bit of date and time specific behavior and so the client needs to be able to test how the system behaves at different dates and times. They login to the admin section and then set the date and time to the appropriate values, then I switch the default implementation to the test implementation that makes the appropriate adjustment.
As it is only the implementation of the interface that changes I just switch which implementation I want to use when the client admin sets it, and then reset it when they are done
Changing the IoC configuration at runtime is a bit scary IMO. Perhaps using a factory would be cleaner solution. For instance:
public interface IOptionalFactory
{
IOptional GetInstance(IPrinciple currentUser);
}
You can inject the IOptionalFactory instead of an IOptional interface and let the factory return different instances based on the supplied user information.
So if the switch is controlled by something the user changes, I am guessing its in the database or maybe session? If this is the case you can use the conditional construction feature of StructureMap to do this:
var container = new Container(x =>
{
x.InstanceOf<IDateAdjuster>().Is.Conditional(o =>
{
o.If(c => c.GetInstance<IUserSettings>()
.UseTestDateAdjuster).ThenIt.Is.OfConcreteType<DateAdjusterForTest>();
o.TheDefault.Is.OfConcreteType<DateAdjuster>();
});
});
IUserSettings could be anything that is registered in your contain that will tell you if the user has set the date and time in the admin section.
See this blog for more details: http://codebetter.com/jeremymiller/2009/01/19/conditional-object-construction-in-structuremap-i-e-fun-with-lambdas/
I've been working on an API that encapsulates another, trickier-to-use API. The goal is for my API to not require the user to touch any of the old API by 1) not requiring any parameters of classes in the old API and 2) not returning any instances of classes in the old API. Is there a program, perhaps a Visual Studio plugin, that can analyze my C# solution and give me a list of all the return types from publicly accessible methods in publicly accessible classes, as well as all the parameter types in such methods? Otherwise it seems like I'll have to manually go through all my classes and see if any of the old API is exposed to the user.
Edit: Since I've been using MSTest for unit testing my API anyway, I added another unit test to use reflection and Fail if any parts of the old API are exposed. However, I'm stuck with a reflection problem. I have using OldAPI in the unit test class and then I use
AppDomain.CurrentDomain.GetAssemblies().SelectMany(
assembly => assembly.GetTypes()
)
to get a list of the types in all the assemblies currently loaded. I then iterate over those in hopes of paring down the list of types to only those in the namespace OldAPI. The problem is that the namespace OldAPI does not show up. I see namespaces like Microsoft.VisualStudio.TestTools, System.Reflection, and others that are included via using statements in the test class, but no "OldAPI". Could this be because of COM stuff with the old API, so AppDomain.CurrentDomain.GetAssemblies() doesn't include the assembly even though it's included via a using statement in the class?
Solution: I got the necessary assembly by arbitrarily choosing one class I know is in OldAPI and doing the following, thanks to SLaks' comment:
Func<Type, bool> isBad = t => t.Assembly == typeof(OldAPI.SomeClass).Assembly;
Here's a snippet of my unit test for checking if any of my API's classes use any of OldAPI's classes, thanks to SLaks' answer:
MethodInfo[] badMethods = methods.Where(
m => (
isBad(m.ReturnType) ||
m.GetParameters().Any(p => isBad(p.ParameterType))
) && !isBad(m.DeclaringType)
).ToArray();
string[] badMethodNames = badMethods.Select(
m => m.DeclaringType.Name + "." + m.Name
).Distinct().ToArray();
Assert.AreEqual(0, badMethodNames.Length, "Some methods in " +
monitoredNamespaces + " projects expose OldAPI: " +
string.Join(", ", badMethodNames));
You can use LINQ, like this:
Func<Type, bool> isBad = t => t.Assembly == badAssembly;
var types = yourAssembly.GetTypes();
var methods = types.SelectMany(t => t.GetMethods()).ToArray();
var badMethods = methods.Where(m => isBad(m.ReturnType)
|| m.GetParameters().Any(p => isBad(p.ParameterType);
var properties = types.SelectMany(t => t.GetProperties()).ToArray();
var badProperties = properties.Where(p => isBad(p.PropertyType));
This would be easiest to do in LINQPad.
Note that this doesn't traverse generic types, so it'll ignore a List<BadType>.
You should probably make isBad recursive. (In which case you should turn it into a regular function)
I'm not aware of an existing tool for this, but that doesn't mean you have to do it manually -- you can very easily write your own tool to do this using Reflection. Basically you'll just need to iterate over Assembly.GetExportedTypes(); for each type, call Type.GetMethods() and Type.GetProperties() and iterate over the results; and dumping the return and parameter types for each method or property that is public.
Note that such a handwritten tool would need to run over your compiled assembly, not your C# source code. You can do something similar to source code, but it depends on the Visual Studio code model which is rather harder to use and probably not worth the effort for a one-off like this!