EF Core database configuration - c#

Hi I'm needing a little help with EF CORE in WPF I'm still new to ef core, I'm getting duplicate entry with primary key when adding a job, I'm assuming its trying to insert the same contact and department into the tables where I'm currently storing them.
Error when adding a job is
Microsoft.EntityFrameworkCore.DbUpdateException: 'An error occurred while updating the entries. See the inner exception for details.'
Inner exception
MySqlException: Duplicate entry '4' for key 'PRIMARY'
all values passed to the add job command have the correct values
code is
public static class JobsService
{
public static async Task<List<Job>> GetJobs()
{
using (var db = new DatabaseContext())
{
db.Database.EnsureCreated();
return await db.Jobs.ToListAsync();
}
}
public static async Task AddJob(string title, string partNumber, double quantity, Contact assignedTo, Department department)
{
using(var db = new DatabaseContext())
{
db.Database.EnsureCreated();
var job = new Job(title, partNumber, quantity, assignedTo, department);
await db.Jobs.AddAsync(job);
await db.SaveChangesAsync();
}
}
}
public class Job
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public double Quantity { get; set; }
public double? ReceivedQuantity { get; set; }
public string PartNumber { get; set; }
public DateTimeOffset? CreatedDateTime { get; set; }
public User CreatedBy { get; set; }
public Contact AssignedTo { get; set; }
public Contact? CompletedBy { get; set; }
public DateTimeOffset? CompletedDateTime { get; set; }
public Department Department { get; set; }
public JobStatus Status { get; set; }
public Job(string title, string partNumber, double quantity, Contact assignedTo, Department department)
{
Title = title;
PartNumber = partNumber;
Quantity = quantity;
AssignedTo = assignedTo;
Department = department;
CreatedBy = UsersService.LoggedInUser;
CreatedDateTime = DateTimeOffset.Now;
}
public Job()
{
}
}
public class Contact
{
[Key]
public int Id { get; set; }
public ContactType Type { get; set; }
public Title Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string TelephoneNumber { get; set; }
public bool IsDeleted { get; set; }
public DateTimeOffset? DeletedDateTime { get; internal set; }
public string FullName { get; private set; }
public Address? Address { get; set; }
public Contact(ContactType type, Title title, string firstName, string lastName, string emailAddress, string telephoneNumber)
{
Type = type;
Title = Title;
FirstName = firstName;
LastName = lastName;
EmailAddress = emailAddress;
TelephoneNumber = telephoneNumber;
IsDeleted = false;
Address = new Address();
}
public Contact()
{
}
public void AddAddress(string line1, string line2, string line3, string city, string county, string postCode, Country country)
{
Address = new Address(line1, line2, line3, city, county, postCode, country);
}
public void Update(ContactType type, Title title, string firstName, string lastName, string emailAddress, string telephoneNumber)
{
Type = type;
Title = title;
FirstName = firstName;
LastName = lastName;
EmailAddress = emailAddress;
TelephoneNumber = telephoneNumber;
}
public void Update(Contact contact)
{
Type = contact.Type;
Title = contact.Title;
FirstName = contact.FirstName;
LastName = contact.LastName;
EmailAddress = contact.EmailAddress;
TelephoneNumber = contact.TelephoneNumber;
Address = contact.Address;
}
public void Delete()
{
if(IsDeleted)
{
return;
}
IsDeleted = true;
DeletedDateTime = DateTimeOffset.UtcNow;
}
public void Restore()
{
IsDeleted = false;
DeletedDateTime = null;
}
}
public class ContactEntityTypeConfiguration : IEntityTypeConfiguration<Contact>
{
public void Configure(EntityTypeBuilder<Contact> builder)
{
builder.Property(x => x.FullName)
.HasComputedColumnSql($"CONCAT(FirstName, ' ', LastName)");
}
}
public class Department
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public Department()
{
}
public Department(string name)
{
Name = name;
}
}
public class DepartmentEntityTypeConfiguration : IEntityTypeConfiguration<Department>
{
public void Configure(EntityTypeBuilder<Department> builder)
{
builder.HasKey(x => x.Id);
}
}
class DatabaseContext : DbContext
{
public DbSet<Item> Inventory { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Tags> ItemTags { get; set; }
public DbSet<TaggedItems> TaggedItems { get; set; }
public DbSet<Contact> Contacts { get; set; }
public DbSet<SavedDirectory> Directorys { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Country> Countries { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var settings = ConfigurationManager.ConnectionStrings;
if (settings != null)
{
foreach (ConnectionStringSettings cs in settings)
{
optionsBuilder.UseMySQL(cs.ConnectionString);
}
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Item>(entity =>
{
entity.HasKey(e => e.PartNumber);
});
modelBuilder.Entity<Permission>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<CloudAccount>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<Tags>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(e => e.Id);
entity.HasMany(e => e.CloudAccounts);
});
modelBuilder.Entity<Contact>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<SavedDirectory>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<Job>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.Entity<Country>(entity =>
{
entity.HasKey(e => e.Id);
});
modelBuilder.ApplyConfiguration(new ContactEntityTypeConfiguration());
modelBuilder.ApplyConfiguration(new DepartmentEntityTypeConfiguration());
}
}

This occurs due to update in the database model please goto edmx file and import your database model again to it. this will help you. but if still you facing the same issue then Truncate your previous data in database and import the model again to configure the injectors. then run hope that will fix your error.

Related

AutoMapper ForMember and MapFrom is not executed

ForMember/MapFrom is somehow not executed whatever I try.
Those are the classes to be mapped;
public class ImageParams : IEntityParams, IImage, IOperatorFields
{
public ImageParams()
{
}
public ImageParams(string userId, string title, string description, string imagePath, bool profilePhoto)
{
UserId = userId;
Title = title;
Description = description;
ImagePath = imagePath;
ProfilePhoto = profilePhoto;
}
public ImageParams(int id, string userId, string title, string description, string imagePath, bool profilePhoto)
{
Id = id;
UserId = userId;
Title = title;
Description = description;
ImagePath = imagePath;
ProfilePhoto = profilePhoto;
}
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
[JsonProperty(PropertyName = "imagePath")]
public string ImagePath { get; set; }
[JsonProperty(PropertyName = "profilePhoto")]
public bool ProfilePhoto { get; set; }
public Status Status { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? LastModifiedDate { get; set; }
}
public interface IImage: IEntity, IHasStatus, IDateOperationFields
{
string UserId { get; set; }
string Title { get; set; }
string Description { get; set; }
string ImagePath { get; set; }
bool ProfilePhoto { get; set; }
}
public class Image: IImage
{
public int Id { get; set; }
public string UserId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ImagePath { get; set; }
public bool ProfilePhoto { get; set; }
public Status Status { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? LastModifiedDate { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public List<ReportImage> Reports { get; set; }
public List<Report> UserReports { get; set; }
}
I create the map as below;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Image, ImageParams>().ForMember(x => x.ImagePath, o => o.MapFrom(s => ImagePathFormatting(s.ImagePath))).ReverseMap();
}
private static string ImagePathFormatting(string imagePath)
{
var formattedImagePath = imagePath.Contains(AppSettingsProvider.PictureBaseUrl) ? imagePath : $"{AppSettingsProvider.PictureBaseUrl}/{imagePath}";
return formattedImagePath;
}
}
I register my profile as below;
services.AddAutoMapper(typeof(MappingProfile));
And I try to map as below;
public ImageParams GetProfileImage(string userId)
{
var image = Entities.FirstOrDefault(x => x.UserId == userId && x.ProfilePhoto && x.Status == Status.Active);
return _mapper.Map<ImageParams>(image);
}
I am sure the MappingProfile is executed successfully and mapping Image object to ImageParams object as well however ImagePathFormatting function is not called.
I have tried so many variations like instead of ImagePathFormatting I have used anonymous funtion. I have also tried using IValueResolver as below;
public class CustomResolver : IValueResolver<ImageParams, Image, string>
{
public string Resolve(ImageParams source, Image destination, string imagePath, ResolutionContext context)
{
return source.ImagePath.Contains(AppSettingsProvider.PictureBaseUrl) ? source.ImagePath : $"{AppSettingsProvider.PictureBaseUrl}/{source.ImagePath}";
}
}
Whatever I try, I cannot make MapFrom nor CustomResolver invoked.
Any help would be appreciated.
First solution:
Remove from class ImageParams any constructor except default without parameters:
public class ImageParams : IImage
{
public ImageParams()
{
}
//Other members.
}
Second solution:
Add DisableConstructorMapping():
var config = new MapperConfiguration(cfg => {
cfg.AddProfile(new MappingProfile());
cfg.DisableConstructorMapping();
}
);
var mapper = config.CreateMapper();
Third solution:
CreateMap<ImageParams, Image>()
.ForMember(x => x.ImagePath, o => o.MapFrom(s => ImagePathFormatting(s.ImagePath)))
.ReverseMap()
.ForMember(x => x.ImagePath, o => o.MapFrom(s => ImagePathFormatting(s.ImagePath)))
.ConstructUsing(x => new ImageParams());
Source 1
Source 2
I'd strongly suggest you use AutoMapper Execution Plan Tool tool to see exactly what automapper is doing when it runs the mapping.
Confident that will solve your problem.

Adding data to crosstable created by EF with AutoMapper

It is my first a many-to-many relation consisting of Team, User and TeamUser objects. In TeamController I mapped TeamForCreationDto to Team, but ICollection Members was empty. Some bug in CreateMap?Q1: How it should be combined to fill all property and tables by EF? Now I have "for" loop and there created/added TeamUser.
Q2: If I must fill both property AdminId and Admin?
A2: No, after adding Admin, property AdminId in DB thanks EF will find value automatically.
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public int AdminId { get; set; }
public User Admin { get; set; }
//public int[] MembersId { get; set; }
public ICollection<TeamUser> Members { get; set; }
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public ICollection<Team> TeamsAsAdmin { get; set; }
public ICollection<TeamUser> TeamsAsMember { get; set; }
}
public class TeamUser
{
public int TeamId { get; set; }
public Team Team { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
Relations between tables in ModelBuilder
builder.Entity<Team>()
.HasOne(t => t.Admin)
.WithMany(u => u.TeamsAsAdmin)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<TeamUser>()
.HasKey(tu => new { tu.TeamId, tu.UserId });
builder.Entity<TeamUser>()
.HasOne(tu => tu.User)
.WithMany(u => u.TeamsAsMember)
.HasForeignKey(tu => tu.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<TeamUser>()
.HasOne(tu => tu.Team)
.WithMany(t => t.Members)
.HasForeignKey(tu => tu.TeamId);
My CreateMap in AutoMapperProfiles()
CreateMap<TeamForCreationDto, Team>().ReverseMap().ForMember(u => u.MembersId, opt => opt.MapFrom(x => x.Members));
My TeamController.cs
public async Task<IActionResult> Create(int userId, TeamForCreationDto teamForCreationDto)
{
if (await _repoTeams.TeamExists(teamForCreationDto.Name))
return BadRequest("A team with this name already exists!");
var mappedTeam = _mapper.Map<Team>(teamForCreationDto);
//mappedTeam.AdminId = userId;
mappedTeam.Admin = await _repoUsers.GetUser(userId);
_repoTeams.Add(mappedTeam);
for (int i = 0; i < teamForCreationDto.MembersId.Length; i++)
{
TeamUser tm = new TeamUser();
tm.Team = mappedTeam;
tm.User = await _repoUsers.GetUser(teamForCreationDto.MembersId[i]);
_repoTeams.Add(tm);
}
await _repoTeams.SaveAll();
}
TeamForCreationDto.cs
public class TeamForCreationDto
{
int Id { get; set; }
public string Name { get; set; }
public string PhotoUrl { get; set; }
public int[] MembersId { get; set; }
}

Entity Framework Core and many to many relation

I try to migrate a small application to Entity Framework Core but I cant get the many to many relation to work.
First my Entities
public class Currency : Entity<int>, IMayHaveUser
{
public string Code { get; set; }
public string Name { get; set; }
public string Symbol { get; set; }
public virtual List<CountryCurrency> CountryCurrencies { get; set; }
public bool IsUserDefined => User != null;
[ForeignKey("UserId")]
public virtual User User { get; set; }
public long? UserId { get; set; }
}
public class Country : Entity<int>, IMayHaveUser
{
public string Iso2Code { get; set; }
public virtual ICollection<Era> Eras { get; set; }
public string Name { get; set; }
public virtual List<CountryCurrency> CountryCurrencies { get; set; }
[NotMapped]
public bool IsUserDefined => User != null;
[ForeignKey("UserId")]
public virtual User User { get; set; }
public long? UserId { get; set; }
}
public class CountryCurrency : Entity<Guid>
{
public int CountryId { get; set; }
public Country Country { get; set; }
public int CurrencyId { get; set; }
public Currency Currency { get; set; }
}
and my DbContext is
modelBuilder.Entity().HasKey(currency => new {
currency.CountryId, currency.CurrencyId });
modelBuilder.Entity()
.HasOne(pt => pt.Country)
.WithMany(p => p.CountryCurrencies)
.HasForeignKey(pt => pt.CountryId);
modelBuilder.Entity<CountryCurrency>()
.HasOne(pt => pt.Currency)
.WithMany(t => t.CountryCurrencies)
.HasForeignKey(pt => pt.CurrencyId);
now when I add a currency for example
Currency currency;
Country country;
CountryCurrency countryCurrency;
currency = new Currency();
currency.Id = i++;
currency.User = null;
currency.Code = "ETB";
currency.Name = "Ethiopian Birr";
currency.Symbol = "Br";
country =
this._context.Countries.FirstOrDefault(
country1 => country1.Iso2Code == "ET");
if (country != null)
{
currency.CountryCurrencies = new List<CountryCurrency>();
countryCurrency = new CountryCurrency();
countryCurrency.Country = country;
countryCurrency.Currency = currency;
currency.CountryCurrencies.Add(countryCurrency);
this.InitialCurrencies.Add(currency);
}
this._context.Currencies.Add(currency);
so when now I'm retrieve the data in my test I get this with this code
Country = context.Countries.Include(country => country.CountryCurrencies).First();
I can't get the currency the id is set but the property not...
You have also to include the currency entity, not just the join entity
Country = context.Countries
.Include(country => country.CountryCurrencies)
.ThenInclude(e => e.Currency)
.First();

Automapper, CustomMapping not loading fields of a virtual property

I have the following classes.
public class SomeModel
{
[Key]
public int Id { get; set; }
[Required]
public string UserId { get; set; }
public virtual User User { get; set; }
[Required]
public string Name { get; set; }
}
And:
public class SomeModelDetailsResponseModel : IMapFrom<SomeModel>, IHaveCustomMappings
{
public int Id { get; set; }
public string UserId { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public void CreateMappings(IConfiguration configuration)
{
configuration.CreateMap<SomeModel, SomeModelDetailsResponseModel>("name").AfterMap((b, r) =>
{
r.UserName = b.User.FirstName + b.User.LastName;
});
}
}
For some reason, when I project an IQueryable of SomeModel to an IQueryable of SomeModelDetailsResponseModel the UserName property turns out to be null.
Assuming these are you class definitions:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class SomeModel
{
public int Id { get; set; }
public string UserId { get; set; }
public virtual User User { get; set; }
public string Name { get; set; }
}
public class SomeModelDetailsResponseModel
{
public int Id { get; set; }
public string UserId { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
}
Solution 1
Do your mapping like this:
var config = new MapperConfiguration(
cfg =>
{
cfg.CreateMap<SomeModel, SomeModelDetailsResponseModel>().AfterMap((b, r) =>
{
r.UserName = b.User.FirstName + b.User.LastName;
});
});
var mapper = config.CreateMapper();
var response = mapper.Map<SomeModel, SomeModelDetailsResponseModel>(new SomeModel()
{
User = new User()
{
FirstName = "FN",
LastName = "LN"
}
});
Since you have your input as IQueryable<SomeModel> and you want to project it into IQueryable<SomeModelDetailsResponseModel>, then you can do this:
var result = q.Select(m => mapper.Map<SomeModel, SomeModelDetailsResponseModel>(m));
where q is your IQueryable<SomeModel> instance.
Solution 2
If you want to use ProjectTo<>, then initialize your mapper as the following:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SomeModel, SomeModelDetailsResponseModel>()
.ForMember(r => r.UserName, c => c.MapFrom(o => o.User.FirstName + o.User.LastName));
});
Then, do your projection as this:
var result = q.ProjectTo<SomeModelDetailsResponseModel>().ToArray();
Where q is your IQueryable<SomeModel>.

SimpleMembership with custom table using EF Code First

I was following HTML5 Line of business course on pluralsight to use Simple membership. I use the same code but getting error while Creating account using SimpleMembership with custom table. I cant find whats wrong in it. Can anybody help?
Invalid column name 'ConfirmationToken'. Invalid column name
'PasswordChangedDate'. Invalid column name
'PasswordFailuresSinceLastSuccess'.
Here is my code
Register Method:
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password,
new
{
FirstName = "Admin",
LastName = "Admin",
IsActive = true,
CreatedOn = DateTime.Now,
ModifiedOn = DateTime.Now
});
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
Entities:
public class Membership
{
public int UserId {get; set;}
public DateTime? CreateDate { get; set; }
public string ConfirmationTokeen { get; set; }
public bool? IsConfirmed { get; set; }
public DateTime? LastPaswordFailureDate { get; set; }
public int PasswordFailureSinceLastSuccess { get; set; }
public string Password { get; set; }
public DateTime? PasswordChangeDate { get; set; }
public string PasswordSalt { get; set; }
public string PasswordVerificationToken { get; set; }
public DateTime? PasswordVerificationTokenExpirationDate { get; set; }
}
public class OAuthMembership
{
public string Provider { get; set; }
public string ProviderUserId { get; set; }
public int UserId { get; set; }
}
public class Role
{
public int RoleId {get; set;}
public string RoleName {get; set;}
public ICollection<User> Users { get; set; }
public Role()
{
this.Users = new List<User>();
}
}
public class User : IAuditInfo
{
public int Id {get; set;}
public string Username{get; set;}
public string FirstName {get; set;}
public string LastName { get; set; }
public bool IsActive{get; set;}
public DateTime? CreatedOn { get; set; }
public DateTime? ModifiedOn {get; set;}
public ICollection<Role> Roles { get; set; }
public User()
{
this.Roles = new List<Role>();
}
}
Configuration
public UserConfiguration()
{
this.Property(p => p.Id).HasColumnOrder(0);
this.Property(p => p.Username).IsRequired().HasMaxLength(200);
this.Property(p => p.FirstName).IsOptional().HasMaxLength(100);
this.Property(p => p.LastName).IsOptional().HasMaxLength(100);
this.HasMany(a => a.Roles).WithMany(b => b.Users).Map(m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
m.ToTable("webpages_UsersInRoles");
});
}
public RoleConfiguration()
{
this.ToTable("webpages_Roles");
this.Property(p => p.RoleName).HasMaxLength(256).HasColumnType("nvarchar").IsRequired();
}
public OAuthMembershipConfiguration()
{
this.ToTable("webpages_OAuthMembership");
this.HasKey(k => new { k.Provider, k.ProviderUserId });
this.Property(p => p.Provider).HasMaxLength(128).HasColumnType("nvarchar").IsRequired();
this.Property(p => p.ProviderUserId).HasMaxLength(128).HasColumnType("nvarchar").IsRequired();
this.Property(p => p.UserId).IsRequired();
}
public MembershipConfiguration()
{
this.ToTable("webpages_Membership");
this.HasKey(s => s.UserId);
this.Property(p => p.UserId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(p => p.ConfirmationTokeen).HasMaxLength(128).HasColumnType("nvarchar");
this.Property(p => p.PasswordFailureSinceLastSuccess).IsRequired();
this.Property(p => p.Password).IsRequired().HasMaxLength(128).HasColumnType("nvarchar");
this.Property(p => p.PasswordSalt).IsRequired().HasMaxLength(128).HasColumnType("nvarchar");
this.Property(p => p.PasswordVerificationToken).HasMaxLength(128).HasColumnType("nvarchar");
}
Filter:
public SimpleMembershipInitializer()
{
DataContext context = new DataContext();
context.Database.Initialize(true);
try
{
if (!context.Database.Exists())
{
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
WebSecurity.InitializeDatabaseConnection(
Config.ConnectionStringName,
Config.UsersTableName,
Config.UsersPrimaryKeyColumnName,
Config.UsersUsernameColumnName,
autoCreateTables: true);
}
catch (Exception ex)
{
}
}

Categories