C
C#12mo ago
Tvde1

Not mapping an EF Core DB-First enum property

I have scaffolded my Db Context. My entity has a
[Column("status")]
public string _status { get; set; }
[Column("status")]
public string _status { get; set; }
I am making a partial class which is:
public partial class Host
{
[NotMapped]
public HostStatus Status
{
get => Enum.Parse<HostStatus>(this._status);
set => this._status = value.ToString().ToLower();
}
}
public partial class Host
{
[NotMapped]
public HostStatus Status
{
get => Enum.Parse<HostStatus>(this._status);
set => this._status = value.ToString().ToLower();
}
}
Yet, when I do _context.Hosts.ToList(), it does attempt to select a column named "Status". How do I prevent this? The NotMapped attribute does not seem to be working.
11 Replies
Tvde1
Tvde112mo ago
[JsonIgnore] also doesn't work I tried something like
modelBuilder.Entity<Host>()
.Property(x => x.Status)
.HasColumnName("status")
.HasConversion<string>(x => x.ToString().ToLower(), x => Enum.Parse<HostStatus>(x));
modelBuilder.Entity<Host>()
.Property(x => x.Status)
.HasColumnName("status")
.HasConversion<string>(x => x.ToString().ToLower(), x => Enum.Parse<HostStatus>(x));
But this gives me errors that the column status is referenced twice for the time being I'm making a script that edits the generated files + adds conversion
MutableString
MutableString12mo ago
to me is unclear what behavior you would like from _context.Hosts.ToList()
Tvde1
Tvde112mo ago
to create a list of Hosts, including all properties in the entity. Currently the generated query also includes [Status] which does not exist as a column. I want EF to not include this fake column in the query generation
MutableString
MutableString12mo ago
you can also use simply .HasConversion<string>()); i believe because enum is one of the basic supported types even if there is a EnumToStringConverter<> you don't need it (ok the name is "pre-defined conversions")
PixxelKick
PixxelKick12mo ago
Typically the approach I use for this is having a seperation of my database models versus my "view" models, and perform a .Select to translate between the two. This translation logic I would put on the view model instead with its Read/Write methods I would have the following on HostModel
public static Expression<Func<Host, HostViewModel>> ToModel(YourDbContext db, string userId) {
return model => new() { ... };
}
public static Expression<Func<Host, HostViewModel>> ToModel(YourDbContext db, string userId) {
return model => new() { ... };
}
and
public async Task WriteAsync(YourDbContext db, string userId, Host model) { .... }
public async Task WriteAsync(YourDbContext db, string userId, Host model) { .... }
And then you can do stuff like
Hosts.Select(HostViewModel.ToModel(db, userId)).ToList();
Hosts.Select(HostViewModel.ToModel(db, userId)).ToList();
or
viewModel.Write(db, userId, model);
viewModel.Write(db, userId, model);
MutableString
MutableString12mo ago
yeah but enums are supported by ef
Tvde1
Tvde112mo ago
That could work, though modifying entities isn't that easy then anymore
PixxelKick
PixxelKick12mo ago
its considered a pretty common practice, since you typically dont want to expose your entire entity to the front end. Its typically better to have an intermediary "view model" or "DTO" that acts as the translator between FE <-> BE. This serves the role of explicitly stating what columns you select and whatnot, and tightens up the scope of what specifically is exposed to the FE and what the FE can modify, and also allows you to use values that might not exist on the db at all. For example you may have FirstName and LastName as seperate columns on the BE, but you expose just a simple readonly FullName on your DTO to the front end, etc etc
Tvde1
Tvde112mo ago
I do have separate DTOs for the output of my endpoints, but that's done on the final line of my service methods. I don't think it's valuable to convert entities into models and then models into entities again. This service is pretty small I settled on adding this to my powershell script that re-creates the db scaffold:
@("Setup", "Host") | ForEach-Object {
$name = $_
Get-ChildItem -Path **\Models\$name.cs -Recurse | ForEach-Object {
(Get-Content $_.FullName) -replace 'public string Status { get; set; } = null!;', ('public Domain.Shared.' + $name + 'Status Status { get; set; }') | Set-Content $_.FullName
}
}
@("Setup", "Host") | ForEach-Object {
$name = $_
Get-ChildItem -Path **\Models\$name.cs -Recurse | ForEach-Object {
(Get-Content $_.FullName) -replace 'public string Status { get; set; } = null!;', ('public Domain.Shared.' + $name + 'Status Status { get; set; }') | Set-Content $_.FullName
}
}
Pretty nifty replaces
public string Status { get; set; } = null!;
public string Status { get; set; } = null!;
with
public Domain.Shared.[X]Status Status { get; set; }
public Domain.Shared.[X]Status Status { get; set; }
with the name of the filename substituted to [X] and then in the OnModelCreatingPartial, I add
modelBuilder.Entity<Host>()
.Property(x => x.Status)
.HasConversion<string>(x => x.ToString().ToLower(), x => Enum.Parse<HostStatus>(x, true));
modelBuilder.Entity<Host>()
.Property(x => x.Status)
.HasConversion<string>(x => x.ToString().ToLower(), x => Enum.Parse<HostStatus>(x, true));
might eventually make it reflection based, to look for properties named Status of type enum, and add this automatically, but for now I've hardcoded it for two entities
PixxelKick
PixxelKick12mo ago
just for the record, you do know that entity framework just straight up supports enums as a property out of the box right?
Tvde1
Tvde112mo ago
that's what my replace script does it changes the generated string into an enum my entities are generated DB first by dotnet ef dbcontext scaffold
Want results from more Discord servers?
Add your server