Reputation: 2325
I have an entity stored in DB, but some of this entity properties should be set from another configuration source.
Example:
public class Character {
public Item[] Inventory { get; set; }
}
...
public class Item {
public string Name { get; set; }
public ItemUISize UISize { get; set; } /* <- should not be stored in DB and also
should be set from another config source on entity creation (fetching from db) */
}
The problem is that I can't do it in Item
's constructor without creating a lot of abstractions, because this class is used by different libraries that have custom implementation of required configuration.
The only similar thing I've found is HasConversion
, but in that predicate root object isn't accessible.
How can I implement this?
Upvotes: 0
Views: 3006
Reputation: 238
Just to post the Fluent method to achieve this as well.
public class MyDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>.Ignore(c => c.UISize);
}
}
Depending on your usecase, and where the other configuration object is stored. What you're trying to achieve can be done in several ways. But if a property is set to be ignored. Obviously will not be specified when you query the DbSets. EF Core requires an empty constructor, though it doesn't have to be public.
public class Person
{
// It doesn't have to be public though. So you can still put some encapsulation
// on your application logic.
protected Person()
{
// EF Core will use this.
// But since name only exists in database. Shoesize won't be set.
}
// Descriptive constructor to use for instantiating outside of the Database logic.
public Person(string name)
{
Name = name;
ShoeSize = 1 // some value prefetched from configuration object ?
}
public string Name { get; private set; }
public int ShoeSize { get; private set; }
public void SpecifyShoeSize(int shoeSize)
{
ShoeSize = shoeSize;
}
}
public MyDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>.Ignore(person => person.ShoeSize);
}
}
If you want to be fully sure that you don't mess up other consumers of the class
public PersonwithShoeSize() : Person
{
public int ShoeSize {get; set;}
public PersonwithShoeSize(Person person, int shoeSize): base(person.Name)
{
ShoeSize = shoeSize;
}
}
// you can ignore entity classes entirely through the fluid api as well
public class MyDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<PersonWithShoeSize>();
}
}
link to article for example
https://www.learnentityframeworkcore.com/configuration/fluent-api/ignore-method
-------------------------- UPDATE -------------------------
For an automated way of doing this, after data have been fetched.
you could as you mentioned, try using value converters
.HasConversion(
fromprovider: ()=> { /* since property is not mapped, this line doesn't matter */ },
toprovider()=>{ /* set the not mapped property here */ })
I'm personally not really a big fan of this approach, because you'd then have to inject your other data store into the DbContext to be allowed to do this.
IF you are using EF Core 5, a much cleaner way would be to use the EF Core Interceptors and tagging your query so the interceptor can pick it up. Then you are able to use real DI and have logic separated out enough.
EF Core interceptors link: https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/interceptors
Upvotes: 1
Reputation: 1845
Entity framework generates public partial class, so you can put this code in a separate file and it will not be lost if you regenerate the EF class files. I would suggest you use this approach even if you use Code First because it separates the database elements from the helper elements.
public partial class Item
{
// Private data member.
private ItemUISize uiSize;
// Public property mediates access to private data member.
// NotMapped is used to indicate properties of an entity class for which
// there are no corresponding columns in the database.
[NotMapped]
public ItemUISize UISize {
// Lazy load UISize.
get {
if(this.uiSize == null) {
// load UI size here
}
return this.uiSize;
}
set {
this.uiSize = value;
}
}
}
Upvotes: 1