In my .net core 3.1 application I wanted to encapsulate properties in some entity:
public class Sample : AuditableEntity
{
public Sample(string name)
{
Name = name;
}
public int Id { get; }
public string Name { get; }
}
So I've removed all public setters and because of that somewhere in my code when I want to check whether such Sample already exists
_context.Samples.Any(r => r.Name == name)
that line causes the error: System.InvalidOperationException: 'No suitable constructor found for entity type 'Sample'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'name' in 'Sample(string name)'.'
.
So I've added to code empty construtor
public class Sample : AuditableEntity
{
public Sample() { } // Added empty constructor here
public Sample(string name)
{
Name = name;
}
public int Id { get; }
public string Name { get; }
}
and now that line causes error: System.InvalidOperationException: 'The LINQ expression 'DbSet<Sample>
.Any(s => s.Name == __name_0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
.
But if I'll add private set to Name
(or public) then everything works fine (even without empty constructor).
public class Sample : AuditableEntity
{
public Sample(string name)
{
Name = name;
}
public int Id { get; }
public string Name { get; private set; } // added setter, removed empty constructor
}
Can anyone explain me why this setter is required, for instance Id does not require that setter.
This has to do with Reflection as mentioned in the comments by Farhad Jabiyev.
EF cannot find the property, it is hidden from it. When you make it private set
, EF has access to it through reflection so everything works.
This can be done with a backing field https://docs.microsoft.com/en-us/ef/core/modeling/backing-field
From link above: Backing fields allow EF to read and/or write to a field rather than a property. This can be useful when encapsulation in the class is being used to restrict the use of and/or enhance the semantics around access to the data by application code, but the value should be read from and/or written to the database without using those restrictions/enhancements.
Meaning you would have to add a mapping to your backing field through fluent API like this.
modelBuilder.Entity<Sample >()
.Property(b => b.Name)
.HasField("_name"); // this would have to be the name of the backing field
For accessing backing fields of auto props you could use this-> Is it possible to access backing fields behind auto-implemented properties?
Me I would just add it myself, so it would be easier. So my class would look sth like this. You would need the mapping above and the mapping solves the problem that my property is private. Without the mapping this would fail.
public class Sample : AuditableEntity
{
private string _name; //now EF has access to this property with the Mapping added
public Sample(string name)
{
_name = name;
}
public int Id { get; }
public string Name => _name;
}
Please take a look at Lerman's approach-> https://www.youtube.com/watch?v=Z62cbp61Bb8&feature=youtu.be&t=1191
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With