Customizing nopCommerce - c#

We are developing an nopCommerce based application. Our login page needs to be minimalistic and would need only an email id, password entry fields and a Login button.
Could you point me to best practices for achieving the above objective ?
Do I modify the corresponding pages found in \Presentation\Nop.Web\Views\Customer\ & controllers in \Presentation\Nop.Web\Controllers\
Or
Is there a better way of doing this and organizing all the modified files in one place/folder so that upgrading to future versions of nopCommerce will not be difficult ?
The requirement is to ensure that all the changes made to the project(views/controllers etc) are in one folder so that they are not overwritten when we upgrade to a newer version of nopCommerce.
I read somewhere that you can copy stuff you need to change (Login.chtml, CustomerController) to Themes/DefaultClean and then make your changes in this folder. I dont remember where i read it.
I feel doing so will make it that much easier to maintain our codebase because all your custom code is in one place/folder/sub folders
Is this a best practise? And is there a disadvantage to this method of doing things?

The best way to modify your nopCommerce project without changing anything in the core code would be to use the plugin functionality which is described here (assuming you're using the newest version 4.40).
To change the login page you would then need to create your modified version as a .cshtml file in your plugin. You then need to set this file as Content and set the Copy to Output Directory property to Copy if Newer or Copy Always.
You also need to implement the IViewLocationExpander interface so that the Razor Engine knows that it should use your custom Login Page. The implementation should look something like this:
public class MyViewLocationExpander : IViewLocationExpander
{
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
if(context.ViewName == "Login")
{
viewLocations = new[] { "PathToCustomLoginPage" }.Concat(viewLocations);
}
return viewLocations;
}
public void PopulateValues(ViewLocationExpanderContext context)
{
return;
}
}
After that you also need to register your ViewExpander by implementing the INopStartup interface. The implementation would look something like this:
public class MyStartup : INopStartup
{
public int Order => int.MaxValue;
public void Configure(IApplicationBuilder application)
{
}
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new MyViewLocationExpander());
});
}
}

Related

How to set the dependency injection when it depends of the selection of user at startup in a WPF application?

When I see some examples about how to use dependency injection in a WPF application, I have seen that this is configure in the app.xaml.cs file, that it is execute before any window is showed.
But in my case, some dependencies depends on the selection of the user in the first windows.
This is the case. I want to have an application that allow to upload and download files from two different clouds. The user selects from a dropbox which cloud he wants to use. Once it is selected, the whole application will use the selected cloud. If the user wants to use the other cloud, he has to close and run the application again (it is a bit silly behaviour, but it is to simplify and I think it expose the doubt better).
How the user need to select the cloud, I can't configure the dependency in the app file.
My code is this:
interface ICloudService
{
UploadFile(string pathFileToUpload);
DownloadFile(string pathToSaveFile);
}
class CloudOneService() : ICloudService
{
//Implementation
}
class CloudTwoService() : ICloudService
{
//Implementation
}
In the app.xaml.cs file, I should to configure the dependencies, something like that:
public partial class App : Application
{
public App()
{
host = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddScoped<ICloudService, CloudOneService>();
}).Build();
}
}
But this code first it will use always CloudOneService and second, it is run before the user can select the cloud.
So I am not sure how could I configure the dependency injection when it depends on the selection of the user.
How could I do it?
Thanks.
So, as I see it, your application has two states:
where the user did not yet select something and
after the selection happened
Now, the question is: Do you need the interface to be available in state 1? If yes, then you should provide "something" there. If no, then you can easily resolve it when going into state 2, e.g. by using a factory class, like you suggested.
enum CloudServiceType
{
One,
Two
}
interface ICloudServiceFactory
{
ICloudService GetService(CloudServiceType selectedCloud);
}
If you need to have an ICloudService ready before the selection, you could either inject a "default" one using regular DI or just let the factory provide a default one with a GetDefault() method.

Where does custom IRouteConstraint get discovered by ASP.NET Core

I'm digging the source code to see how asp.net core discovery custom IRouteConstraint.
We know that when we define a custom IRouteConstraint, we add it to RouteOptions as
public void ConfigureServices(IServiceCollection services)
{
services.Configure<RouteOptions>(opts => {
opts.ConstraintMap.Add("countryName", typeof(CountryRouteConstraint));
});
}
public class CountryRouteConstraint: IRouteConstraint
{
public bool Match(...) { ... }
}
where IOptions<RouteOptions> is registered.
So I check the source code of
EndpointRoutingMiddleware (https://source.dot.net/#Microsoft.AspNetCore.Routing/EndpointRoutingMiddleware.cs,e91e5febd7b6da29)
DfaMatcher
(https://source.dot.net/#Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs,0b08e610bec2cfbc)
and so on, I didn't find any part of the source code that tries to read from RouteOptions to discovery custom IRouteConstraint.
This is the place I think most likely https://source.dot.net/#Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs,197
but still doesn't find anything.
Can anybody show me the section of the source code that ASP.NET Core read from RouteOptions to discovery custom IRouteConstraint?
Navigate to that ConstraintMap property of RouteOptions, to which you are adding your CountryRouteConstraint constraint.
Look for its references.
The ones of most interest for you are
DefaultInlineConstraintResolver
DefaultParameterPolicyFactory
Both make use of the ParameterPolicyActivator for the instantation of the constraints, passing in that ConstraintMap.
E.g. DefaultParameterPolicyFactory shows below
var parameterPolicy = ParameterPolicyActivator.ResolveParameterPolicy<IParameterPolicy>(
_options.ConstraintMap,
_serviceProvider,
inlineText,
out var parameterPolicyKey);

.NET Core data protection - where is it used?

I am using .NET Core 3.1. I have the following code snippet in Startup.cs inside ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddDataProtection()
.SetApplicationName("MyApplication")
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine("AppData", "Keys")));
// ...
}
As far as I understand, this is only needed if I want to protect some input with IDataProtectionProvider, like so:
public class MyClass
{
readonly IDataProtectionProvider _rootProvider;
public MyClass(IDataProtectionProvider rootProvider)
{
_rootProvider = rootProvider;
}
public void Test()
{
IDataProtector protector = provider.CreateProtector("Test123");
string protectedPayload = protector.Protect("Hello world");
Console.WriteLine($"Protect returned: {protectedPayload}");
}
}
However, we are not using this functionality aynwhere in our application. Is it safe to remove AddDataProtection from ConfigureServices? Does any part of .NET Core application (TempData, AntiForgery tokens, ...) use it behind the scenes (so that Visual Studio doesn't find string IDataProtectionProvider)?
According to this document, here's a sentence said:
It cannot directly be used to protect or unprotect data. Instead, the
consumer must get a reference to an IDataProtector by calling
IDataProtectionProvider.CreateProtector(purpose)
hence based on this saying, it seems that data protection doesn't work as _rootProvider didn't be called in some place. And your another misgiving is some default setting or effect may work in some other places, you may refer to this document to see the common usage of data protection api.
And in my opinion, it's really hard to say it have no influence in your project as if by any chance we ignore some thing, that may lead to something unexpected. So if your app runs well now, why not just leave it there.

IOptions Injection

It seems to me that it's a bad idea to have a domain service require an instance of IOptions<T> to pass it configuration. Now I've got to pull additional (unnecessary?) dependencies into the library. I've seen lots of examples of injecting IOptions all over the web, but I fail to see the added benefit of it.
Why not just inject that actual POCO into the service?
services.AddTransient<IConnectionResolver>(x =>
{
var appSettings = x.GetService<IOptions<AppSettings>>();
return new ConnectionResolver(appSettings.Value);
});
Or even use this mechanism:
AppSettings appSettings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(appSettings);
services.AddTransient<IConnectionResolver>(x =>
{
return new ConnectionResolver(appSettings.SomeValue);
});
Usage of the settings:
public class MyConnectionResolver
{
// Why this?
public MyConnectionResolver(IOptions<AppSettings> appSettings)
{
...
}
// Why not this?
public MyConnectionResolver(AppSettings appSettings)
{
...
}
// Or this
public MyConnectionResolver(IAppSettings appSettings)
{
...
}
}
Why the additional dependencies? What does IOptions buy me instead of the old school way of injecting stuff?
Technically nothing prevents you from registering your POCO classes with ASP.NET Core's Dependency Injection or create a wrapper class and return the IOption<T>.Value from it.
But you will lose the advanced features of the Options package, namely to get them updated automatically when the source changes as you can see in the source here.
As you can see in that code example, if you register your options via services.Configure<AppSettings>(Configuration.GetSection("AppSettings")); it will read and bind the settings from appsettings.json into the model and additionally track it for changes. When appsettings.json is edited, and will rebind the model with the new values as seen here.
Of course you need to decide for yourself, if you want to leak a bit of infrastructure into your domain or pass on the extra features offered by the Microsoft.Extensions.Options package. It's a pretty small package which is not tied to ASP.NET Core, so it can be used independent of it.
The Microsoft.Extensions.Options package is small enough that it only contains abstractions and the concrete services.Configure overload which for IConfiguration (which is closer tied to how the configuration is obtained, command line, json, environment, azure key vault, etc.) is a separate package.
So all in all, its dependencies on "infrastructure" is pretty limited.
In order to avoid constructors pollution of IOptions<>:
With this two simple lines in startup.cs inside ConfigureServices you can inject the IOptions value like:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddScoped(cfg => cfg.GetService<IOptions<AppSettings>>().Value);
}
And then use with:
public MyService(AppSettings appSettings)
{
...
}
credit
While using IOption is the official way of doing things, I just can't seem to move past the fact that our external libraries shouldn't need to know anything about the DI container or the way it is implemented. IOption seems to violate this concept since we are now telling our class library something about the way the DI container will be injecting settings - we should just be injecting a POCO or interface defined by that class.
This annoyed me badly enough that I've written a utility to inject a POCO into my class library populated with values from an appSettings.json section. Add the following class to your application project:
public static class ConfigurationHelper
{
public static T GetObjectFromConfigSection<T>(
this IConfigurationRoot configurationRoot,
string configSection) where T : new()
{
var result = new T();
foreach (var propInfo in typeof(T).GetProperties())
{
var propertyType = propInfo.PropertyType;
if (propInfo?.CanWrite ?? false)
{
var value = Convert.ChangeType(configurationRoot.GetValue<string>($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
propInfo.SetValue(result, value, null);
}
}
return result;
}
}
There's probably some enhancements that could be made, but it worked well when I tested it with simple string and integer values. Here's an example of where I used this in the application project's Startup.cs -> ConfigureServices method for a settings class named DataStoreConfiguration and an appSettings.json section by the same name:
services.AddSingleton<DataStoreConfiguration>((_) =>
Configuration.GetObjectFromConfigSection<DataStoreConfiguration>("DataStoreConfiguration"));
The appSettings.json config looked something like the following:
{
"DataStoreConfiguration": {
"ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
"MeaningOfLifeInt" : "42"
},
"AnotherSection" : {
"Prop1" : "etc."
}
}
The DataStoreConfiguration class was defined in my library project and looked like the following:
namespace MyLibrary.DataAccessors
{
public class DataStoreConfiguration
{
public string ConnectionString { get; set; }
public int MeaningOfLifeInt { get; set; }
}
}
With this application and libraries configuration, I was able to inject a concrete instance of DataStoreConfiguration directly into my library using constructor injection without the IOption wrapper:
using System.Data.SqlClient;
namespace MyLibrary.DataAccessors
{
public class DatabaseConnectionFactory : IDatabaseConnectionFactory
{
private readonly DataStoreConfiguration dataStoreConfiguration;
public DatabaseConnectionFactory(
DataStoreConfiguration dataStoreConfiguration)
{
// Here we inject a concrete instance of DataStoreConfiguration
// without the `IOption` wrapper.
this.dataStoreConfiguration = dataStoreConfiguration;
}
public SqlConnection NewConnection()
{
return new SqlConnection(dataStoreConfiguration.ConnectionString);
}
}
}
Decoupling is an important consideration for DI, so I'm not sure why Microsoft have funnelled users into coupling their class libraries to an external dependency like IOptions, no matter how trivial it seems or what benefits it supposedly provides. I would also suggest that some of the benefits of IOptions seem like over-engineering. For example, it allows me to dynamically change configuration and have the changes tracked - I've used three other DI containers which included this feature and I've never used it once... Meanwhile, I can virtually guarantee you that teams will want to inject POCO classes or interfaces into libraries for their settings to replace ConfigurationManager, and seasoned developers will not be happy about an extraneous wrapper interface. I hope a utility similar to what I have described here is included in future versions of ASP.NET Core OR that someone provides me with a convincing argument for why I'm wrong.
I can't stand the IOptions recommendation either. It's a crappy design to force this on developers. IOptions should be clearly documented as optional, oh the irony.
This is what I do for my configuraition values
var mySettings = new MySettings();
Configuration.GetSection("Key").Bind(mySettings);
services.AddTransient(p => new MyService(mySettings));
You retain strong typing and don't need need to use IOptions in your services/libraries.
You can do something like this:
services.AddTransient(
o => ConfigurationBinder.Get<AppSettings>(Configuration.GetSection("AppSettings")
);
Using Net.Core v.2.2, it's worked for me.
Or then, use IOption<T>.Value
It would look something like this
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
I would recommend avoiding it wherever possible. I used to really like IOptions back when I was working primarily with core but as soon as you're in a hybrid framework scenario it's enough to drive you spare.
I found a similar issue with ILogger - Code that should work across frameworks won't because I just can't get it to bind properly as the code is too dependent on the DI framework.

Dependency injection based on configuration?

I am working on an app which uses active directory to retrieve users. Sometimes I need to work on the app but AD is not available. I use a wrapper class to retrieve the AD user. I would like to inject a different class based on the configuration. I was thinking on using an appSetting that would tell me the current configutarion and decide what type to use when injecting. Is it possible to get the current configuration without using a Web.config transform? Is it possible to inject objects using Unity based on Web.config transformations? Can you recommend a better approach?
[UPDATE]
I have a user repository class that loads the users from AD. All I need is to be able to change the implementation of this repository class so that when AD is unavailable I can load the users a different way. This would only be used for development, production would always access AD and retrieve users.
You can use preprocessor directives:
#if DEBUG
// register fake repository
#else
// register AD repository
#endif
Well, Dependency Injection is incredibly powerful, agile, and creates a separation of concerns. The pitfall in your approach will come from validation. You see, with this approach you have to choose a concrete implementation.
So how will you call those two classes?
public class LogToText : ILogger
{
public void LogMessage(string message) { }
}
public class LogToEvent : ILogger
{
public void LogMessage(string message) { }
}
You have these two implementations, but when you pass the interface to:
public class AD
{
public AD(ILogger logger) { }
}
So the question will be do you feel that you can properly validate to choose the proper implementation effectively. Otherwise, Dependency Injection may not work well. We don't entirely know the usage or goal, so our advice may not be truly beneficial. Hopefully you see what I mean, because you'll have to see if you can't test a particular way.
You could use an Abstraction for this:
public interface IPersonService
{
IEnumerable<Person> Find(PersonSearchParameters searchParams);
Person GetByAccountName(string accountName);
[ETC...]
}
Then both your AD and Development implements this interface.
To make things easier, I suggest using StructureMap IoC, so you can easily do this:
x.For<IPersonService>.Use<ActiveDirectoryPersonService>(); //for production
or
x.For<IPersonService>.Use<MockPersonService>(); //for development
When using this you can, for instancce:
public class TestController : Controller
{
IPersonService _service;
public TestController(IPersonService service)
{
_service = service;
}
}
Instead of using an XML file for configuration in this case, I suggest using the Fluent code of Structure Map, it's better when refactoring and to avoid mistyping because it compiles.
StructureMap:
http://structuremap.net
Easy Installable via NuGET
A different approach would be to use the build configuration, read back from the assembly.
var buildConfiguration = typeof(Program).Assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()?.Configuration;
if (buildConfiguration == "Debug")
{
// register fake repository
}
else
{
// register AD repository
}
Then it is safer to apply refactorings or automatic code clean up (e.g. ReSharper etc.). Depending on your current configuration, code cleanup can otherwise remove unused usings. What then leads to build issues if an other configuration is used.

Categories