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.
Related
Is the below code achievable?
If not, is there a similar approach to get child properties through methods in same class.
The reason to do it this was is to check for null and at the same time avoid creating multiple mapping profiles. Please guide me to the right direction on this one. Thanks
Its giving me this error:
Client projection contains reference to constant expression of 'Project.Web.Dto.Mapper.ProviderMappingProfile' through instance method 'GetLastUpdate'. This could potentially cause memory leak. Consider making the method static so that it does not capture constant in the instance.
{
public ProviderMappingProfile ()
{
CreateMap<Provider, ProviderDto>(MemberList.Destination)
.ForMember(d => d.property1, opts => opts.MapFrom(s => GetLastUpdate(s)))
.ForMember(d => d.property2, opts => opts.MapFrom(s => GetPropertChild2(s)));
}
private DateTime? GetLastUpdate(Provider provider)
{
var LastUpdate = provider.childDetails?.FirstOrDefault().LastUpdate;
return LastUpdate;
}
private string GetPropertChild2(Provider provider)
{
if (provider.IsCombined)
{
return provider.CombinedNo;
}
return provider.Worker?.Work;
}
}
You should refactor your Get... methods into custom resolvers:
public class LastUpdateResolver : IValueResolver<Provider, ProviderDto, DateTime?>
{
public DateTime? Resolve(Provider source, ProviderDto destination, DateTime? member, ResolutionContext context)
{
return source.childDetails?.FirstOrDefault().LastUpdate;
}
}
and use it on the profile declaration:
.ForMember(d => d.property1, opts => opts.MapFrom<LastUpdateResolver>())
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
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).
I have the following validator class:
public class FranchiseInfoValidator : AbstractValidator<FranchiseInfo>
{
public FranchiseInfoValidator()
{
RuleFor(franchiseInfo => franchiseInfo.FolderName).NotEmpty().Matches("^[a-zA-Z0-9_.:-]+$").WithMessage("Invalid characters");
RuleFor(franchiseInfo => franchiseInfo.ExeIconName).NotEmpty().Matches("^[a-zA-Z0-9_.:-]+$").WithMessage("Invalid characters");
RuleFor(franchiseInfo => franchiseInfo.FullName).NotEmpty().Matches("^[a-zA-Z0-9_.:-]+$").WithMessage("Invalid characters");
RuleFor(franchiseInfo => franchiseInfo.ShortName).NotEmpty().Matches("^[a-zA-Z0-9_.:-]+$").WithMessage("Invalid characters");
}
NotEmpty() and Matches("^[a-zA-Z0-9_.:-]+$").WithMessage("Invalid characters") validators with the custom message are the same for all properties. Is it possible to group them in one custom validator and then write something like this:
RuleFor(franchiseInfo => franchiseInfo.FolderName).SetValidator(new CustomValidator());
I have done some custom validators but not in this scenario. Is this possible? I have found no such example in the documentation. Furthermore, I wonder if this is possible to be done generic so if I have another validator class with properties to apply the same custom validator? Thanks.
yes, it should work with something like that
public class MyCustomValidator : PropertyValidator
{
public MyCustomValidator()
: base("Property {PropertyName} has invalid characters.")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var element = context.PropertyValue as string;
return !string.IsNullOrEmpty(element) && Regex.IsMatch(element, "^[a-zA-Z0-9_.:-]+$");
}
}
usage :
with your code, or create your own extension
public static class MyValidatorExtensions {
public static IRuleBuilderOptions<T, string> CheckIfNullAndMatches<T>(this IRuleBuilder<T, string> ruleBuilder) {
return ruleBuilder.SetValidator(new MyCustomValidator<TElement>());
}
}
then usage would be
RuleFor(franchiseInfo => franchiseInfo.FolderName).CheckIfNullAndMatches();
You could also have a regex as a parameter, if you need a more generic validator...
doc here
I have successfully configured StrctureMap and FluentNHibernate to work together with a parameter-less constructor. What I need to do now is set the interceptor with parameters.
The following code below works well in my MVC application:
protected void Application_Start()
{
//Code removed from here to save space...
ObjectFactory.Initialize(x => {
x.For<IInterceptor>().Singleton().Use(context => new MyInterceptor());
x.For<ISessionFactory>().Singleton().Use(context => CreateSessionFactory(context.GetInstance<IInterceptor>()));
x.For<ISession>().HttpContextScoped().Use(context => context.GetInstance<ISessionFactory>().OpenSession());
x.For<IAuditDao>().Use<AuditDao>().Ctor<ISessionFactory>().Is(context => context.GetInstance<ISessionFactory>());
});
}
public static ISessionFactory CreateSessionFactory(IInterceptor interceptor)
{
return Fluently.Configure()
.Database(MySQLConfiguration
.Standard
.ConnectionString(c => c.FromConnectionStringWithKey("MySqlConnectionString")))
.Mappings(m => { m.FluentMappings.Add<AuditItemMap>(); })
.ExposeConfiguration(config => new SchemaUpdate(config).Execute(true, true))
.ExposeConfiguration(config => config.SetInterceptor(interceptor))
.BuildSessionFactory();
}
What I now need to do is pass an instance of AuditDao into the the interceptor. At the moment 'MyInterceptor' which inherits from the NHibernate 'EmptyInterceptor' has two constructors:
public class MyInterceptor : EmptyInterceptor
{
private IAuditDao AuditDao;
public MyInterceptor()
{
}
public MyInterceptor(IAuditDao auditDao)
{
AuditDao = auditDao;
}
}
Now obviously an instance of AuditDao needs an instance of SessionFactory and SessionFactory would need an instance of AuditDao.
How do I solve this circular reference problem?