.net core Options configuration array override - asp.net-core

I have a class with one List<string> variable having default values.
public class MyOptions{
public List<string> Settings {get; set;} = new List<string>(){"Controls","Menus"};
}
Then I register it in ConfigureServices method like
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
I want to be able to change the value of Settings collection without changing the code.
In my appsettings.json, I have tried the following
{
"MyOptions":{
"Settings:0":"ReplacedSettings"
}
}
to replace "Controls" with "ReplacedSettings", but it doesn't work and I now get Settings with three values instead ["Controls","Menus","ReplacedSettings"]
whereas I want ["ReplacedSettings","Menus"].
Is this supported? Or is there any similar data structure I can with Option pattern that allows defaults values to be overridden from appsettings.json.
Thanks.

This behavior is by design because when binding to collections in options, the values will be appended. The reason why you cannot overwrite the default values from your MyOptions options model is that options and configuration are actually two distinct concepts which can work together but don’t have to. I go into more detail in an answer to a related question but basically handling the configuration and binding the configuration to options objects are two separate things.
When you just look at your configuration, then there’s the following value:
MyOptions:Settings:0 => "ReplacedSettings"
The configuration system does not know about your "Controls" and "Menus" values. Those only exist in the options type you later bind to. So because there is nothing else in the configuration, there is nothing you can replace here. And when the binder then uses the configuration to set up the options types, it will just append that single value "ReplacedSettings" to the list.
If you want to make sure that you can replace values, then you will have to declare those settings in configuration:
MyOptions:Settings:0 => "Controls"
MyOptions:Settings:1 => "Menus"
If you now apply the configurations with your "ReplacedSettings", then it will properly replace the one matching key and leave the other:
MyOptions:Settings:0 => "ReplacedSettings"
MyOptions:Settings:1 => "Menus"
The common solution for this is to use the appsettings.json for default values. That way, you can overwrite those with an environment-specific appsettings.<env>.json or some other source (e.g. environment variables) and apply your overrides. Of course, you shouldn’t specify the defaults in your options type then.

Related

How set targetEntity from config file in symfony?

I am building bundle and i wat to use it in another application. I created Entity with user field and i want make config option with user class that must implement AdvancedUserInterface.
In Entity i use this mapping:
/**
* #ORM\ManyToOne(targetEntity="Symfony\Component\Security\Core\User\AdvancedUserInterface")
*/
private $author;
I know i can replace this Interface by adding to config.yml
//config.yml
doctrine:
[...]
orm:
[...]
resolve_target_entities:
Symfony\Component\Security\Core\User\AdvancedUserInterface: Draconicka\FosUserBundle\Entity\FosUser
But i think this is not good solution. I want put this class to bundle section in config. For examlpe
//config.yml
[...]
nattle_demo:
user_class: Draconicka\FosUserBundle\Entity\FosUser
Is this possible? Or better solution is override this bundle and add this field in each application?
I always use resolve taget entities - its easies way to fill targetEntity attr in your bundle.
Sometimes I use setting class in config if I need put class full name (with namespace) in other services.
For example, you want check user data in authListener. AuthListener has method supportClass ($class) { return $class == '\Your\Configured\Class' }

How to get all FAL File Objects which are referenced?

I'm trying to make a extbase extension for TYPO3 to get alle file objects with mimetype image/... which referenced by any content, plugin or fluid in typo3.
But i don't know which is the best way to get these data. How should i create a model in my extension and how should i create the correct repository?
If i create a custom query i'm not sure how to return a complete FAL Object which contains any data (like metadata) etc.
hope someone could help me to find the right way, and maybe has a example or something.
thanks a lot
You could do it like this, details are at the bottom:
Get all file references.
Go through them, retrieve the referenced file for each of them and retain only the ones where the field mime_type starts with image/.
There are two things you probably need to watch out for:
The field mime_type needs to be up to date. Check the FAL scheduler indexing task for that.
Performance. Depending on the number of files you have, it could be much faster to do this with a custom SQL statement which makes use of a JOIN. But you should only do that if performance is a problem.
How to get all file references:
First, build your own empty file reference class:
namespace Vendor/Extkey/Domain/Model;
class FileReference extends \TYPO3\CMS\Extbase\Domain\Model\FileReference {}
Make sure to configure it in your TypoScript to be serialized to the table sys_file_reference:
config.tx_extbase.persistence {
classes {
Vendor\Extkey\Domain\Model\FileReference {
mapping {
tableName = sys_file_reference
}
}
}
}
Add a repository for the references:
namespace Vendor/Extkey/Domain/Repository;
class FileReferenceRepository extends \TYPO3\CMS\Extbase\Persistence\Repository {
public function initializeObject() {
/** #var \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface */
$defaultQuerySettings = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
$defaultQuerySettings->setRespectStoragePage(FALSE);
$this->setDefaultQuerySettings($defaultQuerySettings);
}
}
The reference class can be empty, but the repository needs it to be there in order to work correctly. Make sure you add the default query settings to ignore the page id, so you get all non-hidden and non-deleted file references by calling $fileReferenceRepository->findAll().
How to check the MIME-type of each referenced file:
You can get the MIME-type of each reference by calling
$fileReference->getOriginalResource()->getMimeType()
This should automatically fetch the original file from storage and read its MIME-type.

Web API 2 Routing based on Parameter's Value

Is it possible to setup Web Api 2 route based on a parameter's value in the query string.
I have the following requirement:
/api/controller?data=param.data1
should go to controller's action 1
/api/controller?data=param.data2
should go to controller's action 2
any other value of data must go to action 3.
I know there's an option to set a constraint with a regex, but the examples I've found are for generic scenarios and not as specific as mine.
This is what I've tried
config.Routes.MapHttpRoute(
name: "test",
routeTemplate: "api/Hub/{data2}",
defaults: new { action = "Test" },
constraints: new { data2 = #"^(param\.data2)$" }
);
Is there a way to do it? Maybe there's a better way?
Important to note, I cannot change the URI of the service. It must have ?data=[value]
This is a fallback for a legacy system :(
You can use Attribute Routing, new in Web API 2.
Let's say you have the following actions, where the data param is, let's say, a string:
public Stuff GetStuffForData1(string data) { ... }
public Stuff GetStuffForData2(string data) { ... }
public Stuff GetStuffForData(string data) { ... }
Since you mentioned regex, you can specify route constraints for each of the above actions using a regex like the one you mentioned in your question1, for example:
[Route("controller/{data:regex(#"^(param\.data1)$")]
public Stuff GetStuffForData1(string data) { ... }
[Route("controller/{data:regex(#"^(param\.data2)$")]
public Stuff GetStuffForData2(string data) { ... }
// No need for a route constraint for other data params.
public Stuff GetStuffForData(string data) { ... }
The general syntax is {parameterName:constraint(params)} (params is optional and is not used for all constraints). In the above example, the first route will only be selected if the data segment of the URI matches the data1 regex. Similarly, the second route will be selected if the data segment of the URI matches the data2 regex. Otherwise, the last route will be chosen.
In general, the total ordering is determined as follows:
Compare the RouteOrder property of the route attribute. Lower values are evaluated first. The default order value is zero.
Look at each URI segment in the route template. For each segment, order as follows:
Literal segments.
Route parameters with constraints.
Route parameters without constraints.
Wildcard parameter segments with constraints.
Wildcard parameter segments without constraints.
In the case of a tie, routes are ordered by a case-insensitive ordinal string comparison (OrdinalIgnoreCase) of the route template.
You can even create your own custom route constraints by implementing the IHttpRouteConstraint interface and registering it in the Register method of your WebApiConfig class, assuming you're hosting on IIS, or in the Configuration method of your Startup class if self-hosting using OWIN.
Note I haven't personally tried any of the above, but it should all work; at the very least it should give you some ideas. For more details, including very nice examples, you should start by taking a look at the following article (which I shamelessly used extensively in my answer):
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#constraints
1 I'm really not an expert on writing regexes, so unfortunately I can't advise you on the specific ones you'll need.

Do Ninject MetaData constrained Get()'s apply to child resolutions?

I am using ninject.extensions.conventions to bind all implementations in a given assembly and tag them with the assembly name as the binding's metadata. I can pull these items back out using a Get and supplying a func as standard.
What I would like to know is does this func apply to all children resolved also? My worry is that while my logic works now, if I add more bindings that satisfy any children more than once ninject will throw.
Code Sample:
_kernel.Bind(binder => binder.From(new[] { pathToAssembly })
.SelectAllClasses()
.BindAllInterfaces()
.Configure(binding =>
binding.WithMetadata("context",
assemblyName)));
_kernel.Get<IRootDependency>
(metadata => metadata.Get<IRootDependency>("context") ==
assemblyName);
// Bound from convention above.
RootDependencyBase: IRootDependency
{
Public RootDependencyBase(IChildDependency Child) {};
}
// Bound using the convention above with the same MetaData Tag.
ChildDependencyFromSameAssembly : IChildDependency {}
// Bound using a differing convention and does not have the same MetaData tag.
ChildDependencyFromOtherAssembly : IChildDependency {}
Based on the above sample I know that IRootDependency will be resolved to the correct binding based on the metadata filter.
What I am looking to find out is is the following true.
This filter does not feed down the dependency chain. IChildDependency will throw an exception because although the binding specified MetaData it is not queried.
Constraints are applyed to the root resolution only. In case you have multiple assemblies containing a child dependency you will get an exception.
To get it work you have to add a condition to the bindings. E.g like this:
.When(r => r.ParentContext == null || r.ParentContext.Binding.Metadata.Get<string>("context", null) == assemblyName)
Or get the root request (request.ParentRequest until parentRequest is null) and apply the constraint
.When(r => GetRootRequest(r).Constraint(r))
Yes, in case if your examples will has another implementation of IChildDependency in same assembly as ChildDependencyFromSameAssembly you'll get exception ActivationException with message:
Error activating IDependency
More than one matching bindings are available.
You have to provide exact criteria to Ninject to find what implementation of IChildDependency, from same assembly, will suits better

Named singleton instance in StructureMap (Multiple nHibernate session factories)

I have a scenario where I have two Nhibernate SessionFactorys I need to register an use with StructureMap. Only Foo needs mySessionFactory sessions.
Like this:
For<ISessionFactory>().Singleton().Use(NHibernateConfiguration.GetDefaultSessionFactory());
For<ISession>().HybridHttpOrThreadLocalScoped().Use(x => x.GetInstance<ISessionFactory>().OpenSession());
For<ISessionFactory>().Singleton().Use(AnotherNHibernateConfiguration.GetDefaultSessionFactory).Named("mySessionFactory");
For<ISession>().HybridHttpOrThreadLocalScoped().Use(x => x.GetInstance<ISessionFactory>("mySessionFactory").OpenSession()).Named("mySession");
For<Foo>()
.Use<Foo>()
.Ctor<ISession>("session").Is(x => x.TheInstanceNamed("mySession"));
The problem is that mySessionFactory is now used everywhere when I only wanted to to be used in Foo and everywhere else should use my unnamed instance.
What I'm I doing wrong?
On both your named instances, change Use to Add. Use sets that instance as the default as well as adding it as an instance. You could also reverse the order of your config (the last instance of a type added with Use will become the default), but using the Add method is much more explicit.