Default parameter values in primary constructor
I've created a data model class for use with Entity Framework Core, and I just had a question regarding my use of a primary constructor here. As you can see,
IsActive
and Prizes
are both being initialized to default values. Will this give me the same behaviour as if I'd used an old-school constructor? (i.e. public RaffleModel(...)
... you know the drill)
Additionally, are there any considerations I need to have specifically relating to how Entity Framework Core will interact with this class?
28 Replies
Just don't use the constructor and make the necessary properties
required
That's my approach, at least
(get rid of the virtual
while you're at it)
That said, here's what your code compiles to:Generates a ctor that directly assigns the values to the backing fields of your properties
As in, have no constructor at all? Is that good practice? Also, in some other models I may have fields that are nullable and not required, but still may be initialized upon instance creation - as well as having calculated values assigned upon instance creation. Would that throw a spanner in the works?
I've used the virtual keyword to opt for lazy-loading rather than eager-loading, from what I can see, that's pretty common practice no?
That's actually the first time I've seen compiled C# code (I'm still a newbie compared to most on here probably) 😄
Yes, as in no constructor at all. Making a property
required
(the C# keyword, not the [Required]
attribute) makes it so that it is... required, during construction
Also, this may be implied by the compiled code you sent (I'm not sure) - but is what I currently have valid or invalid?
It is valid, yes
Regarding lazy loading... no. Save yourself unnecessary database trips and load things eagerly
Load them eagerly, and load them in limited capacity with
.Select()
The only times I see lazy loading being used is:
1. My professor is 97 years old and told us that's how it was done in Visual Basic days
2. This is my first C# project ever I started learning yesterday from this musty 2001 bookTo be honest, I need to balance efficiency with ease of development/use. This is for a university project and they're not teaching any of the stack that I've chosen to use. I opted for lazy loading thinking that it would be easier to implement and require less code. With that in mind, would you still recommend eager loading?
It would be easier, yes, and it would require less code, yes
But it will produce worse and less performant code
Whether whoever's gonna be grading it will pay attention to that, dunno
Honestly, I'm not sure they will - the only part of the grading grid that directly references the code is:
"Code is elegant, suitably complex but navigable: it is exceptionally easy to tie up elements on the website to the code associated with them."
So with lazy-loading being a valid approach, although less performant, I think it should be totally fine. If it makes my life easier on a project like this with not a whole lot of time to work on, it's probably the best bet
Sure, as long as you remember to never use it in real life
Side note - until a few days ago, I'd only ever used C# for game development and this project has already highlighted just how little of the language you even need to touch for the majority of game dev (especially small projects)
Yep! I'm sure if I ever end up using it in a professional capacity, those sorts of things will probably be enforced anyway. Unfortunately, I only get to use C in a professional capacity at the moment
A bit of explanation:
will query the database with something like
while
will call
and then referencing the virtual property
will run a second query,
You end up with two database calls instead of one, and with more data fetched
Thanks for the explanation! It's 4am here so I'll look at it again after a snooze when my eyes aren't half shut, but I see your point and can see why it'd be bad practice in the real world. Ordinarily I'd always prefer to do things the best way, but tight deadlines means corner cutting has to happen somewhere unfortunately lol
Sure thing
regarding constructors, if you want to have a ctor for "human" use that validates domain rules for example, you can create a second private constructor for EFs use.
EF Core always needs to call a constructor.
Case 1 (Default Parameterless Constructor)
EF Core
- calls this default parameter constructor first.
- and then populates the properties.
Case 2 (Non Default Parameterless Constructor)
EF Core
- calls this non default parameter constructor first.
- and then populates the properties.
We usually do this to restrict outsiders from instantiate this class.
Case 3 (Matching Parameter Constructor)
EF Core
- calls this matching parameter constructor first.
- and then populates the remaining properties.
NOTES: If there is more than one matching constructor, EF Core selects one of them with its own algorithm.
I don't know the details of this algorithm to be honest.
Case 4 (Non Matching Parameter Constructor)
The following will throw exceptions because EF Core cannot instantiate this class.
So we need to add non default parameterless constructor.
Assuming you have added the non default parameterless constructor, EF Core
- calls the non default parameterless constructor first.
- and then populates the properties.
Just to be clear, are you saying it's essential to define a non-default parameterless constructor? On my little test project I was using before moving over to this, I had a simple model class shown below:
DbSet created in DB context:
Raffle service for CRUD operations, injected where relevant:
New raffle entry created with:
This was creating records in the database which I was also able to later retrieve. As far as I can tell, there were no issues with this, even though I did not define a parameterless constructor. Unless I have misunderstood you, aren't you saying that the above scenario would not work? If so, is there maybe some sort of issue that I haven't yet noticed, even though everything looked to be working okay?
Thanks for the detailed response, sorry if I've misunderstood it.
If possible always make the model class the dumbest (aka simplest) one.
Your current model below is not the dumbest because you have defined a matching parameter constructor. Yours belongs to case 3.
As you have a matching parameter constructor you don't need to specify non-default parameterless constructor.
To make your class the dumbest one, remove the parameter constructor as follows.
Now you can create the instance outside the class as
Just another case for the sake of completeness.
When
Id
is public init
and of type Guid
, you can instantiate the class with Id
too as follows.
We need non-default parameterless constructor for one of the following cases:
- Case 2: our model has no parameter constructor and we want to prevent the model class from being instantiated from outside the class.
- Case 4: our model has no matching parameter constructor.Okay, I see what you're saying - I assume the reasoning behind this approach is centred around flexibility and alignment with EF Core principles? Additionally, it sounds like it's preferable to separate the business logic from the entity model? I've had a go at taking on board what you've said and applied it one of my entity models. Message length limits so I'll send them in separate messages.
OLD MODEL:
NEW MODEL:
As per your advice, I've simplified the entity model and separated the business logic. I've made EntryId
private init
, and RaffleId init
. If I've understood you correctly, I believe this is a good change.
If I'm on the right track, I have a follow up question - is it better practice to directly create an EntryModel object in the relevant part of the code (i.e. in my Blazor application, create it in the code-behind the relevant page where an entry is made), or create an intermediate class that has functions like CreateUserEntry()
, CreateGuestEntry()
that act as sort of separated constructors for this entity model?
Again, thank you for the assistance - this is the sort of detail that we really don't have time to go into at university
Sorry, forgot to tag you in my response(Nullable)
Yes, the type has a question mark, it is nullable. Kinda redundant
EntryId
The Entry
prefix is redundant as well
In general, it's best that the database models don't leave the app. I'm not 100% sure how Blazor does it, but I would guess anything you bind to would be "leaving the app boundary", so I'd introduce DTOs there at leastMeh, stylistic choice - some argue it's pointless, some argue it's good practice. I'm personally somewhere in the middle but opted to use the table name prefix for this project
Okay - I'll have a look into it
Some people argue generic repositories on top of EF are a good idea
I believe it's a good idea to not listen to stupid people
Agreed, but having a stylistic preference over whether to use the table name prefix for an ID is not a case where either side is stupid. Both are perfectly valid, different SQL naming conventions use either option
The company I work for enforces the use of the table name prefix for the primary key as a standard
Sure it's a standard, but it's a stupid standard
Useless code for the sake of useless code
That's your opinion, there are valid arguments for both options
I don't see any arguments for
Model.ModelNameForsureItBelongsToTheModelAndItsANameAlsoItsVarcharOneHundred
But sure, agree to disagreeSure, if it ends up being that, that's probably rather stupid. However, using a unrealistic and ridiculously convoluted example is not a valid argument