Nopcommerce Error in service registration for new table - asp.net-mvc

Please help me out. I am stuck with the service registration. I tried every possible aspect from google but could not conclude. What I am trying to do is create a new table EducationDegree, a master table. The Consumer will then be assigned Education degree.
My issue is with the EducationDegreeService registration. Please see constructor created in Nop.web>Controllers> CustomController.cs and registration done in Nop.web>Framework> dependencyregister.cs. Additional code commented as
//Added By Shyam
It’s been more than a week and I have not been able to crack it. So please help me out.
Below here is the error message
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Nop.Web.Controllers.CustomerController' can be invoked with the available services and parameters:
Cannot resolve parameter 'Nop.Services.EducationDegrees.EducationDegreeService educationDegreeService' of constructor 'Void .ctor(Nop.Services.Authentication.IAuthenticationService, Nop.Services.Helpers.IDateTimeHelper, Nop.Services.Helpers.DateTimeSettings, Nop.Core.Domain.Tax.TaxSettings, Nop.Services.Localization.ILocalizationService, Nop.Core.IWorkContext, Nop.Core.IStoreContext, Nop.Services.Stores.IStoreMappingService, Nop.Services.Customers.ICustomerService, Nop.Services.Customers.ICustomerAttributeParser, Nop.Services.Customers.ICustomerAttributeService, Nop.Services.Common.IGenericAttributeService, Nop.Services.Customers.ICustomerRegistrationService, Nop.Services.Tax.ITaxService, Nop.Core.Domain.Customers.RewardPointsSettings, Nop.Core.Domain.Customers.CustomerSettings, Nop.Core.Domain.Common.AddressSettings, Nop.Core.Domain.Forums.ForumSettings, Nop.Core.Domain.Orders.OrderSettings, Nop.Services.Common.IAddressService, Nop.Services.Directory.ICountryService, Nop.Services.Directory.IStateProvinceService, Nop.Services.Orders.IOrderTotalCalculationService, Nop.Services.Orders.IOrderProcessingService, Nop.Services.Orders.IOrderService, Nop.Services.Directory.ICurrencyService, Nop.Services.Catalog.IPriceFormatter, Nop.Services.Media.IPictureService, Nop.Services.Messages.INewsLetterSubscriptionService, Nop.Services.Forums.IForumService, Nop.Services.Orders.IShoppingCartService, Nop.Services.Authentication.External.IOpenAuthenticationService, Nop.Services.Catalog.IBackInStockSubscriptionService, Nop.Services.Media.IDownloadService, Nop.Core.IWebHelper, Nop.Services.Logging.ICustomerActivityService, Nop.Core.Domain.Media.MediaSettings, Nop.Services.Messages.IWorkflowMessageService, Nop.Core.Domain.Localization.LocalizationSettings, Nop.Web.Framework.UI.Captcha.CaptchaSettings, Nop.Core.Domain.Customers.ExternalAuthenticationSettings, Nop.Services.EducationDegrees.EducationDegreeService)'
And here is the code I have added
Nop.Core>Domain >Educationdegrees
using Nop.Core.Domain.Localization;
namespace Nop.Core.Domain.EducationDegrees
{
public partial class EducationDegree: BaseEntity, ILocalizedEntity
{
/// <summary>
/// Gets or sets the name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the page size
/// </summary>
public int PageSize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether customers can select the page size
/// </summary>
public bool AllowCustomersToSelectPageSize { get; set; }
/// <summary>
/// Gets or sets the available customer selectable page size options
/// </summary>
public string PageSizeOptions { get; set; }
}
}
Nop.Data>Mapping>Education Degree – EducationDegreeMap.cs
using System.Data.Entity.ModelConfiguration;
using Nop.Core.Domain.EducationDegrees;
namespace Nop.Data.Mapping.EducationDegrees
{
public partial class EducationDegreeMap : EntityTypeConfiguration<EducationDegree>
{
public EducationDegreeMap()
{
this.ToTable("EducationDegree");
this.HasKey(v => v.Id);
this.Property(v => v.Name).IsRequired().HasMaxLength(1000);
}
}
}
Nop.Services>EducationDegree>EducationDegreeService.cs
using System;
using System.Linq;
using Nop.Core;
using Nop.Core.Data;
using Nop.Core.Domain.EducationDegrees;
using Nop.Services.Events;
namespace Nop.Services.EducationDegrees
{
public partial class EducationDegreeService : IEducationDegreeService
{
#region Fields
private readonly IRepository<EducationDegree> _educationdegreeRepository;
private readonly IEventPublisher _eventPublisher;
#endregion
#region Ctor
/// <summary>
/// Ctor
/// </summary>
/// <param name="educatioRepository">Vendor repository</param>
/// <param name="eventPublisher">Event published</param>
public EducationDegreeService(IRepository<EducationDegree> educationdegreeRepository,
IEventPublisher eventPublisher)
{
this._educationdegreeRepository = educationdegreeRepository;
this._eventPublisher = eventPublisher;
}
#endregion
#region Methods
/// <summary>
/// Gets all vendors
/// </summary>
/// <param name="showHidden">A value indicating whether to show hidden records</param>
/// <param name="pageIndex">Page index</param>
/// <param name="pageSize">Page size</param>
/// <returns>Vendors</returns>
public virtual IPagedList<EducationDegree> GetAllEducationDegrees(int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false)
{
var query = _educationdegreeRepository.Table;
query = query.OrderBy(v => v.Name);
var educationdegrees = new PagedList<EducationDegree>(query, pageIndex, pageSize);
return educationdegrees;
}
#endregion
}
}
Nop.Services>EducationDegree>IEducationDegreeService.cs
using Nop.Core;
using Nop.Core.Domain.EducationDegrees;
namespace Nop.Services.EducationDegrees
{
public partial interface IEducationDegreeService
{
/// <summary>
/// Gets all vendors
/// </summary>
/// <param name="showHidden">A value indicating whether to show hidden records</param>
/// <param name="pageIndex">Page index</param>
/// <param name="pageSize">Page size</param>
/// <returns>Vendors</returns>
IPagedList<EducationDegree> GetAllEducationDegrees(int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false);
}
}
Nop.Web>Infrastructure>DependencyRegister.cs
using Autofac;
using Autofac.Core;
using Nop.Core.Caching;
using Nop.Core.Infrastructure;
using Nop.Core.Infrastructure.DependencyManagement;
using Nop.Web.Controllers;
using Nop.Web.Infrastructure.Installation;
using Nop.Services.EducationDegrees;
namespace Nop.Web.Infrastructure
{
public class DependencyRegistrar : IDependencyRegistrar
{
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
//we cache presentation models between requests
builder.RegisterType<BlogController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<CatalogController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<CountryController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<CommonController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<NewsController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<PollController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<ProductController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<ShoppingCartController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<TopicController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
builder.RegisterType<WidgetController>()
.WithParameter(ResolvedParameter.ForNamed<ICacheManager>("nop_cache_static"));
//installation localization service
builder.RegisterType<InstallationLocalizationService>().As<IInstallationLocalizationService>().InstancePerLifetimeScope();
//Added by Shyam
builder.RegisterType<EducationDegreeService>().As<IEducationDegreeService>().InstancePerLifetimeScope();
}
public int Order
{
get { return 2; }
}
}
}
Nop.Web>Models>EducationDegrees
using System.Collections.Generic;
using System.Web.Mvc;
using FluentValidation.Attributes;
using Nop.Web.Framework;
using Nop.Web.Framework.Localization;
using Nop.Web.Framework.Mvc;
namespace Nop.Web.Models.EducationDegrees
{
public partial class EducationDegreeModel : BaseNopEntityModel
{
public EducationDegreeModel()
{
}
[NopResourceDisplayName("Education Degree")]
[AllowHtml]
public string Name { get; set; }
}
}
Nop.Web>Models>Customer>registerModel.cs
public RegisterModel()
{
this.AvailableTimeZones = new List<SelectListItem>();
this.AvailableCountries = new List<SelectListItem>();
this.AvailableStates = new List<SelectListItem>();
this.CustomerAttributes = new List<CustomerAttributeModel>();
//Added By Shyam
this.AvailableEducationDegrees = new List<SelectListItem>();
}
//Added By Shyam
public int? EducationDegreeId { get; set; }
public IList<SelectListItem> AvailableEducationDegrees { get; set; }
Nop.Web>Models>Customer>customerinfoModel.cs
//Added by Shyam
[NopResourceDisplayName("What is the highest educational degree that you have obtained (select one)?")]
public int? EducationDegreeId { get; set; }
public IList<SelectListItem> AvailableEducationDegree { get; set; }
Nop.web>Controllers> EducationDegreeController.cs
using System.Linq;
using System.Web.Mvc;
using Nop.Core.Domain.Vendors;
using Nop.Services.EducationDegrees;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Kendoui;
namespace Nop.Web.Controllers
{
public partial class EducationDegreeController : BasePublicController
{
#region Fields
private readonly IEducationDegreeService _educationdegreeService;
#endregion
#region Constructors
public EducationDegreeController(IEducationDegreeService educationdegreeService)
{
this._educationdegreeService = educationdegreeService;
}
#endregion
#region Methods
//list
public ActionResult Index()
{
return RedirectToAction("List");
}
#endregion
}
}
Nop.web>Framework> dependencyregister.cs
//Added by Shyam
using Nop.Services.EducationDegrees;
namespace Nop.Web.Framework
{
public class DependencyRegistrar : IDependencyRegistrar
{
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
. . . . . . .
. . . . . . . .
//Added by Shyam
builder.RegisterType<EducationDegreeService>().As<IEducationDegreeService>().InstancePerLifetimeScope();
}
}
}
Nop.web>Controllers> CustomController.cs
using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Nop.Web.Models.Customer;
//Added by shyam
using Nop.Services.EducationDegrees;
namespace Nop.Web.Controllers
{
public partial class CustomerController : BasePublicController
{
#region Fields
private readonly ExternalAuthenticationSettings _externalAuthenticationSettings;
//Added by Shyam
private readonly IEducationDegreeService _educationdegreeService;
#endregion
#region Ctor
public CustomerController(IAuthenticationService authenticationService,
IDateTimeHelper dateTimeHelper,
DateTimeSettings dateTimeSettings,
TaxSettings taxSettings,
ILocalizationService localizationService,
IWorkContext workContext,
IStoreContext storeContext,
IStoreMappingService storeMappingService,
ICustomerService customerService,
ICustomerAttributeParser customerAttributeParser,
ICustomerAttributeService customerAttributeService,
IGenericAttributeService genericAttributeService,
ICustomerRegistrationService customerRegistrationService,
ITaxService taxService, RewardPointsSettings rewardPointsSettings,
CustomerSettings customerSettings,AddressSettings addressSettings, ForumSettings forumSettings,
OrderSettings orderSettings, IAddressService addressService,
ICountryService countryService, IStateProvinceService stateProvinceService,
IOrderTotalCalculationService orderTotalCalculationService,
IOrderProcessingService orderProcessingService, IOrderService orderService,
ICurrencyService currencyService, IPriceFormatter priceFormatter,
IPictureService pictureService, INewsLetterSubscriptionService newsLetterSubscriptionService,
IForumService forumService, IShoppingCartService shoppingCartService,
IOpenAuthenticationService openAuthenticationService,
IBackInStockSubscriptionService backInStockSubscriptionService,
IDownloadService downloadService, IWebHelper webHelper,
ICustomerActivityService customerActivityService, MediaSettings mediaSettings,
IWorkflowMessageService workflowMessageService, LocalizationSettings localizationSettings,
CaptchaSettings captchaSettings, ExternalAuthenticationSettings externalAuthenticationSettings, EducationDegreeService educationDegreeService)
{
//Added by Shyam
this._educationdegreeService = educationDegreeService;
this._authenticationService = authenticationService;
this._dateTimeHelper = dateTimeHelper;
this._dateTimeSettings = dateTimeSettings;
this._taxSettings = taxSettings;
this._localizationService = localizationService;
this._workContext = workContext;
this._storeContext = storeContext;
this._storeMappingService = storeMappingService;
this._customerService = customerService;
this._customerAttributeParser = customerAttributeParser;
this._customerAttributeService = customerAttributeService;
this._genericAttributeService = genericAttributeService;
this._customerRegistrationService = customerRegistrationService;
this._taxService = taxService;
this._rewardPointsSettings = rewardPointsSettings;
this._customerSettings = customerSettings;
this._addressSettings = addressSettings;
this._forumSettings = forumSettings;
this._orderSettings = orderSettings;
this._addressService = addressService;
this._countryService = countryService;
this._stateProvinceService = stateProvinceService;
this._orderProcessingService = orderProcessingService;
this._orderTotalCalculationService = orderTotalCalculationService;
this._orderService = orderService;
this._currencyService = currencyService;
this._priceFormatter = priceFormatter;
this._pictureService = pictureService;
this._newsLetterSubscriptionService = newsLetterSubscriptionService;
this._forumService = forumService;
this._shoppingCartService = shoppingCartService;
this._openAuthenticationService = openAuthenticationService;
this._backInStockSubscriptionService = backInStockSubscriptionService;
this._downloadService = downloadService;
this._webHelper = webHelper;
this._customerActivityService = customerActivityService;
this._mediaSettings = mediaSettings;
this._workflowMessageService = workflowMessageService;
this._localizationSettings = localizationSettings;
this._captchaSettings = captchaSettings;
this._externalAuthenticationSettings = externalAuthenticationSettings;
}
}}

yes you are Right user3838557 :
Nop.Web.Controllers constructor declaration.
Instead of declaring
"EducationDegreeService educationDegreeService"
You should have used
IEducationDegreeService educationDegreeService"

Related

Requiring all parameters or return 404

I am trying to require the user to input all parameters into the URL like this:
http://127.0.0.1:5000/api/movies/1?foo=a&bar=c
The browser displays;
1 a c
I would like to ensure that all parameters are included in the URL, and the other combinations of parameters would result in 404. How do I modify the code to do this?
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using MoviesAPI.Services;
namespace MoviesAPI.Controllers
{
public interface IActionConstraint : IActionConstraintMetadata
{
/// <summary>
/// The constraint order.
/// </summary>
/// <remarks>
/// Constraints are grouped into stages by the value of <see cref="Order"/>. See remarks on
/// <see cref="IActionConstraint"/>.
/// </remarks>
/// <summary>
/// Determines whether an action is a valid candidate for selection.
/// </summary>
/// <param name="context">The <see cref="ActionConstraintContext"/>.</param>
/// <returns>True if the action is valid for selection, otherwise false.</returns>
bool Accept(ActionConstraintContext context);
}
public class RequiredFromQueryActionConstraint : IActionConstraint
{
private readonly string _parameter;
public RequiredFromQueryActionConstraint(string parameter)
{
_parameter = parameter;
}
public bool Accept(ActionConstraintContext context)
{
if (!context.RouteContext.HttpContext.Request.Query.ContainsKey(_parameter))
{
return false;
}
return true;
}
}
public class RequiredFromQueryAttribute : FromQueryAttribute, IParameterModelConvention
{
public void Apply(ParameterModel parameter)
{
if (parameter.Action.Selectors != null && parameter.Action.Selectors.Any())
{
parameter.Action.Selectors.Last().ActionConstraints.Add(new RequiredFromQueryActionConstraint(parameter.BindingInfo?.BinderModelName ?? parameter.ParameterName));
}
}
}
[Route("api/[controller]")]
public class MoviesController : Controller
{
private MoviesDbContext _context;
public MoviesController(MoviesDbContext context)
{
_context = context;
}
public IActionResult GetMovies()
{
return Ok(_context.Movies);
}
[HttpGet("{id}")]
public string Get(int id, [RequiredFromQuery]string foo, [RequiredFromQuery]string bar)
{
return id + " " + foo + " " + bar;
}
}
}
IActionConstraint Interface is included in Microsoft.AspNetCore.Mvc.ActionConstraints.
Remove your interface code and add the Order property for the RequiredFromQueryActionConstraint
public class RequiredFromQueryActionConstraint : IActionConstraint
{
private readonly string _parameter;
public RequiredFromQueryActionConstraint(string parameter)
{
_parameter = parameter;
}
public int Order => 999;
public bool Accept(ActionConstraintContext context)
{
if (!context.RouteContext.HttpContext.Request.Query.ContainsKey(_parameter))
{
return false;
}
return true;
}
}
public class RequiredFromQueryAttribute : FromQueryAttribute, IParameterModelConvention
{
public void Apply(ParameterModel parameter)
{
if (parameter.Action.Selectors != null && parameter.Action.Selectors.Any())
{
parameter.Action.Selectors.Last().ActionConstraints.Add(new RequiredFromQueryActionConstraint(parameter.BindingInfo?.BinderModelName ?? parameter.ParameterName));
}
}
}
Refer to https://www.strathweb.com/2016/09/required-query-string-parameters-in-asp-net-core-mvc/

Syncfusion Server-Side event is not passing data

I have a Asp.Net MVC project created from Syncfusion ASP.New MVC (Essential JS 2) VS template that is using Syncfusion's Data Grid. I can get the CrudUpdate event set in CrudUrl to fire at the server, however the value returned to CrudUpdate is empty. action parameter seems correctly set.
If I cast the value as Object, I get back a not-null, but VS cannot interrogate it. My guess is some weirdness in the way the value is cast or returned.
Has anyone got a complete working sample of the Syncfusion grid using the CrudUrl method with MVC (not asp). I am also guessing I may have some dependency issue.
View
#Html.EJS().Grid("CrudUrl").DataSource(dataManager => { dataManager.Url("/TestGrid2/UrlDatasource").CrudUrl("/TestGrid2/CrudUpdate").Adaptor("UrlAdaptor"); }).Columns(col =>
{
col.Field("RowKey").IsPrimaryKey(true).Add();
col.Field("PartitionKey").Add();
col.Field("sourceResourceId").Add();
col.Field("imagesLocation").Add();
col.Field("imagesResourceGroup").Add();
col.Field("imagePrefix").Add();
col.Field("imageVersion").Add();
}).AllowPaging().Toolbar(new List<string>() { "Search", "Add", "Edit", "Delete", "Update", "Cancel" }).EditSettings(edit => { edit.AllowAdding(true).AllowEditing(true).AllowDeleting(true); }).Render()
Controller
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Script.Services;
using System.Web.Services;
using DB;
//using Microsoft.AspNetCore.Mvc;
using Syncfusion.EJ2.Base;
namespace VMSSManagmentConsole.Controllers
{
public class TestGrid2Controller : Controller
{
private ModelContainer db = new ModelContainer();
public ActionResult TestGrid2()
{
//var items = db.ManagementItems.ToList();
//ViewBag.dataSource = items;
return View();
}
public ActionResult UrlDatasource([FromBody]DataManagerRequest dm)
{
IEnumerable DataSource = db.ManagementItems.ToList();
DataOperations operation = new DataOperations();
int count = DataSource.Cast<ManagementItem>().Count();
if (dm.Skip != 0)
{
DataSource = operation.PerformSkip(DataSource, dm.Skip); //Paging
}
if (dm.Take != 0)
{
DataSource = operation.PerformTake(DataSource, dm.Take);
}
var result = (ActionResult)(dm.RequiresCounts ? Json(new { result = DataSource, count = count }) : Json(DataSource));
return result;
}
public ActionResult CrudUpdate([FromBody]ICRUDModel<ManagementItem> value, string action)
{
//if (value.action == "update")
//{
// var ord = value.value;
// ManagementItem val = db.ManagementItems.Where(or => or.RowKey == ord.RowKey).FirstOrDefault();
// val.imagePrefix = ord.imagePrefix;
// val.imagesLocation = ord.imagesLocation;
// val.imagesResourceGroup = ord.imagesResourceGroup;
// val.imageVersion = ord.imageVersion;
// val.sourceResourceId = ord.sourceResourceId;
//}
//else if (value.action == "insert")
//{
// db.ManagementItems.Add(value.value);
//}
//else if (value.action == "remove")
//{
// db.ManagementItems.Remove(db.ManagementItems.Where(or => or.RowKey == value.key.ToString()).FirstOrDefault());
// return Json(value);
//}
//return Json(value.value);
return null;
}
public class ICRUDModel<T> where T : class
{
public string action { get; set; }
public string table { get; set; }
public string keyColumn { get; set; }
public object key { get; set; }
public T value { get; set; }
public List<T> added { get; set; }
public List<T> changed { get; set; }
public List<T> deleted { get; set; }
public IDictionary<string, object> #params { get; set; }
}
}
}
Use the DataGrid scaffold wizard. On the third page select DataSourceType = "Remote Data". A page and controller will be created with the correct code.
For your reference, we have created a sample and perform CRUD actions. Please refer the attached sample for more information.
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/GridEJ2Mvc-914352281
The reported problem occurred when model mismatched. For Example, when we specify the number column field(EmployeeID) in grid but while inserting you did not specify the value for that column then it shows value as null in CrudUpdate, for this scenario you need to specify the nullable value for that field in model class as follows.
public class OrdersDetails
{
public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, double Freight, bool Verified, DateTime OrderDate, string ShipCity, string ShipName, string ShipCountry, DateTime ShippedDate, string ShipAddress)
{
this.OrderID = OrderID;
this.CustomerID = CustomerId;
this.EmployeeID = EmployeeId;
. . . . .
}
public static List<OrdersDetails> GetAllRecords()
{
if (order.Count() == 0)
{
int code = 10000;
for (int i = 1; i < 10; i++)
{
order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6"));
. . . . .
}
}
return order;
}
public int? OrderID { get; set; }
public string CustomerID { get; set; }
public int? EmployeeID { get; set; } // it accept null value
. . . . .
}
}
If you still face the problem then share more details or below information that will helpful for us to validate further and provide a better solution as soon as possible.
• Did the problem occurred for both update and insert?
• Share package version details.
Regards,
Thavasianand S.

Integration test with IOptions<> in .NET Core

I pass IOption<T> to my CommandBus so I can get the settings from my ServiceBusSetting class. I want to do an integration test of my Bus. I do not want to resolve it just use new QueueCommandBus and need to pass IOptions to it.
var services = new ServiceCollection().AddOptions();
services.Configure<ServiceBusAppSettings>(Configuration.GetSection("ServiceBus"));
var options = services.BuildServiceProvider().GetService<IOptions<ServiceBusAppSettings>>();
////Act
var commandBus = new QueueCommandBus(options);
This works fine, but feels very complex code to get the IOptions<T> from my appsetting.json in my test project.
Any clue if this is the only way or is there a better way?
You don't need to create the ServiceCollection or IServiceProvider. The IConfiguration interface has a Bind() method, or from .NET Core 1.1 onwards, Get<T> which you can use to get the strongly-typed object directly:
var config = Configuration.GetSection("ServiceBus");
// .NET Core 1.0
var options = new ServiceBusAppSettings();
config.Bind(options);
// .NET Core 1.1
var options = config.Get<ServiceBusAppSettings>();
I like to add these as static methods to my AppSettings strongly-typed object, to make it convenient to load them from JSON in both my web app and from unit tests.
AppSettings.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
namespace My.Namespace
{
public class AppSettings
{
public class ServiceBusAppSettings
{
public string Setting1;
public int Setting2;
}
public class ApiSettings
{
public bool FormatJson { get; set; }
}
public class MySqlSettings
{
public string User { get; set; }
public string Password { get; set; }
public string Host { get; set; }
public string Database { get; set; }
public int Port { get; set; } = 3306;
public string GetConnectionString()
{
return $"Server={Host};Database={Database};Port={Port};Uid={User};Pwd={Password}";
}
}
public ServiceBusAppSettings ServiceBus { get; set; } = new ServiceBusAppSettings();
public ApiSettings Api { get; set; } = new ApiSettings();
public MySqlSettings MySql { get; set; } = new MySqlSettings();
// Static load helper methods. These could also be moved to a factory class.
public static IConfigurationRoot GetConfiguration(string dir)
{
return GetConfiguration(dir, null);
}
public static IConfigurationRoot GetConfiguration(string dir, string environmentName)
{
if (string.IsNullOrEmpty(environmentName))
environmentName = "Development";
var builder = new ConfigurationBuilder()
.SetBasePath(dir)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{environmentName}.json", true)
.AddEnvironmentVariables();
return builder.Build();
}
public static AppSettings GetSettings(string dir)
{
return GetSettings(dir, null);
}
public static AppSettings GetSettings(string dir, string environmentName)
{
var config = GetConfiguration(dir, environmentName);
return GetSettings(config);
}
public static AppSettings GetSettings(IConfiguration config)
{
return config.Get<AppSettings>();
}
}
}
ASP.NET Core Startup.cs: (Getting the strongly-typed settings object is often helpful at this stage, when configuring the other services...)
public class Startup
{
public Startup(IHostingEnvironment env)
{
Configuration = AppSettings.GetConfiguration(env.ContentRootPath, env.EnvironmentName);
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Configure the service collection.
services.AddOptions();
services.Configure<AppSettings>(Configuration);
// It can also be handy to get the AppSettings object here.
var settings = AppSettings.GetSettings(Configuration);
// Add framework services.
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
// Pretty-print JSON in Development
options.SerializerSettings.Formatting = settings.Api.FormatJson ? Formatting.Indented : Formatting.None;
});
// Store DB connection info in AppSettings too...
var conn = settings.MySql.GetConnectionString();
services.AddDbContext<MyDbContext>(opt => opt.UseMySql(conn));
}
}
In Test Class:
var testDir = AppContext.BaseDirectory;
var settings = AppSettings.GetSettings(testDir, "Test");
//Act
var commandBus = new QueueCommandBus(settings);

How can I redirect from my controller using AuthorizeAttributes?

I have made an AuthorizeAttributeBase to extend AuthorizeAttribute.
It looks like this:
public abstract class MyAuthorizeAttribute : AuthorizeAttribute
{
//Holds the roles allowed to perform the action.
public IEnumerable<string> roles { get; set; }
/// <summary>
/// Authorizes if the current user may perform the action
/// </summary>
/// <param name="httpContext">Unused - included for override purposes.</param>
/// <returns>true if authorized.</returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Return true if user is in the action allowed roles.
if (IsUserInRole)
{
return true;
}
else
{
HttpContext.Current.Response.StatusCode = 401;
return false;
}
}
/// <summary>
/// Checks if the user is member of a role that is allowed by the authorization
/// </summary>
public bool IsUserInRole
{
get
{
if (roles != null)
{
//Check if any of the roles in the session is in the list of roles of the authorization
return (MySessionGetter.GetSession().Roles.Intersect<string>(roles).Any());
}
//If none of the roles match return false.
return false;
}
}
/// <summary>
/// Sets the allowed roles of the authorization
/// </summary>
/// <param name="userRoles">Allowed roles</param>
public void AlowedRoles(IEnumerable<string> userRoles)
{
roles = userRoles;
}
I keep the allowed rolenames like this:
/// <summary>
/// Holds the role names.
/// </summary>
public static class UserRoles
{
public static string Administrators = "Administrators";
public static string Teachers= "Teachers";
}
And use my base like this:
/// <summary>
/// Authorization for the access to the SomeAction
/// </summary>
public class AuthorizeAccessToSomeActionAttribute : MyAuthorizeAttribute
{
public AuthorizeAccessToSomeActionAttribute()
{
AlowedRoles(new List<string> { UserRoles.Adminstrators,
UserRoles.Teachers });
}
}
And last but not least the controller:
/// <summary>
/// The main Index view of application
/// </summary>
/// <returns>Application Index views</returns>
[AuthorizeAccessToSomeAction]
public ActionResult Index()
{
return View("Index");
}
Now what I want to do is make the index switch return value on base of the AuthorizeAttributes.
Lets say Teachers to the TeachersIndex() and Administrators to the AdministratorsIndex().
I tried adding this to the base:
//Checks if the current user is authorized.
public bool IsAuthorized()
{
return AuthorizeCore(new HttpContextWrapper());
}
But I end up having to create new AutorizeAttributes every time.
Making it static seemed to give me even more problems.
Is there a correct way of going about this?
Solved it. :)
The OnAuthorization override got me a new lead.
Found this question.
I put the redirects in a Dictionary<string, RedirectToRouteResult> because I like the idea of keeping all role strings in one place instead of filling up my controllers with magic strings.
public static Dictionary<string, RedirectToRouteResult> HomeRedirect
{
get
{
return new Dictionary<string, RedirectToRouteResult> {
{"Administrators", new RedirectToRouteResult(
new RouteValueDictionary { { "action", "AdministratorIndex" }, { "controller", "MyController" }})},
{"Teacher", new RedirectToRouteResult(
new RouteValueDictionary { { "action", "TeacherIndex" }, { "controller", "MyController" }})}
};
And the override HandleUnauthorizedRequest looks like this now:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
filterContext.Result = UserRoles.HomeRedirect
.SingleOrDefault(m => m.Key == MySessionGetter.GetSession().Roles.First()).Value;
}
Take a look at RedirectToRouteResult and RedirectResult.
Here would be a good start:
// Redirects users of given role to given action
public class AuthorizeAccessToSomeActionAttribute : MyAuthorizeAttribute
{
public string Role { get; set; }
public string RedirectToAction { get; set; }
public AuthorizeAccessToSomeActionAttribute(string role, string action)
{
Role = role;
RedirectToAction = action;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
// Test if current user is in the role
if (filterContext.HttpContext.User.IsInRole(Role))
{
// Get current routevalues
var rvals = filterContext.RouteData.Values;
// Change action
rvals["action"] = RedirectToAction;
filterContext.Result = new RedirectToRouteResult("Default",rvals);
}
}
}
Usage:
// Redirect role to given action
[AuthorizeAccessToSomeActionAttribute("Teacher", "TeacherIndex" )]
[AuthorizeAccessToSomeActionAttribute("Admin", "AdminIndex" )]
public ActionResult Index()
...
public ActionResult TeacherIndex()
...
public ActionResult AdminIndex()

DI Creational pattern

Been struggling with this for quite a while now, so I'm starting to think I have created an anti-pattern. Nevertheless, here goes;
//Register self
container.Register(Component.For<IWindsorContainer>().Instance(container));
//Register all
container.Register(Component.For<IService1>().ImplementedBy<Service1>());
container.Register(Component.For<IService2>().ImplementedBy<Service2>());
//etc
IService1
{
//blabla
}
IService2 {IService1 Service1{get;}}
So IService1 and IService2 can be created without anything special.
Starting at IService3, an IProject is involved.
IProject{}
//Resolve a service that, amongst other things, relies on an IProject
IProjectGet
{
T Get<T>(IProject proj)
where T : class;
}
//Impl
ProjectGet : IProjectGet
{
IWindsorContainer _cont;
public ProjectGet(IWindsorContainer cont){_cont=cont}
public T Get<T>(IProject proj)
{
//Resolve using the main (and only) container and pass the IProject
return _cont.Resolve<T>(new {p = proj});
}
}
This does not work, only the main service is resolved with the
'p = proj' and any other dependencies the main service has, that also rely on the project,
cause an exception saying the project service was not found.
IService3
{
IService2 Service2{get;}
IProjectGet ProjectGet{get;}
IProjectLevelStuff SetActiveProject(IProject proj);
}
Service3 : IService3
{
IService2 Service2{get;private set;}
IProjectGet ProjectGet{get;private set;}
public Service3(IService2 s2, IProjectGet p)
{
ProjectGet = p;
Service2 = s2;
}
public IProjectLevelStuff SetActiveProject(IProject proj)
{
return ProjectGet.Get<IProjectLevelStuff>(proj);
}
}
ProjectLevelStuff : IProjectLevelStuff
{
IProject Project{get;private set;}
IService4 Service4 {get;private set;}
public ProjectLevelStuff(IProject p, IService4)//etc.
}
IService4
{
IService2 Service2{get;}
IService5 Service5{get;}
IService6 Service6{get;}
IProject Project{get;}
}
IService5{IProject Project{get;}}
IService6{IProject Project{get;}}
This fails because only ProjectLevelStuff gets the IProject passed to it, and since IService4 and it's dependencies also need it, an exception is thrown. Even if this did work, I don't like it, because each service with a dependency on IProject is forced to call that parameter 'p' which I want to avoid.
I just want to keep on using the services I already had but this time add the IProject instance that was passed to our generic Get method as a resolvable dependency. I have found no way to copy the container and creating a new one and then adding the main one as a child does not change anything (dependency still missing). How is this done?
Castle Windsor does have a TypeFactory built in but it essentially does the same thing as what I'm already doing and does not solve anything. The only 'solution' I found is creating a new container and registering the types all over again but this time resolve them via the main container (except for the IProject of course).. it's a maintenance nightmare in the works.
UPDATE: I added some unit tests to my answer below that hopefully clears up some things
please check if you could use the approach below using scopes:
[SetUp]
public void Setup()
{
int counter = 0;
_container = new WindsorContainer();
_container.AddFacility<TypedFactoryFacility>();
_container.Register(
Component.For<IService1>().ImplementedBy<Service1>().LifestyleScoped(),
Component.For<IService2>().ImplementedBy<Service2>().LifestyleScoped(),
Component.For<IService3>().ImplementedBy<Service3>().LifestyleScoped(),
Component.For<Class1>().LifestyleTransient(),
Component.For<Class2>().LifestyleTransient(),
Component.For<IProject>().ImplementedBy<Project>().LifestyleScoped().DynamicParameters((k, d) => d["name"] = "MyProjectName"+counter++)
);
}
[Test]
public void TestClass1()
{
using (_container.BeginScope())
{
Class1 object1 = _container.Resolve<Class1>();;
var object2 = _container.Resolve<Class1>();
Assert.AreNotSame(object1, object2);
Assert.AreSame(object1.Service1, object2.Service1);
}
}
[Test]
public void TestClass2()
{
Class2 object1;
using (_container.BeginScope())
{
object1 = _container.Resolve<Class2>();
var object2 = _container.Resolve<Class2>();
Assert.AreNotSame(object1, object2);
Assert.AreSame(object1.Project, object2.Project);
Assert.AreSame(object1.Service2.Project, object2.Service2.Project);
}
Class2 object3;
using (_container.BeginScope())
{
object3 = _container.Resolve<Class2>();
}
Assert.AreNotSame(object1.Project, object3.Project);
}
For some strange (but probably valid) reason Windsor child-containers can access their Parent containers but not the other way around. This means that in order to use services registered in the main container from the new container, we have to set the Parent of the main container rather than that of the new container.
This is painfully inconvenient because a container can only have one Parent.
internal class ProjServices : IProjServices
{
private readonly IKwProject _proj;
private readonly IWindsorContainer _mainCont;
public ProjServices(IKwProject proj, IWindsorContainer mainCont)
{
_mainCont = mainCont;
_proj = proj;
}
public T Resolve<T>()
{
T rett;
//Create new container
var projCont = new WindsorContainer();
//Register new service
projCont.Register(Component.For<IKwProject>().Instance(_proj));
//Set hierarchy
lock (_mainCont)
{
projCont.AddChildContainer(UiContainer); //ui needs project, set parent to projCont
UiContainer.AddChildContainer(_mainCont); //main needs ui, set parent to uiCont
//Resolve using main, which now has access to UI and Project services
try
{
rett = _mainCont.Resolve<T>();
}
finally
{
projCont.RemoveChildContainer(UiContainer);
UiContainer.RemoveChildContainer(_mainCont);
}
}
return rett;
}
private static readonly object UIContainerLock = new object();
private static volatile IWindsorContainer _uiContainer;
private static IWindsorContainer UiContainer
{
get
{
if(_uiContainer==null)
lock(UIContainerLock)
if (_uiContainer == null)
{
//Register the UI services
}
return _uiContainer;
}
}
}
And now if I wanted to use these new containers in even newer containers in the future I think I'd get stuck again due to the one-parent-only thing.... how do I properly do this, please?
UPDATE:
Unit Tests for VS 2010 and 2012:
ServiceTest.zip (798 KB)
https://mega.co.nz/#!z4JxUDoI!UEnt3TCoMFVg-vXKEAaJrhzjxfhcvirsW2hv1XBnZCc
Or to copy&paste:
using System;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ServiceTest
{
/// <summary>
/// A service that doesn't rely on anything else
/// </summary>
public interface IService1
{
}
class Service1 : IService1
{
}
/// <summary>
/// The Project
/// </summary>
public interface IProject
{
string Name { get; }
}
public class Project : IProject
{
public Project(string name)
{
Name = name;
}
public string Name { get; private set; }
}
/// <summary>
/// A Service that relies on a Project
/// </summary>
public interface IService2
{
IProject Project { get; }
string GetProjectName();
}
/// <summary>
/// The implementation shows it also relies on IService3
/// </summary>
public class Service2 : IService2
{
public Service2(IProject project, IService3 service3)
{
Project = project;
Service3 = service3;
}
public IProject Project { get; private set; }
public IService3 Service3 { get; private set; }
public string GetProjectName()
{
return Project.Name;
}
}
/// <summary>
/// IService3 is a Service that also relies on the Project
/// </summary>
public interface IService3
{
IProject Project { get; }
}
public class Service3 : IService3
{
public Service3(IProject project)
{
Project = project;
}
public IProject Project { get; private set; }
}
/// <summary>
/// Class1 uses the service without any dependencies so it will be easy to resolve
/// </summary>
public class Class1
{
public Class1(IService1 service1)
{
Service1 = service1;
}
public IService1 Service1 { get; private set; }
}
/// <summary>
/// Class2 also uses that service, but it also relies on a Project ánd IService2
/// which as you know also relies on the Project and IService3 which also relies on
/// the Project
/// </summary>
public class Class2
{
public Class2(IService1 service1, IProject project, IService2 service2)
{
Service1 = service1;
Project = project;
Service2 = service2;
}
public IProject Project { get; private set; }
public IService1 Service1 { get; private set; }
public IService2 Service2 { get; private set; }
}
/// <summary>
/// Set up the base services
/// </summary>
[TestClass]
public class UnitTestBase
{
protected WindsorContainer Cont;
[TestInitialize]
public void BaseSetup()
{
Cont = new WindsorContainer();
Cont.Register(Component.For<IService1>().ImplementedBy<Service1>().LifestyleTransient());
Cont.Register(Component.For<IService2>().ImplementedBy<Service2>().LifestyleTransient());
Cont.Register(Component.For<IService3>().ImplementedBy<Service3>().LifestyleTransient());
Cont.Register(Component.For<Class1>().LifestyleTransient());
Cont.Register(Component.For<Class2>().LifestyleTransient());
}
[TestMethod]
public void Class1_Resolves()
{
Cont.Resolve<Class1>();
}
}
/// <summary>
/// Set up the base unit tests
/// </summary>
[TestClass]
public class UnitTestClass2Base : UnitTestBase
{
protected void RunTest3Times(Func<string, IWindsorContainer> getContainer)
{
const string projNameBase = "MyProjectName";
Func<int, string> getProjectName = i => projNameBase + i;
for (var i = 0; i < 3; i++)
{
var pName = getProjectName(i);
GetClass2ForProject(getContainer(pName), pName);
}
}
protected void GetClass2ForProject(IWindsorContainer cont, string projName)
{
var c2 = cont.Resolve<Class2>();
Assert.IsTrue(c2.Project.Name == projName);
Assert.IsTrue(c2.Service2.Project.Name == projName);
Assert.IsTrue(c2.Service2.GetProjectName() == projName);
}
}
/// <summary>
/// This will fail on the second request because we cannot
/// overwrite the earlier registration. And iirc containers can't
/// be altered after the first resolve.
/// </summary>
[TestClass]
public class Attempt_1 : UnitTestClass2Base
{
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests()
{
RunTest3Times(s =>
{
Cont.Register(Component.For<IProject>().Instance(new Project(s)));
return Cont;
});
}
}
/// <summary>
/// It looks like we have to create a new container for every Project
/// So now the question remains; how do we get to keep using the base IService implementations
/// in the container that is scoped for the IProject?
/// </summary>
[TestClass]
public class Attempt_2 : UnitTestClass2Base
{
static IWindsorContainer CreateContainer(IProject p)
{
var ret = new WindsorContainer();
ret.Register(Component.For<IProject>().Instance(p));
return ret;
}
/// <summary>
/// This will fail because the services in the main
/// container can't access the IProject in the new container
/// </summary>
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests_1()
{
RunTest3Times(s =>
{
//Add the project container as a Child to the Main container
var projCont = CreateContainer(new Project(s));
Cont.AddChildContainer(projCont);
return Cont;
});
}
/// <summary>
/// Doing the previous approach the other way around works.
/// But now we can only resolve one thing at a time
/// </summary>
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests_2()
{
IWindsorContainer projCont = null;
//Add the Main container as a Child to the project container
// (in other words set the Parent of Main to Project)
// and then resolve using the main container.
//A container can only have one parent at a time so we can only
// resolve one scoped thing at a time.
RunTest3Times(s =>
{
if (projCont != null)
projCont.RemoveChildContainer(Cont);
projCont = CreateContainer(new Project(s));
projCont.AddChildContainer(Cont);
return Cont;
});
}
/// <summary>
/// The only way around that issue seems to be to register all project-dependent
/// services in the new container. Then re-register all original services
/// in the new container and pass the resolving on to the main container;
/// a maintenance nightmare and especially painful for named registrions.
/// </summary>
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests_3()
{
Func<IProject, IWindsorContainer> createContainer2 = p =>
{
var contNew = new WindsorContainer();
//Pass resolving of the non-dependent services on to the main container.
// this way it will respect it's lifestyle rules and not create new
// instances of services we wanted to use as a singleton etc.
contNew.Register(Component.For<IService1>().UsingFactoryMethod(() => Cont.Resolve<IService1>()).LifestyleTransient());
contNew.Register(Component.For<Class1>().UsingFactoryMethod(() => Cont.Resolve<Class1>()).LifestyleTransient());
//Register the dependent services directly in the new container so they can access the project
contNew.Register(Component.For<IService2>().ImplementedBy<Service2>().LifestyleTransient());
contNew.Register(Component.For<IService3>().ImplementedBy<Service3>().LifestyleTransient());
contNew.Register(Component.For<Class2>().LifestyleTransient());
contNew.Register(Component.For<IProject>().Instance(p));
return contNew;
};
RunTest3Times(s =>
{
var projCont = createContainer2(new Project(s));
return projCont;
});
}
}
}

Resources