I am using AutoMapper 6.2.0 in my ASP.NET MVC 5 application.
When I call my view through controller it shows all things right. But, when I refresh that view, Visual Studio shows an error:
System.InvalidOperationException: 'Mapper already initialized. You must call Initialize once per application domain/process.'
I am using AutoMapper only in one controller. Not made any configuration in any place yet nor used AutoMapper in any other service or controller.
My controller:
public class StudentsController : Controller
{
private DataContext db = new DataContext();
// GET: Students
public ActionResult Index([Form] QueryOptions queryOptions)
{
var students = db.Students.Include(s => s.Father);
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Student, StudentViewModel>();
});
return View(new ResulList<StudentViewModel> {
QueryOptions = queryOptions,
Model = AutoMapper.Mapper.Map<List<Student>,List<StudentViewModel>>(students.ToList())
});
}
// Other Methods are deleted for ease...
Error within controller:
My Model class:
public class Student
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string CNIC { get; set; }
public string FormNo { get; set; }
public string PreviousEducaton { get; set; }
public string DOB { get; set; }
public int AdmissionYear { get; set; }
public virtual Father Father { get; set; }
public virtual Sarparast Sarparast { get; set; }
public virtual Zamin Zamin { get; set; }
public virtual ICollection<MulaqatiMehram> MulaqatiMehram { get; set; }
public virtual ICollection<Result> Results { get; set; }
}
My ViewModel Class:
public class StudentViewModel
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string CNIC { get; set; }
public string FormNo { get; set; }
public string PreviousEducaton { get; set; }
public string DOB { get; set; }
public int AdmissionYear { get; set; }
public virtual FatherViewModel Father { get; set; }
public virtual SarparastViewModel Sarparast { get; set; }
public virtual ZaminViewModel Zamin { get; set; }
}
If you want/need to stick with the static implementation in a unit testing scenario, note that you can call AutoMapper.Mapper.Reset() before calling initialize. Do note that this should not be used in production code as noted in the documentation.
Source: AutoMapper documentation.
When you refresh the view you are creating a new instance of the StudentsController -- and therefore reinitializing your Mapper -- resulting in the error message "Mapper already initialized".
From the Getting Started Guide
Where do I configure AutoMapper?
If you're using the static Mapper method, configuration should only happen once per AppDomain. That means the best place to put the configuration code is in application startup, such as the Global.asax file for ASP.NET applications.
One way to set this up is to place all of your mapping configurations into a static method.
App_Start/AutoMapperConfig.cs:
public class AutoMapperConfig
{
public static void Initialize()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Student, StudentViewModel>();
...
});
}
}
Then call this method in the Global.asax.cs
protected void Application_Start()
{
App_Start.AutoMapperConfig.Initialize();
}
Now you can (re)use it in your controller actions.
public class StudentsController : Controller
{
public ActionResult Index(int id)
{
var query = db.Students.Where(...);
var students = AutoMapper.Mapper.Map<List<StudentViewModel>>(query.ToList());
return View(students);
}
}
I've used this method before and it worked till version 6.1.1
Mapper.Initialize(cfg => cfg.CreateMap<ContactModel, ContactModel>()
.ConstructUsing(x => new ContactModel(LoggingDelegate))
.ForMember(x => x.EntityReference, opt => opt.Ignore())
);
Since version 6.2, this doesn't work any more. To correctly use Automapper create a new Mapper and us this one like this:
var mapper = new MapperConfiguration(cfg => cfg.CreateMap<ContactModel, ContactModel>()
.ConstructUsing(x => new ContactModel(LoggingDelegate))
.ForMember(x => x.EntityReference, opt => opt.Ignore())).CreateMapper();
var model = mapper.Map<ContactModel>(this);
In case you really need to "re-initialize" AutoMapper you should switch to the instance based API to avoid System.InvalidOperationException: Mapper already initialized. You must call Initialize once per application domain/process.
For example, when you are creating the TestServer for xUnit tests you can just set ServiceCollectionExtensions.UseStaticRegistration inside fixure class constructor to false to make the trick:
public TestServerFixture()
{
ServiceCollectionExtensions.UseStaticRegistration = false; // <-- HERE
var hostBuilder = new WebHostBuilder()
.UseEnvironment("Testing")
.UseStartup<Startup>();
Server = new TestServer(hostBuilder);
Client = Server.CreateClient();
}
For Unit Testing, you can add Mapper.Reset() to your unit test class
[TearDown]
public void TearDown()
{
Mapper.Reset();
}
You can use automapper as Static API and Instance API ,
Mapper already initialized is common issue in Static API , you can use mapper.Reset()
where you initialized mapper but this this not an answer at all.
Just try with instance API
var students = db.Students.Include(s => s.Father);
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Student, StudentViewModel>();
});
IMapper iMapper = config.CreateMapper();
return iMapper.Map<List<Student>, List<StudentViewModel>>(students);
Automapper 8.0.0 version
AutoMapper.Mapper.Reset();
Mapper.Initialize(
cfg => {
cfg.CreateMap<sourceModel,targetModel>();
}
);
You can simply use Mapper.Reset().
Example:
public static TDestination MapToObject<TSource, TDestination>(TSource Obj)
{
Mapper.Initialize(cfg => cfg.CreateMap<TSource, TDestination>());
TDestination tDestination = Mapper.Map<TDestination>(Obj);
Mapper.Reset();
return tDestination;
}
If you are using MsTest you can use the AssemblyInitialize attribute so that mapping gets configured only once for that assembly (here test assembly). This is generally added into to the base class of controller unit tests.
[TestClass]
public class BaseUnitTest
{
[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.EmailAddress));
});
}
}
I hope this answer helps
If you are using Mapper in UnitTest and your tests more then one, You may use Mapper.Reset()
`
//Your mapping.
public static void Initialize()
{
Mapper.Reset();
Mapper.Initialize(cfg =>
{
cfg.CreateMap<***>
}
//Your test classes.
[TestInitialize()]
public void Initialize()
{
AutoMapping.Initialize();
}`
private static bool _mapperIsInitialized = false;
public InventoryController()
{
if (!_mapperIsInitialized)
{
_mapperIsInitialized = true;
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<Inventory, Inventory>()
.ForMember(x => x.Orders, opt => opt.Ignore());
}
);
}
}
Related
I have multiple classes (A, B and C) each extends IdentityUser<Guid>. I also have a class called UserRole which extends IdentityRole<Guid>.
The following is my DbContext:
public sealed class EntityDbContext: DbContext
{
public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }
public DbSet<C> Cs { get; set; }
}
I added identities to IServiceCollection:
services
.AddIdentityCore<A>()
.AddEntityFrameworkStores<EntityDbContext>()
.AddRoles<UserRole>()
.AddUserStore<AUserStore>()
// .AddRoleStore<TRoleStore>()
.AddDefaultTokenProviders();
// Same for B, C
I also have the following stores:
public class AUserStore : UserStore<A, UserRole, EntityDbContext, Guid> { }
public class BUserStore : UserStore<B, UserRole, EntityDbContext, Guid> { }
public class CUserStore : UserStore<C, UserRole, EntityDbContext, Guid> { }
The following is the error I'm getting:
Specified argument was out of the range of valid values. (Parameter
'instance 'AUserStore' with ReturnType AUserStore cannot be cast to
IUserStore')
I don't know if what I'm doing is possible or not. Thanks for any help or hint.
Update
I think I got it working:
class GenericUserRoleStore : RoleStore<UserRole, EntityDbContext, Guid> { }
services.AddIdentity<A, UserRole>()
.AddDefaultTokenProviders()
.AddUserStore<AUserStore>()
.AddRoleStore<GenericUserRoleStore>();
services.AddIdentityCore<B>()
.AddRoles<UserRole>()
.AddDefaultTokenProviders()
.AddUserStore<BUserStore>()
.AddRoleStore<GenericUserRoleStore>();
services.AddIdentityCore<C>()
.AddRoles<UserRole>()
.AddDefaultTokenProviders()
.AddUserStore<CUserStore>()
.AddRoleStore<GenericUserRoleStore>();
Both comments on AddIdentity and AddIdentityCore have this:
Adds and configures the identity system for the specified User and Role types.
and,
Compare source code for AddIdentity<> and AddIdentityCore<>,
Review the default code from project template:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
....
}
I would say: IdentityFramework got confused when you register multiple identity types to it, but we do need it.
I believe what you are looking for are these posts:
Inheritance with EF Code First: Part 1 – Table per Hierarchy (TPH)
Inheritance with EF Code First: Part 2 – Table per Type (TPT)
Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC)
you have 3 above normal options to map your any UserType data to database. and the 1st options give you best performance, but give you very messive datatable when your usertypes are pretty complex. you would choose either of them for your real project as a balance.
Here is sample code with 1st approach:
public class ApplicationUser : IdentityUser<int>
{
public ApplicationUser() : base()
{
UserRoles = new HashSet<ApplicationUserRole>();
}
public int YearsOfExperience { get; set; }
[InverseProperty("User")]
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ProjectManager : ApplicationUser
{
public bool Talktive { get; set; }
}
public class Developer : ApplicationUser
{
public bool IsCSharper { get; set; }
}
public class Tester : Developer
{
public bool WhiteBox { get; set; }
}
public class Documenter : Tester
{
public List<string> Languages { get; set; } = new List<string>();
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
//get following users directly by following properties
public DbSet<ProjectManager> ProjectManagers { get; set; }
public DbSet<Developer> Developers { get; set; }
public DbSet<Tester> Testers { get; set; }
public DbSet<Documenter> Documenters { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
//prevent creating tables for following usertypes
builder.Ignore<ProjectManager>();
builder.Ignore<Developer>();
builder.Ignore<Tester>();
builder.Ignore<Documenter>();
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(entity =>
{
entity.HasMany(u => u.UserRoles).WithOne(x => x.User).HasForeignKey(c => c.UserId).IsRequired().OnDelete(DeleteBehavior.Cascade);
//tell database to use this column as Discriminator
entity.HasDiscriminator<string>("UserType");
});
builder.Entity<ApplicationRole>(entity =>
{
entity.HasKey(x => x.Id);
});
builder.Entity<ApplicationUserRole>(entity =>
{
entity.HasKey(c => new { c.UserId, c.RoleId });
entity.HasOne(x => x.Role).WithMany(x => x.UserRoles).HasForeignKey(x => x.RoleId).IsRequired().OnDelete(DeleteBehavior.Cascade);
entity.HasOne(x => x.User).WithMany(x => x.UserRoles).HasForeignKey(x => x.UserId).IsRequired().OnDelete(DeleteBehavior.Cascade);
});
}
}
when you need your users:
var allUsers = await _dbContext.Users.ToListAsync();
var allProjectManagers = await _dbContext.ProjectManagers.ToListAsync();
var allDevelopers = await _dbContext.Developers.ToListAsync();
var allTesters = await _dbContext.Testers.ToListAsync();
The next thing you want to configure is UserManager, instead of IUserStore.
public class ApplicationUserManager<TUser, TRole>
where TUser : ApplicationUser
where TRole : ApplicationRole
{
private readonly ApplicationDbContext _context;
private readonly UserManager<TUser> _userManager;
private readonly RoleManager<TRole> _roleManager;
public ApplicationUserManager(ApplicationDbContext context,
UserManager<TUser> userManager,
RoleManager<TRole> roleManager)
{
_context = context;
_userManager = userManager;
_roleManager = roleManager;
}
//customize your own base logics here.
}
public class DeveloperUserManager : ApplicationUserManager<Developer, ApplicationRole>
{
}
public class DocumenterUserManager : ApplicationUserManager<Documenter, ApplicationRole>
{
}
Enjoy it.
I am building a new project for browsing through movies and giving your opinion for them. Now I am on the administration part and I added functionality for adding a movie but when I try to add a movie the automapper throws exception for unmapped members on the service where I am mapping dto to data model. The members are from the base data model for example the id.
EDIT:
I tried to ignore all the members that make this exception, also tried to made a constructor with no arguments but doesn't work.
// Initialization
Mapper.Initialize(conf =>
{
conf.CreateMap<Movie, MovieDto>();
conf.CreateMap<MovieDto, Movie>();
conf.CreateMap<MovieDto, MovieViewModel>();
});
// Base Data Model
public class DataModel
{
[Key]
public int Id { get; set; }
[DataType(DataType.DateTime)]
public DateTime? CreatedOn { get; set; }
[DataType(DataType.DateTime)]
public DateTime? ModifiedOn { get; set; }
public bool IsDeleted { get; set; }
[DataType(DataType.DateTime)]
public DateTime? DeletedOn { get; set; }
}
// Movie Data Model
public class Movie: DataModel
{
public Movie(string title, double rating, string duration, string type, string description, DateTime releaseDate, string producer)
{
this.Title = title;
this.Rating = rating;
this.Duration = duration;
this.Type = type;
this.Description = description;
this.ReleaseDate = releaseDate;
this.Producer = producer;
}
// successfully mapped members
}
// Movie DTO
public class MovieDto
{
public string Title { get; set; }
public double Rating { get; set; }
public string Duration { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public DateTime ReleaseDate { get; set; }
public string Producer { get; set; }
}
// Add functionality
public void AddMovie(MovieDto movie)
{
//execption here
var movieDM = this.mapper.Map<Movie>(movie);
this.repo.Add(movieDM);
this.saver.SaveChanges();
}
This is the exception on img: https://i.imgur.com/RGZP6NP.png
Got it to work by doing the following.
Firstly, since DataModel is a base class, I followed automapper's mapping inheritance (see docs).
Then since you are using a mapper instance to map this.mapper.Map<Movie>(movie), the configuration needs to be instance rather than static as well, and I use the AutoMapper.Extensions.Microsoft.DependencyInjection nuget package for this, which allows registering Automapper with the IoC container.
My configuration looks like this (inside the ConfigureServices method of the Startup class).
services.AddAutoMapper(conf =>
{
conf.CreateMap<object, DataModel>()
.ForMember(d => d.Id, opts => opts.Ignore())
.ForMember(d => d.CreatedOn, opts => opts.MapFrom(_ => DateTime.Now))
.ForMember(d => d.ModifiedOn, opts => opts.MapFrom(_ => DateTime.Now))
.ForMember(d => d.DeletedOn, opts => opts.MapFrom(_ => (DateTime?)null))
.ForMember(d => d.IsDeleted, opts => opts.MapFrom(_ => false))
.Include<MovieDto, Movie>();
conf.CreateMap<Movie, MovieDto>();
conf.CreateMap<MovieDto, Movie>();
});
Note that I used CreateMap<object, DataModel> for the base class mapping and just used hardcoded values for dates there, feel free to adjust to suit your scenario.
After injecting an instance of IMapper, I was able to call this.mapper.Map<Movie>(movie) successfully.
Hope this sets u off in a good direction.
You can specify that AutoMapper should not validate that all properties are being mapped. The MemberList enum can be used for this when creating the mapping configuration. For example:
conf.CreateMap<MovieDto, Movie>(MemberList.None)
The error in the screenshot however indicates that another mapping is problematic, the one from MovieViewModel to MovieDto. I suggest you add a mapping configuration for these types as well:
conf.CreateMap<MovieViewModel, MovieDto>(MemberList.None)
You could try Profile Instances.
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<OrderViewModel, Order>()
.ForMember(dest => dest.OrderItem, opt => opt.MapFrom(src => src.OrderItemViewModel));
CreateMap<OrderItemViewModel, OrderItem>();
CreateMap<Order, Order>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
CreateMap<Movie, MovieDto>();
CreateMap<MovieDto, Movie>();
}
}
Here is the working demo AutoMapperProfile
I have a project with 4 classes:Direction, Area, Section and Local. Direction have many areas, Area have many sections and section have many locals. Local have positives locals and negatives locals, therefore Local entity will have a self many to many relationship. I'm using Automapper for convert LocalDto to Local, but when i try to update this entity with positives locals and/or negatives locals inserted, the system generate this exception:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
So, they are my mapper classes for my entities:
*******DirectionMapper*******
public static class DirectionMappers
{
public static void SettingMappingDirectionToDirectionDto()
{
Mapper.CreateMap<Direction, DirectionDto>()
.ForMember(directionDto => directionDto.AreasDtosList,
mc => mc.MapFrom(direction => direction.AreasCollection));
}
public static void SettingMappingDirectionDtoToDirection()
{
Mapper.CreateMap<DirectionDto, Direction>()
.ForMember(direction => direction.AreasCollection,
mc => mc.MapFrom(directionDto => directionDto.AreasDtosList));
}
public static void SettingMappingDirectionToString()
{
Mapper.CreateMap<Direction, string>().ConvertUsing(direction => direction.Name ?? string.Empty);
}
}
********AreaMapper**********
public class AreaMappers
{
public static void SettingMappingAreaToAreaDto()
{
Mapper.CreateMap<Area, AreaDto>()
.ForMember(areaDto => areaDto.SectionsDtosList, mc => mc.MapFrom(area => area.SectionsCollection))
.ForMember(areaDto => areaDto.DirectionDto, mc => mc.MapFrom(area => area.Direction));
}
public static void SettingMappingAreaDtoToArea()
{
Mapper.CreateMap<AreaDto, Area>()
.ForMember(area => area.SectionsCollection, mc => mc.MapFrom(areaDto => areaDto.SectionsDtosList))
.ForMember(area => area.Direction,mc=> mc.MapFrom(areaDto=> areaDto.DirectionDto));
}
public static void SettingMappingAreaToString()
{
Mapper.CreateMap<Area, string>().ConvertUsing(area => area.Name ?? string.Empty);
}
}
******SectionMapper*******************
public class SectionMappers
{
public static void SettingMappingSectionToSectionDto()
{
Mapper.CreateMap<Section, SectionDto>()
.ForMember(sectionDto => sectionDto.LocalsDtosList, mc => mc.MapFrom(section => section.LocalsCollection))
.ForMember(sectionDto => sectionDto.AreaDto, mc => mc.MapFrom(section => section.Area));
}
public static void SettingMappingSectionDtoToSection()
{
Mapper.CreateMap<SectionDto, Section>()
.ForMember(section => section.LocalsCollection,
mc => mc.MapFrom(sectionDto => sectionDto.LocalsDtosList))
.ForMember(section => section.Area, mc => mc.MapFrom(sectionDto => sectionDto.AreaDto));
}
public static void SettingMappingSectionToString()
{
Mapper.CreateMap<Section, string>().ConvertUsing(section => section.Name ?? string.Empty);
}
}
******LocalMapper (the main course)******
public static class LocalMappers
{
public static void SettingMappingLocalToLocalDto()
{
Mapper.CreateMap<Local, LocalDto>()
.ForMember(localDto => localDto.PositivesLocalsDtos,
mc => mc.MapFrom(local => local.PositivesLocals)
)
.ForMember(localDto => localDto.NegativesLocalsDtos,
mc => mc.MapFrom(local => local.NegativesLocals)
)
.ForMember(localDto => localDto.SectionDto, mc => mc.MapFrom(local => local.Section));
}
public static void SettingMappingLocalDtoToLocal()
{
Mapper.CreateMap<LocalDto, Local>()
.ForMember(local => local.PositivesLocals,
mc => mc.MapFrom(localDto => localDto.PositivesLocalsDtos)
)
.ForMember(local => local.NegativesLocals,
mc => mc.MapFrom(localDto => localDto.NegativesLocalsDtos)
)
.ForMember(local => local.Section, mc => mc.MapFrom(localDto => localDto.SectionDto));
}
public static void SettingMappingLocalToString()
{
Mapper.CreateMap<Local, string>().ConvertUsing(local => local.Number ?? string.Empty);
}
}
Well, this's a service method for Local update:
public AppOperationResult Update(int id, LocalDto localDto)
{
var appOperationResult = CommunValidations.IsDtoNull(localDto);
if (appOperationResult != null) return appOperationResult;
var tupleValidation = localDto.IsModelDtoValidateForUpdate(id);
var isValidate = tupleValidation.Item1;
if (isValidate)
{
if (TryUpdateLocalFromLocalDto(id, localDto)) return AppOperationResult.Successful();
}
string messageError = tupleValidation.Item2;
return AppOperationResult.WithError(messageError);
}
And these are the methods I did to add positive and negative locals (i call them AdjacentLocals):
public AppOperationResult AddAdjacentLocalsToLocal(AdjacentLocalsToLocalDto adjacentLocalsToLocal)
{
var localDto = adjacentLocalsToLocal.LocalToModify;
var appOperationResult = CommunValidations.IsDtoNull(localDto);
if (appOperationResult != null) return appOperationResult;
var tupleValidation = localDto.IsModelDtoValidate();
var isValidate = tupleValidation.Item1;
if (isValidate)
{
if (TryToAddAdjacentLocalsToLocal(adjacentLocalsToLocal, localDto))
return AppOperationResult.Successful();
}
string messageError = tupleValidation.Item2;
return AppOperationResult.WithError(messageError);
}
private bool TryToAddAdjacentLocalsToLocal(AdjacentLocalsToLocalDto adjacentLocalsToLocal, LocalDto localDto)
{
var positiveLocals = adjacentLocalsToLocal.PositiveLocals;
var negativeLocals = adjacentLocalsToLocal.NegativeLocals;
var positiveslocalsRepeated = positiveLocals.Intersect(localDto.PositivesLocalsDtos);
positiveLocals.RemoveAll(x => positiveslocalsRepeated.Contains(x));
var negativeslocalsRepeated = negativeLocals.Intersect(localDto.NegativesLocalsDtos);
negativeLocals.RemoveAll(x => negativeslocalsRepeated.Contains(x));
localDto.PositivesLocalsDtos = new List<LocalDto>(positiveLocals);
localDto.NegativesLocalsDtos = new List<LocalDto>(negativeLocals);
return TryUpdateLocalFromLocalDto(localDto.Id, localDto);
}
private bool TryUpdateLocalFromLocalDto(int idLocal, LocalDto localDto)
{
var local = _localServices.GetById(idLocal);
local.PositivesLocals.Clear();
local.NegativesLocals.Clear();
_localServices.Update(local);
if (local != null)
{
localDto.Id = idLocal;
var localUpdated = _mappingServices.Map(localDto, local);
_localServices.Update(localUpdated);
return true;
}
return false;
}
********LocalDto*************
public class LocalDto
{
public int Id { get; set; }
public string Number { get; set; }
public float Volumen { get; set; }
public int NumberMaxPeople { get; set; }
public SectionDto SectionDto { get; set; }
public List<LocalDto> PositivesLocalsDtos { get; set; }
public List<LocalDto> NegativesLocalsDtos { get; set; }
}
I'm working using ASP.NET WEB API philosophy,that's why I pass the list of adjacent places with a JSON (correctly), because I think the relationship between the objects in the lists with the database record is lost, but I do not understand why, since these local DTOs they are mapped correctly and return the corresponding local object. However, when I update a local with out a any list of positives or negatives locals, no problem.. so i think that problem is with the self many to many relationship.
I have traced the code several times, I check if all the entities have their relationships and everything seems to be fine, but when I try to update the Local entity inserting adjacents locals(positive and negative local) gives me the error that I mentioned above. So, i . I await your answers.Regards
I think what is happening is the following some entity entity framework that is not linking the existing sections in your database when you use the service of automapper, so I suggest that in your Dto not use the relationships for the other dto, for example:
public class LocalDto
{
public int Id { get; set; }
public string Number { get; set; }
public float Volumen { get; set; }
public int NumberMaxPeople { get; set; }
public SectionDto SectionDto { get; set; }
public List<LocalDto> PositivesLocalsDtos { get; set; }
public List<LocalDto> NegativesLocalsDtos { get; set; }
}
change it to :
public class LocalDto
{
public int Id { get; set; }
public string Number { get; set; }
public float Volumen { get; set; }
public int NumberMaxPeople { get; set; }
public int SectionId { get; set; }
public List<LocalDto> PositivesLocalsDtos { get; set; }
public List<LocalDto> NegativesLocalsDtos { get; set; }
}
and you must also change the mapper associated with these entities,this must be removed from the class LocalMappers,
.ForMember(localDto => localDto.SectionDto, mc => mc.MapFrom(local => local.Section));
This solution is for all your Dtos that have relations, link them to the id of the entity with which it is related not with the Dtos
I hope I've helped
I've been stuck with below exception from OnModelCreating() in DBContext and struggling to find the cause or a solution. Can't find much help online either.
{System.MissingMethodException: Method not found: 'System.Nullable`1<Int32> Models.Application.get_AddrSeqNum()'. at Data.Configuration.ApplicationConfiguration..ctor()
Any help or ideas please?
Details:
I've two entity types 'Application' and 'Organization' and their Entity Type Configurations as below:
public class Application
{
public int ApplId { get; set; }
public int? OrganizationId { get; set; } // Compsite FK (maps to ExternalId in Organization)
public int? AddrSeqNum { get; set; } // Compsite FK (maps to AddrSeqNum)
public virtual Organization Organization { get; set; }
}
public class Organization
{
public int Id { get; set; }
/// <summary>
/// Organization ID
/// </summary>
public int ExternalId { get; set; }
public int AddrSeqNum { get; set; }
}
public class ApplicationConfiguration : EntityTypeConfiguration<Application>
{
ToTable("Application");
HasKey(e => e.ApplId)
.Property(e => e.ApplId)
.HasColumnName("appl_id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(e => e.AddrSeqNum)
.HasColumnName("addr_seq_num");
// Relationship
HasOptional(e => e.Organization)
.WithMany()
.HasForeignKey(e => new { e.OrganizationId, e.AddrSeqNum });
}
public class OrganizationConfiguration : EntityTypeConfiguration<Organization>
{
public OrganizationConfiguration()
{
ToTable("Organization");
HasKey(e => new { e.ExternalId, e.AddrSeqNum }); // Compsite Unique key in table
Property(e => e.ExternalId)
.HasColumnName("external_id");
Property(e => e.AddrSeqNum)
.HasColumnName("addr_sequence");
Property(e => e.Id)
.HasColumnName("Id") // Primary key (auto increment)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
DBContext:
public partial class AppDbContext : DbContext
{
public AppDbContext ()
{
Configuration.LazyLoadingEnabled = true;
Configuration.ProxyCreationEnabled = false;
//Configuration.AutoDetectChangesEnabled = false;
DbInterception.Add(new FtsInterceptor());
Database.Log = (msg) => Logger.Debug(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations // <<- Exception from here
.Add(new ApplicationConfiguration())
.Add(new OrganizationConfiguration())
base.OnModelCreating(modelBuilder);
}
}
It was a weird situation, the references to the project that has my pocos were out of sync and were not getting rebuilt when I built my solution. One of the project is a Workflow console app targeting x86 platform. I changed to AnyCPU for all the projects in my sln. And magically ot worked again.
I was trying to add a new column/property to ine of my poco entity.
I was having this problem too, because I made a nullable database column non-nullable and Visual Studio didn't seem to want to cooperate with the change.
Deleting bin and obj folders, as well as rebuilding, didn't seem to do it.
As a workaround, I just made it nullable again. It's interesting because I'm not sure where VS is caching the nullable part--a question to be answered when I'm under less tight deadlines. :)
I am working with Entity Framework Code First and MVC 5. When I created my application with Individual User Accounts Authentication I was given an Account controller and along with it all the required classes and code that is needed to get the Indiv User Accounts authentication to work.
Among the code already in place was this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DXContext", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
But then I went ahead and created my own context using code first, so I now have the following too:
public class DXContext : DbContext
{
public DXContext() : base("DXContext")
{
}
public DbSet<ApplicationUser> Users { get; set; }
public DbSet<IdentityRole> Roles { get; set; }
public DbSet<Artist> Artists { get; set; }
public DbSet<Paintings> Paintings { get; set; }
}
Finally I have the following seed method to add some data for me to work with whilst developing:
protected override void Seed(DXContext context)
{
try
{
if (!context.Roles.Any(r => r.Name == "Admin"))
{
var store = new RoleStore<IdentityRole>(context);
var manager = new RoleManager<IdentityRole>(store);
var role = new IdentityRole { Name = "Admin" };
manager.Create(role);
}
context.SaveChanges();
if (!context.Users.Any(u => u.UserName == "James"))
{
var store = new UserStore<ApplicationUser>(context);
var manager = new UserManager<ApplicationUser>(store);
var user = new ApplicationUser { UserName = "James" };
manager.Create(user, "ChangeAsap1#");
manager.AddToRole(user.Id, "Admin");
}
context.SaveChanges();
string userId = "";
userId = context.Users.FirstOrDefault().Id;
var artists = new List<Artist>
{
new Artist { FName = "Salvador", LName = "Dali", ImgURL = "http://i62.tinypic.com/ss8txxn.jpg", UrlFriendly = "salvador-dali", Verified = true, ApplicationUserId = userId },
};
artists.ForEach(a => context.Artists.Add(a));
context.SaveChanges();
var paintings = new List<Painting>
{
new Painting { Title = "The Persistence of Memory", ImgUrl = "http://i62.tinypic.com/xx8tssn.jpg", ArtistId = 1, Verified = true, ApplicationUserId = userId }
};
paintings.ForEach(p => context.Paintings.Add(p));
context.SaveChanges();
}
catch (DbEntityValidationException ex)
{
foreach (var validationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
}
}
My solution builds fine, but when I try and access a controller that requires access to the database I get the following error:
DX.DOMAIN.Context.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
DX.DOMAIN.Context.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
What am I doing wrong? Is it because I have two contexts?
UPDATE
After reading Augusto's reply, I went with Option 3. Here is what my DXContext class looks like now:
public class DXContext : DbContext
{
public DXContext() : base("DXContext")
{
// remove default initializer
Database.SetInitializer<DXContext>(null);
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Artist> Artists { get; set; }
public DbSet<Painting> Paintings { get; set; }
public static DXContext Create()
{
return new DXContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<Role>().ToTable("Roles");
}
public DbQuery<T> Query<T>() where T : class
{
return Set<T>().AsNoTracking();
}
}
I also added a User.cs and a Role.cs class, they look like this:
public class User
{
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
}
public class Role
{
public int Id { set; get; }
public string Name { set; get; }
}
I wasn't sure if I would need a password property on the user, since the default ApplicationUser has that and a bunch of other fields!
Anyways, the above change builds fine, but again I get this error when the application is ran:
Invalid Column name UserId
UserId is an integer property on my Artist.cs
In my case I had inherited from the IdentityDbContext correctly (with my own custom types and key defined) but had inadvertantly removed the call to the base class's OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); // I had removed this
/// Rest of on model creating here.
}
Which then fixed up my missing indexes from the identity classes and I could then generate migrations and enable migrations appropriately.
The problem is that your ApplicationUser inherits from IdentityUser, which is defined like this:
IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser
....
public virtual ICollection<TRole> Roles { get; private set; }
public virtual ICollection<TClaim> Claims { get; private set; }
public virtual ICollection<TLogin> Logins { get; private set; }
and their primary keys are mapped in the method OnModelCreating of the class IdentityDbContext:
modelBuilder.Entity<TUserRole>()
.HasKey(r => new {r.UserId, r.RoleId})
.ToTable("AspNetUserRoles");
modelBuilder.Entity<TUserLogin>()
.HasKey(l => new {l.LoginProvider, l.ProviderKey, l.UserId})
.ToTable("AspNetUserLogins");
and as your DXContext doesn't derive from it, those keys don't get defined.
If you dig into the sources of Microsoft.AspNet.Identity.EntityFramework, you will understand everything.
I came across this situation some time ago, and I found three possible solutions (maybe there are more):
Use separate DbContexts against two different databases or the same database but different tables.
Merge your DXContext with ApplicationDbContext and use one database.
Use separate DbContexts against the same table and manage their migrations accordingly.
Option 1:
See update the bottom.
Option 2:
You will end up with a DbContext like this one:
public class DXContext : IdentityDbContext<User, Role,
int, UserLogin, UserRole, UserClaim>//: DbContext
{
public DXContext()
: base("name=DXContext")
{
Database.SetInitializer<DXContext>(null);// Remove default initializer
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
}
public static DXContext Create()
{
return new DXContext();
}
//Identity and Authorization
public DbSet<UserLogin> UserLogins { get; set; }
public DbSet<UserClaim> UserClaims { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
// ... your custom DbSets
public DbSet<RoleOperation> RoleOperations { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// Configure Asp Net Identity Tables
modelBuilder.Entity<User>().ToTable("User");
modelBuilder.Entity<User>().Property(u => u.PasswordHash).HasMaxLength(500);
modelBuilder.Entity<User>().Property(u => u.Stamp).HasMaxLength(500);
modelBuilder.Entity<User>().Property(u => u.PhoneNumber).HasMaxLength(50);
modelBuilder.Entity<Role>().ToTable("Role");
modelBuilder.Entity<UserRole>().ToTable("UserRole");
modelBuilder.Entity<UserLogin>().ToTable("UserLogin");
modelBuilder.Entity<UserClaim>().ToTable("UserClaim");
modelBuilder.Entity<UserClaim>().Property(u => u.ClaimType).HasMaxLength(150);
modelBuilder.Entity<UserClaim>().Property(u => u.ClaimValue).HasMaxLength(500);
}
}
Option 3:
You will have one DbContext equal to the option 2. Let's name it IdentityContext. And you will have another DbContext called DXContext:
public class DXContext : DbContext
{
public DXContext()
: base("name=DXContext") // connection string in the application configuration file.
{
Database.SetInitializer<DXContext>(null); // Remove default initializer
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
// Domain Model
public DbSet<User> Users { get; set; }
// ... other custom DbSets
public static DXContext Create()
{
return new DXContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
// IMPORTANT: we are mapping the entity User to the same table as the entity ApplicationUser
modelBuilder.Entity<User>().ToTable("User");
}
public DbQuery<T> Query<T>() where T : class
{
return Set<T>().AsNoTracking();
}
}
where User is:
public class User
{
public int Id { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
[Required, StringLength(128)]
public string SomeOtherColumn { get; set; }
}
With this solution, I'm mapping the entity User to the same table as the entity ApplicationUser.
Then, using Code First Migrations you'll need to generate the migrations for the IdentityContext and THEN for the DXContext, following this great post from Shailendra Chauhan: Code First Migrations with Multiple Data Contexts
You'll have to modify the migration generated for DXContext. Something like this depending on which properties are shared between ApplicationUser and User:
//CreateTable(
// "dbo.User",
// c => new
// {
// Id = c.Int(nullable: false, identity: true),
// Name = c.String(nullable: false, maxLength: 100),
// SomeOtherColumn = c.String(nullable: false, maxLength: 128),
// })
// .PrimaryKey(t => t.Id);
AddColumn("dbo.User", "SomeOtherColumn", c => c.String(nullable: false, maxLength: 128));
and then running the migrations in order (first the Identity migrations) from the global.asax or any other place of your application using this custom class:
public static class DXDatabaseMigrator
{
public static string ExecuteMigrations()
{
return string.Format("Identity migrations: {0}. DX migrations: {1}.", ExecuteIdentityMigrations(),
ExecuteDXMigrations());
}
private static string ExecuteIdentityMigrations()
{
IdentityMigrationConfiguration configuration = new IdentityMigrationConfiguration();
return RunMigrations(configuration);
}
private static string ExecuteDXMigrations()
{
DXMigrationConfiguration configuration = new DXMigrationConfiguration();
return RunMigrations(configuration);
}
private static string RunMigrations(DbMigrationsConfiguration configuration)
{
List<string> pendingMigrations;
try
{
DbMigrator migrator = new DbMigrator(configuration);
pendingMigrations = migrator.GetPendingMigrations().ToList(); // Just to be able to log which migrations were executed
if (pendingMigrations.Any())
migrator.Update();
}
catch (Exception e)
{
ExceptionManager.LogException(e);
return e.Message;
}
return !pendingMigrations.Any() ? "None" : string.Join(", ", pendingMigrations);
}
}
This way, my n-tier cross-cutting entities don't end up inheriting from AspNetIdentity classes, and therefore I don't have to import this framework in every project where I use them.
Sorry for the extensive post. I hope it could offer some guidance on this. I have already used options 2 and 3 in production environments.
UPDATE: Expand Option 1
For the last two projects I have used the 1st option: having an AspNetUser class that derives from IdentityUser, and a separate custom class called AppUser. In my case, the DbContexts are IdentityContext and DomainContext respectively. And I defined the Id of the AppUser like this:
public class AppUser : TrackableEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
// This Id is equal to the Id in the AspNetUser table and it's manually set.
public override int Id { get; set; }
(TrackableEntity is the custom abstract base class that I use in the overridden SaveChanges method of my DomainContext context)
I first create the AspNetUser and then the AppUser. The drawback with this approach is that you have ensured that your "CreateUser" functionality is transactional (remember that there will be two DbContexts calling SaveChanges separately). Using TransactionScope didn't work for me for some reason, so I ended up doing something ugly but that works for me:
IdentityResult identityResult = UserManager.Create(aspNetUser, model.Password);
if (!identityResult.Succeeded)
throw new TechnicalException("User creation didn't succeed", new LogObjectException(result));
AppUser appUser;
try
{
appUser = RegisterInAppUserTable(model, aspNetUser);
}
catch (Exception)
{
// Roll back
UserManager.Delete(aspNetUser);
throw;
}
(Please, if somebody comes with a better way of doing this part I appreciate commenting or proposing an edit to this answer)
The benefits are that you don't have to modify the migrations and you can use any crazy inheritance hierarchy over the AppUser without messing with the AspNetUser. And actually, I use Automatic Migrations for my IdentityContext (the context that derives from IdentityDbContext):
public sealed class IdentityMigrationConfiguration : DbMigrationsConfiguration<IdentityContext>
{
public IdentityMigrationConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;
}
protected override void Seed(IdentityContext context)
{
}
}
This approach also has the benefit of avoiding to have your n-tier cross-cutting entities inheriting from AspNetIdentity classes.
By Changing The DbContext As Below;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
Just adding in OnModelCreating method call to base.OnModelCreating(modelBuilder); and it becomes fine. I am using EF6.
Special Thanks To #The Senator
For those who use ASP.NET Identity 2.1 and have changed the primary key from the default string to either int or Guid, if you're still getting
EntityType 'xxxxUserLogin' has no key defined. Define the key for this EntityType.
EntityType 'xxxxUserRole' has no key defined. Define the key for this EntityType.
you probably just forgot to specify the new key type on IdentityDbContext:
public class AppIdentityDbContext : IdentityDbContext<
AppUser, AppRole, int, AppUserLogin, AppUserRole, AppUserClaim>
{
public AppIdentityDbContext()
: base("MY_CONNECTION_STRING")
{
}
......
}
If you just have
public class AppIdentityDbContext : IdentityDbContext
{
......
}
or even
public class AppIdentityDbContext : IdentityDbContext<AppUser>
{
......
}
you will get that 'no key defined' error when you are trying to add migrations or update the database.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
// relationship.DeleteBehavior = DeleteBehavior.Restrict;
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<IdentityRole<string>>().ToTable("Roles");
modelBuilder.Entity<IdentityUserToken<string>>().ToTable("UserTokens");
modelBuilder.Entity<IdentityUserClaim<string>>().ToTable("UserClaims");
modelBuilder.Entity<IdentityUserLogin<string>>().ToTable("UserLogins");
modelBuilder.Entity<IdentityRoleClaim<string>>().ToTable("RoleClaims");
modelBuilder.Entity<IdentityUserRole<string>>().ToTable("UserRoles");
}
}
My issue was similar - I had a new table i was creating that ahd to tie in to the identity users. After reading the above answers, realized it had to do with IsdentityUser and the inherited properites. I already had Identity set up as its own Context, so to avoid inherently tying the two together, rather than using the related user table as a true EF property, I set up a non-mapped property with the query to get the related entities. (DataManager is set up to retrieve the current context in which OtherEntity exists.)
[Table("UserOtherEntity")]
public partial class UserOtherEntity
{
public Guid UserOtherEntityId { get; set; }
[Required]
[StringLength(128)]
public string UserId { get; set; }
[Required]
public Guid OtherEntityId { get; set; }
public virtual OtherEntity OtherEntity { get; set; }
}
public partial class UserOtherEntity : DataManager
{
public static IEnumerable<OtherEntity> GetOtherEntitiesByUserId(string userId)
{
return Connect2Context.UserOtherEntities.Where(ue => ue.UserId == userId).Select(ue => ue.OtherEntity);
}
}
public partial class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
[NotMapped]
public IEnumerable<OtherEntity> OtherEntities
{
get
{
return UserOtherEntities.GetOtherEntitiesByUserId(this.Id);
}
}
}