I'm trying to inject my dependencies into an automapper profile; the profile is like so;
public class UserProfile : Profile
{
private IIdentityService _identity;
public UserProfile(IIdentityService identity)
{
_identity = identity;
}
protected override void Configure()
{
Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<List<User>, UsersViewModel>()
.ForMember(d => d.Users, o => o.MapFrom(s => s))
.ForMember(d => d.Domains, o => GetDomains());
}
public List<string> GetDomains()
{
return _identity.AvailableDomains();
}
}
Several articles suggest using code like so;
kernel.Bind<IMappingEngine>().ToMethod(ctx => Mapper.Engine);
I've tried adding this in NinjectWebCommon (both in CreateKernel and RegisterServices), but i'm still getting runtime errors as AutoMapper can't resolve without a default (empty) constructor. I'm guessing the issue is with the Ninject side of things (my implementation of it that is).
Related
I have an ASP.Net Core 2.1 using Entity framework with an Angular 5 front-end and Web Api controller for the back-end.
It works fine as is but now I wan to change it so the database connection string is NOT hard coded.
I am following this:
https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-strings
But it does not work. I get:
An unhandled exception occurred while processing the request.
InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
'((Microsoft.EntityFrameworkCore.Internal.InternalDbSet)db.TblEmployee).Local' threw an exception of type 'System.InvalidOperationException'
The logic paths is:
The home page appears. I then click on the "Current Employees" menu item.
It goes into the Angular service and executes the getEmployees() method which executes the web api method.
It goes to the Web api controller and executes the - GetAllEmployee() method which executes the employee data access layers method.
It goes to the employee data access layer class (I instantiate the dbContext here). I have a break point on the return statement. If I hover over the return statement I see the error. And of course when I continue, the app fails.
My database context class is:
namespace Angular5NetcoreEF.Models
{
public partial class DBAngular5NetcoreEFContext : DbContext
{
public DBAngular5NetcoreEFContext()
{
}
public DBAngular5NetcoreEFContext(DbContextOptions<DBAngular5NetcoreEFContext> options)
: base(options)
{
}
public virtual DbSet<TblCities> TblCities { get; set; }
public virtual DbSet<TblEmployee> TblEmployee { get; set; }
//protected override void OnConfiguring(DbContextOptionsBuilder
optionsBuilder)
//{
// if (!optionsBuilder.IsConfigured)
// {
// optionsBuilder.UseSqlServer("Server=
// (localdb)\\mssqllocaldb;Database=DBAngular5NetcoreEF;
// Trusted_Connection=True; MultipleActiveResultSets=true");
// }
//}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TblCities>(entity =>
{
entity.HasKey(e => e.CityId);
entity.ToTable("tblCities");
entity.Property(e => e.CityId).HasColumnName("CityID");
entity.Property(e => e.CityName)
.IsRequired()
.HasMaxLength(20)
.IsUnicode(false);
});
modelBuilder.Entity<TblEmployee>(entity =>
{
entity.HasKey(e => e.EmployeeId);
entity.ToTable("tblEmployee");
entity.Property(e => e.EmployeeId).HasColumnName("EmployeeID");
entity.Property(e => e.City)
.IsRequired()
.HasMaxLength(20)
.IsUnicode(false);
entity.Property(e => e.Department)
.IsRequired()
.HasMaxLength(20)
.IsUnicode(false);
entity.Property(e => e.Gender)
.IsRequired()
.HasMaxLength(6)
.IsUnicode(false);
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(20)
.IsUnicode(false);
});
}
}
}
So per the instructions, I commented out the OnConfiguring method above where I was doing the hard coding.
I added to the appsettings.json file:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"DBAngular5NetcoreEFDatabase": "Server=(localdb)\\mssqllocaldb;Database=DBAngular5NetcoreEF;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"AllowedHosts": "*"
}
I added to my Startup.cs - ConfigureServices method :
using Angular5NetcoreEF.Models;
using Microsoft.EntityFrameworkCore;
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// In production, the Angular files will be served from this directory.
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
// I added this.
services.AddDbContext<DBAngular5NetcoreEFContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DBAngular5NetcoreEFDatabase")));
}
You should not instantiate a new instance of DBAngular5NetcoreEFContext inside EmplyeeDataAccessLayer. Instead, you should inject it.
You also need to register EmployeeDataAccessLayer in DI container and inject it to EmployeeController.
Basically, you let the DI container resolves the dependencies for you.
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DBAngular5NetcoreEFContext>
(options => options.UseSqlServer(Configuration.GetConnectionString("DBAngular5NetcoreEFDatabase")));
services.AddScoped<EmployeeDataAccessLayer>();
...
}
...
}
public class EmployeeController : Controller
{
private readonly EmployeeDataAccessLayer objemployee;
public EmployeeController(EmployeeDataAccessLayer employeeDataAccessLayer)
{
objemployee = employeeDataAccessLayer;
}
}
public class EmployeeDataAccessLayer
{
private readonly DBAngular5NetcoreEFContext _db;
public EmployeeDataAccessLayer(DBAngular5NetcoreEFContext db)
{
_db = db;
}
...
}
Another thought is to use interface instead of concrete implementation. It'll make your life easier when you implement unit tests.
the problem you are facing is fact, that you're not using Dependency Injection pattern.
First of all you need to insert DbContext from services via dependency injection by constructor by
public class EmployeeDataAccessLayer
{
private DBAngular5NetcoreEFContext _db;
public EmployeeDataAccessLayer(DBAngular5NetcoreEFContext db)
{
_db = db;
}
}
Second, all references should also be injected, so in every layer of the application, for each class like your EmployeeDataAccessLayer you should
First: Register it with dependency injection by using in Startup.cs -> ConfigureServices(): i.e.services.AddScoped<EmployeeDataAccessLayer>();,
Then inject it into constructor of Controller like in case above.
You can learn about dependency injection and example scopes (Scoped,Transient,Singleton...) from i.e. Doc
For security, in your current scenario to check when you have unconfigured context, you can do something like:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
throw new Exception("Context not configured");
}
}
As well as "temporary disable" empty constructor
Error activating IntPtr
I'm trying to configure FluentSecurity (v.1.4) with Ninject (v.3) in an ASP.NET MVC 4 application.
I can't set up the ResolveServicesUsing() configuration expression without throwing the above error.
SecurityConfigurator.Configure(
configuration =>
{
configuration.ResolveServicesUsing(
DependencyResolver.Current.GetServices,
DependencyResolver.Current.GetService);
...
I've also tried using another overload for ResolveServicesUsing()
configuration.ResolveServicesUsing(
type => DependencyResolver.Current.GetServices(type));
FluentSecurity needs to be configured with Ninject to inject the method for finding my users' roles and also for the PolicyViolationHandler implementations.
UPDATE
I've found I can leave out the offending lines and still have my GetRolesFrom() implementation called (hurrah):
configuration.GetRolesFrom(
() =>
((IPersonManager)DependencyResolver
.Current
.GetService(typeof(IPersonManager)))
.GetCurrentUserRoles());
I still can't get my PolicyViolationHandler to work, however:
public class RequireRolePolicyViolationHandler : IPolicyViolationHandler
{
public ActionResult Handle(PolicyViolationException exception)
{
return new RedirectToRouteResult(
new RouteValueDictionary(
new
{
action = "AccessDenied",
controller = "Home"
}));
}
}
I'm doing the binding in a NinjectModule like this:
public class SecurityModule : NinjectModule
{
public override void Load()
{
this.Kernel.Bind<IPolicyViolationHandler>()
.To<RequireRolePolicyViolationHandler>();
}
}
Error activating IntPtr
Unfortunately you havn't posted the complete StackTrace. But usually you will get this exception when injecting a Func to some class without having a binding or using the Factory extension.
I use Fluent Security with Ninject as IOC container.
In your Fluent Security configuration, you need to set the service locator to the NinjectServiceLocator.
public static void Configure(IKernel kernel)
{
var locator = new NinjectServiceLocator(kernel);
ServiceLocator.SetLocatorProvider(() => locator);
SecurityConfigurator.Configure(
configuration =>
{
configuration.GetAuthenticationStatusFrom(() => HttpContext.Current.User.Identity.IsAuthenticated);
....
}
You can get the locator here.
Hope this helps
For starters I'm using this module:
public class AutoMapperModule : NinjectModule
{
public override void Load()
{
Bind<ITypeMapFactory>().To<TypeMapFactory>();
foreach (var mapper in MapperRegistry.AllMappers())
{
Bind<IObjectMapper>().ToConstant(mapper);
}
Bind<AutoMapper.ConfigurationStore>().ToSelf().InSingletonScope().WithConstructorArgument("mappers", ctx => ctx.Kernel.GetAll<IObjectMapper>());
Bind<IConfiguration>().ToMethod(ctx => ctx.Kernel.Get<AutoMapper.ConfigurationStore>());
Bind<IConfigurationProvider>().ToMethod(ctx => ctx.Kernel.Get<AutoMapper.ConfigurationStore>());
Bind<IMappingEngine>().To<MappingEngine>();
}
}
I have a bootstrapper class for all my maps
public static void Configure(IKernel kernel)
{
Mapper.Initialize(map => map.ConstructServicesUsing(t => kernel.Get(t)));
}
I have resolvers that access the database and need the repositories injected.
It's working as is, but I can't figure out how to get it to work with unit tests and IMappingEngine.
public HomeController(IMappingEngine mappingEngine)
{
_mappingEngine = mappingEngine;
}
_mappingEngine.Map throws an exception because no map exists. Mapper.Map works.
What am I missing? How do I get my bootstrapper to work with unit tests so that the repositories in my resolvers to use the fake/mock repositories?
Try changing the mapping's bind.
Bind<IMappingEngine>().ToMethod(ctx => Mapper.Engine);
in my solution I have an ASP.NET MVC3 project, and a WCF project that works with a database.
I use entity framework self tracking and AutoMapper to map objects.
My question is: how i can use AutoMapper with Post Action like crate and delete and Edit methods
i see this Questions but not help me
this give me an error argument type '...' is not assignable to parameter type '...'
[HttpPost]
public ActionResult Create(MUser muser)
{
if (ModelState.IsValid)
{
Mapper.CreateMap<User, MUser>();
var user = Mapper.Map<User, MUser>(muser);
_proxy.SaveUser(user);
return RedirectToAction("Index");
}
return View(muser);
}
You shouldn't place the Mapper.CreateMap in your controller, you need to perform that action only once, so create a bootstrapper or something like that and call it from your application start method.
I think that's where your error comes from: you can create a mapping only once.
Oh, and you're defining the wrong types. You aren't trying to convert a User to a MUser, but you're doing it the other way around, so it should be:
Mapper.CreateMap<MUser, User>();
Mapper.Map<MUser, User>(muser);
Example of how to do this:
public class MvcApplication : HttpApplication
{
// some methods
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
MappingsBootstrapper.Initialize(); // call the bootstrap class that I created
}
}
Then I've got a project called 'Mappings' and it contains the bootstrap class and some 'configuration' classes (just like Entity Framework has EntityTypeConfiguration classes and Ninject has NinjectModules):
public static class MappingsBootstrapper
{
public static void Initialize()
{
new UserMappings();
new BookcaseItemMappings();
}
}
And the mappings:
class UserMappings
{
public UserMappings()
{
Mapper.CreateMap<User, UserSetupViewModel>();
Mapper.CreateMap<UserSetupViewModel, User>();
}
}
class BookcaseItemMappings
{
public BookcaseItemMappings()
{
Mapper.CreateMap<NewBookViewModel, BookcaseItem>().ForMember(x => x.BookId, opt => opt.Ignore());
Mapper.CreateMap<BookcaseItem, BookcaseItemViewModel>()
.ForMember(x => x.Title, opt => opt.MapFrom(src => src.Book.Title))
.ForMember(x => x.Authors, opt => opt.MapFrom(src => src.Book.Authors.Select(x => x.Name).Aggregate((i, j) => i + ", " + j)))
.ForMember(x => x.Identifiers, opt => opt.MapFrom(src => (!string.IsNullOrEmpty(src.Book.Isbn10) ? ("ISBN10: " + src.Book.Isbn10 + "\r\n") : string.Empty) +
(!string.IsNullOrEmpty(src.Book.Isbn13) ? ("ISBN13: " + src.Book.Isbn13) : string.Empty)))
.ForMember(x => x.Pages, opt => opt.MapFrom(src => src.Book.Pages))
.ForMember(x => x.ImageUri, opt => opt.MapFrom(src => src.Book.ThumbnailUriSmall));
}
}
You can do it any way you like, you could just place all the mappings in your Application_Start() method, but I found this to be a clean and maintainable way.
Question says it all really, the default is for it to map as a string but I need it to map as an int.
I'm currently using PersistenceModel for setting my conventions if that makes any difference.
Update
Found that getting onto the latest version of the code from the trunk resolved my woes.
The way to define this convention changed sometimes ago, it's now :
public class EnumConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
public void Apply(IPropertyInstance target)
{
target.CustomType(target.Property.PropertyType);
}
}
So, as mentioned, getting the latest version of Fluent NHibernate off the trunk got me to where I needed to be. An example mapping for an enum with the latest code is:
Map(quote => quote.Status).CustomTypeIs(typeof(QuoteStatus));
The custom type forces it to be handled as an instance of the enum rather than using the GenericEnumMapper<TEnum>.
I'm actually considering submitting a patch to be able to change between a enum mapper that persists a string and one that persists an int as that seems like something you should be able to set as a convention.
This popped up on my recent activity and things have changed in the newer versions of Fluent NHibernate to make this easier.
To make all enums be mapped as integers you can now create a convention like so:
public class EnumConvention : IUserTypeConvention
{
public bool Accept(IProperty target)
{
return target.PropertyType.IsEnum;
}
public void Apply(IProperty target)
{
target.CustomTypeIs(target.PropertyType);
}
public bool Accept(Type type)
{
return type.IsEnum;
}
}
Then your mapping only has to be:
Map(quote => quote.Status);
You add the convention to your Fluent NHibernate mapping like so;
Fluently.Configure(nHibConfig)
.Mappings(mappingConfiguration =>
{
mappingConfiguration.FluentMappings
.ConventionDiscovery.AddFromAssemblyOf<EnumConvention>();
})
./* other configuration */
Don't forget about nullable enums (like ExampleEnum? ExampleProperty)! They need to be checked separately. This is how it's done with the new FNH style configuration:
public class EnumConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum ||
(x.Property.PropertyType.IsGenericType &&
x.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
x.Property.PropertyType.GetGenericArguments()[0].IsEnum)
);
}
public void Apply(IPropertyInstance target)
{
target.CustomType(target.Property.PropertyType);
}
}
this is how I've mapped a enum property with an int value:
Map(x => x.Status).CustomType(typeof(Int32));
works for me!
For those using Fluent NHibernate with Automapping (and potentially an IoC container):
The IUserTypeConvention is as #Julien's answer above: https://stackoverflow.com/a/1706462/878612
public class EnumConvention : IUserTypeConvention
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
public void Apply(IPropertyInstance target)
{
target.CustomType(target.Property.PropertyType);
}
}
The Fluent NHibernate Automapping configuration could be configured like this:
protected virtual ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(SetupDatabase)
.Mappings(mappingConfiguration =>
{
mappingConfiguration.AutoMappings
.Add(CreateAutomappings);
}
).BuildSessionFactory();
}
protected virtual IPersistenceConfigurer SetupDatabase()
{
return MsSqlConfiguration.MsSql2008.UseOuterJoin()
.ConnectionString(x =>
x.FromConnectionStringWithKey("AppDatabase")) // In Web.config
.ShowSql();
}
protected static AutoPersistenceModel CreateAutomappings()
{
return AutoMap.AssemblyOf<ClassInAnAssemblyToBeMapped>(
new EntityAutomapConfiguration())
.Conventions.Setup(c =>
{
// Other IUserTypeConvention classes here
c.Add<EnumConvention>();
});
}
*Then the CreateSessionFactory can be utilized in an IoC such as Castle Windsor (using a PersistenceFacility and installer) easily. *
Kernel.Register(
Component.For<ISessionFactory>()
.UsingFactoryMethod(() => CreateSessionFactory()),
Component.For<ISession>()
.UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
.LifestylePerWebRequest()
);
You could create an NHibernate IUserType, and specify it using CustomTypeIs<T>() on the property map.
You should keep the values as int / tinyint in your DB Table. For mapping your enum you need to specify mapping correctly. Please see below mapping and enum sample,
Mapping Class
public class TransactionMap : ClassMap Transaction
{
public TransactionMap()
{
//Other mappings
.....
//Mapping for enum
Map(x => x.Status, "Status").CustomType();
Table("Transaction");
}
}
Enum
public enum TransactionStatus
{
Waiting = 1,
Processed = 2,
RolledBack = 3,
Blocked = 4,
Refunded = 5,
AlreadyProcessed = 6,
}