EF Core ignores some of my mapping configuration - entity-framework-core

I have a class for my model and another class for mapping models with EF Core. When I add a migration, EF Core ignores my mapping configuration.
For example, the max length for all properties is max.
Here's my code:
Model class:
public class Product : EntityBase
{
public string Name { get; private set; }
public string Code { get; private set; }
public double UnitPrice { get; private set; }
public bool IsInStock { get; private set; }
public string ShortDescription { get; private set; }
public string Description { get; private set; }
public string Picture { get; private set; }
public string PictureAlt { get; private set; }
public string PictureTitle { get; private set; }
public long CategoryId { get; private set; }
public string Slug { get; private set; }
public string Keywords { get; private set; }
public string MetaDescription { get; private set; }
public ProductCategory Category { get; set; }
}
Mapping class:
public class ProductMapping:IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.ToTable("Products");
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).HasMaxLength(255).IsRequired();
builder.Property(x => x.Code).HasMaxLength(15).IsRequired();
builder.Property(x => x.ShortDescription).HasMaxLength(500).IsRequired();
builder.Property(x => x.Picture).HasMaxLength(1000);
builder.Property(x => x.PictureAlt).HasMaxLength(255);
builder.Property(x => x.PictureTitle).HasMaxLength(500);
builder.Property(x => x.Description).HasMaxLength(1000);
builder.Property(x => x.Keywords).HasMaxLength(500).IsRequired();
builder.Property(x => x.MetaDescription).HasMaxLength(150).IsRequired();
builder.Property(x => x.Slug).HasMaxLength(500).IsRequired();
builder.HasOne(x => x.Category)
.WithMany(x => x.Products)
.HasForeignKey(x => x.CategoryId);
}
}
Migration :
public partial class ProductAdd : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Products",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
Code = table.Column<string>(type: "nvarchar(max)", nullable: true),
UnitPrice = table.Column<double>(type: "float", nullable: false),
IsInStock = table.Column<bool>(type: "bit", nullable: false),
ShortDescription = table.Column<string>(type: "nvarchar(max)", nullable: true),
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
Picture = table.Column<string>(type: "nvarchar(max)", nullable: true),
PictureAlt = table.Column<string>(type: "nvarchar(max)", nullable: true),
PictureTitle = table.Column<string>(type: "nvarchar(max)", nullable: true),
CategoryId = table.Column<long>(type: "bigint", nullable: false),
Slug = table.Column<string>(type: "nvarchar(max)", nullable: true),
Keywords = table.Column<string>(type: "nvarchar(max)", nullable: true),
MetaDescription = table.Column<string>(type: "nvarchar(max)", nullable: true),
CreationDate = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Products", x => x.Id);
table.ForeignKey(
name: "FK_Products_ProductCategories_CategoryId",
column: x => x.CategoryId,
principalTable: "ProductCategories",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Products_CategoryId",
table: "Products",
column: "CategoryId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Products");
}
}
My context:
public class ShopContext:DbContext
{
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<Product> Products { get; set; }
public ShopContext(DbContextOptions<ShopContext> options):base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var assembly = typeof(ProductCategory).Assembly;
modelBuilder.ApplyConfigurationsFromAssembly(assembly);
base.OnModelCreating(modelBuilder);
}
}

Related

how to stop dotnet ef migrations adding from creating unnecessary additional fields

This is a barebones application with only one class: AppUser having only (Id, FirstName, LastName, Phone1, Phone2, KnownAs, EmailAddress, Question, Answer, and IsTrialMode.
There are no other tables!
DataContext is only:
public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<AppUser> Users { get; set; }
}
When I do a dotnet ef migrations add command, the migrations file contains:
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
FirstName = table.Column<string>(type: "nvarchar(max)", nullable: true),
LastName = table.Column<string>(type: "nvarchar(max)", nullable: true),
Phone1 = table.Column<string>(type: "nvarchar(max)", nullable: true),
Phone2 = table.Column<string>(type: "nvarchar(max)", nullable: true),
KnownAs = table.Column<string>(type: "nvarchar(max)", nullable: true),
EmailAddress = table.Column<string>(type: "nvarchar(max)", nullable: true),
Question = table.Column<string>(type: "nvarchar(max)", nullable: true),
Answer = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsTrialMode = table.Column<bool>(type: "bit", nullable: false),
UserName = table.Column<string>(type: "nvarchar(max)", nullable: true),
NormalizedUserName = table.Column<string>(type: "nvarchar(max)", nullable: true),
Email = table.Column<string>(type: "nvarchar(max)", nullable: true),
NormalizedEmail = table.Column<string>(type: "nvarchar(max)", nullable: true),
EmailConfirmed = table.Column<bool>(type: "bit", nullable: false),
PasswordHash = table.Column<string>(type: "nvarchar(max)", nullable: true),
SecurityStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "nvarchar(max)", nullable: true),
PhoneNumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "bit", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "bit", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
LockoutEnabled = table.Column<bool>(type: "bit", nullable: false),
AccessFailedCount = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
This is clearly NOT what I was expecting.
What is causing this; how to make it stop?
Thanks in advance.
"Chuck"
From your migration file, the additional properties seem to be the IdentityUser class default properties.
Be sure your AppUser does not extend IdentityUser or IdentityUser<T> class:
public class AppUser
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone1 { get; set; }
public string Phone2 { get; set; }
public string KnownAs { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
public string EmailAddress { get; set; }
public bool IsTrialMode { get; set; }
}

entity framework Core generate Wrong migrigation

i have model class like below
public class DIMAuthOffice
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Title { get; set; }
[StringLength(50)]
[Required]
public string UserNameManager { get; set; }
public int? ParentID { get; set; }
[ForeignKey(nameof(ParentID))]
public DIMAuthOffice OfficeParent { get; set; }
public List<DIMAuthOffice> OfficeChilds { get; set; } = new List<DIMAuthOffice>();
[ForeignKey(nameof(UserNameManager))]
public DIMAuthUser UserManager { get; set; }
public List<DIMAuthRole> DIMAuthRoles { get; set; } = new List<DIMAuthRole>();
public List<DIMAuthRolePostOffice> DIMAuthRolePostOffices { get; set; } = new List<DIMAuthRolePostOffice>();
}
but when i execute
Add-Migration
the generated File is :
migrationBuilder.CreateTable(
name: "DIMAuthOffices",
columns: table => new
{
ID = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: false),
UserNameManager = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
ParentID = table.Column<int>(type: "int", nullable: false),
UserManagerUserName = table.Column<string>(type: "nvarchar(50)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DIMAuthOffices", x => x.ID);
table.ForeignKey(
name: "FK_DIMAuthOffices_DIMAuthOffices_ParentID",
column: x => x.ParentID,
principalTable: "DIMAuthOffices",
principalColumn: "ID");
table.ForeignKey(
name: "FK_DIMAuthOffices_DIMAuthUsers_UserManagerUserName",
column: x => x.UserManagerUserName,
principalTable: "DIMAuthUsers",
principalColumn: "UserName");
});
wrong line is :
ParentID = table.Column(type: "int", nullable: false),
the generated File is Wrong and property ParentID set to nullable:false but this should be true ...
why ???
Doesn't repro for me on EF Core 6. eg
public class DIMAuthOffice
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Title { get; set; }
[StringLength(50)]
[Required]
public string UserNameManager { get; set; }
public int? ParentID { get; set; }
[ForeignKey(nameof(ParentID))]
public DIMAuthOffice OfficeParent { get; set; }
public List<DIMAuthOffice> OfficeChilds { get; set; } = new List<DIMAuthOffice>();
}
creates an initial migration of:
migrationBuilder.CreateTable(
name: "DIMAuthOffice",
columns: table => new
{
ID = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
UserNameManager = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
ParentID = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_DIMAuthOffice", x => x.ID);
table.ForeignKey(
name: "FK_DIMAuthOffice_DIMAuthOffice_ParentID",
column: x => x.ParentID,
principalTable: "DIMAuthOffice",
principalColumn: "ID");
});
I use FluentApi and writed this code in modelBuilder and fixed issue
modelBuilder.Entity<DIMAuthOffice>()
.HasOne(q => q.OfficeParent)
.WithMany(q => q.OfficeChilds)
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);

I can't retrieve data from many to many relationship to my ViewModel

I have the Map table that relates to the Day table in a many-to-many relationship.
So, I created the MapDay table according to the EF documentation.
The tables that are not related from many to many, I return without problems...
This is my controller, and I'm using viewmodels...
[HttpGet("getmaps")]
public async Task<ActionResult<IEnumerable<MapViewModel>>> GetMap()
{
var maps = _mapper.Map<IEnumerable<MapViewModel>>(await _mapRepository.GetMaps());
if(maps.Count() > 0) return Ok(maps);
return NotFound();
}
This is my MapViewModel:
public class MapViewModel : MainViewModel
{
public Guid UserId { get; set; }
public double Lng { get; set; }
public double Lat { get; set; }
public AddressViewModel Address { get; set; }
/* EF Relations */
public ItemMapViewModel ItemMap { get; set; }
public IEnumerable<MapDayViewModel> MapDay { get; set; } //testing
public IEnumerable<DayViewModel> Day { get; set; } // need this?
}
And my repository with the query:
public async Task<IEnumerable<Map>> GetMaps()
{
return await Db.Maps.AsNoTracking()
.Include(i => i.Address)
.Include(it => it.ItemMap)
.Include(mp => mp.MapDay).ThenInclude(mp => mp.Day)
.ToListAsync();
}
This is my result JSON:
[
{
"userId": "705cbdaf-86e9-4759-8f85-4fa6f3560726",
"lng": 0.0,
"lat": 0.0,
"address": {
"street": "st 123",
"number": "12",
"apt": "34",
"area": "AreaT",
"zip": "123456789",
"city": "Belo ",
"state": "ST",
"id": "ba3e7a68-63eb-4383-b980-14dea9615072"
},
"itemMap": {
"id": "353ccb80-b9fd-4469-9270-6a399ad37201",
"item": "Item1"
},
"mapDay": [
{
"mapId": "719da65c-42c9-4954-a750-e0b90e82461e",
"dayId": "5b444e8e-642f-4175-9329-9ef4a0f7aa87"
}
],
"day": null,
"id": "719da65c-42c9-4954-a750-e0b90e82461e"
}
]
Using aspnet core 3.1
So, what can I do to return the days of this map?
Using aspnet core 3.1 So, what can I do to return the days of this
map?
If you want to include Day data in the returned results of IEnumerable<MapViewModel> type, you really need to add public IEnumerable<DayViewModel> Day {get; set;} to MapViewModel.
Since you did not provide your automapper code, I will complete your requirements based on the code you gave.
Here I will simplify your model as follows:
Models:
public class Map
{
public int MapId { get; set; }
public string MapName { get; set; }
public ICollection<MapDay> MapDay { get; set; }
}
public class Day
{
public int DayId { get; set; }
public string DayName { get; set; }
public ICollection<MapDay> MapDay { get; set; }
}
public class MapDay
{
public int MapId { get; set; }
public Map Map { get; set; }
public int DayId { get; set; }
public Day Day { get; set; }
}
ViewModels:
public class MapViewModel
{
public int MapId { get; set; }
public string MapName { get; set; }
public IEnumerable<MapDayViewModel> MapDay { get; set; } //testing
public IEnumerable<DayViewModel> Day { get; set; } // need this?
}
public class DayViewModel
{
public int DayId { get; set; }
public string DayName { get; set; }
}
public class MapDayViewModel
{
public int MapId { get; set; }
public int DayId { get; set; }
}
Here is the detailed Automapper code to convert data from Models to ViewModels:
[HttpGet("getmaps")]
public async Task<ActionResult<IEnumerable<MapViewModel>>> GetMap()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Map, MapViewModel>()
.ForMember(dest => dest.MapId, opt => opt.MapFrom(s => s.MapId))
.ForMember(dest => dest.MapName, opt => opt.MapFrom(s => s.MapName))
.ForMember(x => x.MapDay, opt => opt.MapFrom(model => model.MapDay))
.ForMember(x => x.Day, opt => opt.MapFrom(model => model.MapDay.Select(x => x.Day)));
cfg.CreateMap<MapDay, MapDayViewModel>()
.ForMember(dest => dest.MapId, opt => opt.MapFrom(src => src.MapId))
.ForMember(dest => dest.DayId, opt => opt.MapFrom(src => src.DayId));
cfg.CreateMap<Day, DayViewModel>()
.ForMember(dest => dest.DayId, opt => opt.MapFrom(src => src.DayId))
.ForMember(dest => dest.DayName, opt => opt.MapFrom(src => src.DayName));
});
IMapper _mapper = config.CreateMapper();
var maps = _mapper.Map<IEnumerable<MapViewModel>>(await _mapRepository.GetMaps());
if (maps.Count() > 0)
return Ok(maps);
return NotFound();
}
Here is the test result:

Eager loading for one to one relationship in EF Core

Does anyone know if is there the problem with one to one relation in EF Core and Eager Loading or the problem is my poorly knowledge instead?
For example, if I have one simple one to one relation like this:
public class Author
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Biography Biography { get; set; }
}
public class Biography
{
public int Id { get; set; }
public string BiographyResume { get; set; }
public DateTime DateOfBirth { get; set; }
public string PlaceOfBirth { get; set; }
public string Nationality { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
public class OneDbContext : DbContext
{
public DbSet<Author> Authors { get; set; }
public OneDbContext(DbContextOptions<OneDbContext> options)
: base(options)
{
}
}
Here is the Migration:
public partial class CreateAuthorsTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Authors",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
FirstName = table.Column<string>(nullable: true),
LastName = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Authors", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Biography",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
BiographyResume = table.Column<string>(nullable: true),
DateOfBirth = table.Column<DateTime>(nullable: false),
PlaceOfBirth = table.Column<string>(nullable: true),
Nationality = table.Column<string>(nullable: true),
AuthorId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Biography", x => x.Id);
table.ForeignKey(
name: "FK_Biography_Authors_AuthorId",
column: x => x.AuthorId,
principalTable: "Authors",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Biography_AuthorId",
table: "Biography",
column: "AuthorId",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Biography");
migrationBuilder.DropTable(
name: "Authors");
}
}
And now, in Controller everything is fine without Eager Loading:
[Route("api/[controller]")]
public class AuthorsController : Controller
{
private readonly OneDbContext context;
public AuthorsController(OneDbContext context)
{
this.context = context;
}
[HttpGet]
public async Task<IEnumerable<Author>> GetAuthors()
{
return await context.Authors.ToListAsync();
}
}
But when I use Eager Loading it doesn't return anything.
[HttpGet]
public async Task<IEnumerable<Author>> GetAuthors()
{
return await context.Authors.Include(a => a.Biography).ToListAsync();
}
Tested with Postman:
https://localhost:44355/api/authors
for: return await context.Authors.ToListAsync();
It returns:
[
{
"id": 1,
"firstName": "Luka",
"lastName": "Jovic",
"biography": null
},
{
"id": 2,
"firstName": "Stefan",
"lastName": "Savic",
"biography": null
}
]
for: return await context.Authors.Include(a => a.Biography).ToListAsync();
It returns:
Could not get any response
There was an error connecting to https://localhost:44355/api/authors.
Why this might have happened:
The server couldn't send a response:
Ensure that the backend is working properly
Self-signed SSL certificates are being blocked:
Fix this by turning off 'SSL certificate verification' in Settings > General
Proxy configured incorrectly
Ensure that proxy is configured correctly in Settings > Proxy
Request timeout:
Change request timeout in Settings > General

An AddColumn was added to migration even though table is unchanged

I have the following model in a EF Core 3 project:
public class MemberModel
{
[Key]
public int Id { get; set; }
[Required, MaxLength(255)]
public string Name { get; set; }
[Required, MaxLength(30)]
public string Phone { get; set; }
[Required, MaxLength(255)]
public string Email { get; set; }
[Required, Display(Name="Company")]
public int CompanyId { get; set; }
public bool isDeleted { get; set; }
}
The CompanyId property was added only in March, and a migration, with timestamp 20200226063529 was done:
public partial class addedCompanyIdColumn : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "CompanyId",
table: "Members",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CompanyId",
table: "Members");
}
}
In the interim, I modified the table to add [Required] attribute to the fields, and I didn't apply any migrations.
Today, I added a migration to do update some other table, and the migration code included code that alters the Members table, adding a CompanyId column, and creating a Companies table that was already there:
migrationBuilder.AlterColumn<string>(
name: "Phone",
table: "Members",
maxLength: 30,
nullable: false,
oldClrType: typeof(string),
oldType: "nvarchar(30)",
oldMaxLength: 30,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "Members",
maxLength: 255,
nullable: false,
oldClrType: typeof(string),
oldType: "nvarchar(255)",
oldMaxLength: 255,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Email",
table: "Members",
maxLength: 255,
nullable: false,
oldClrType: typeof(string),
oldType: "nvarchar(255)",
oldMaxLength: 255,
oldNullable: true);
migrationBuilder.AddColumn<int>(
name: "CompanyId",
table: "Members",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateTable(
name: "Companies",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(maxLength: 255, nullable: true),
isDeleted = table.Column<bool>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Companies", x => x.Id);
});
So the update failed. I had to go to the database to delete the CompanyId column and the Companies table in order to proceed.
What could have caused this issue, and what should I look out for to avoid such outcomes in future?
Is there a requirement that any changes to the models, no matter how trivial, should be followed by a database update before any transactions are performed?