What is the proper way to write POCO class classes in Entity Framework Core?

EF Core by default has a "code mentality", i.e. it is assumed that it is used in code mode, and even if a database approach is supported, it is described as nothing more than reverse engineering an existing database and creating its first code. I mean that the model (POCO classes) created in the code "manually" (first code) and generated from the database (using the Scaffold-DbContext command) should be identical.

Surprisingly, the official EF Core docs show significant differences. Here is an example of creating a model in code: https://ef.readthedocs.io/en/latest/platforms/aspnetcore/new-db.html And here is an example of reverse engineering from an existing database: https://ef.readthedocs.io/ en / latest / platforms / aspnetcore / existing-db.html

This is the entity class in the first case:

public class Blog { public int BlogId { get; set; } public string Url { get; set; } public List<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } } 

and this is the entity class in the second case:

 public partial class Blog { public Blog() { Post = new HashSet<Post>(); } public int BlogId { get; set; } public string Url { get; set; } public virtual ICollection<Post> Post { get; set; } } 

The first example is a very simple, very obvious POCO class. It is shown everywhere in the documentation (with the exception of examples generated from the database). The second example, however, has some additions:

  • The class is declared partial (although there is no other partial definition of it anywhere).
  • The navigation property is of type ICollection <T> , not just List <T>.
  • The navigation property is initialized with a new HashSet <T> () in the constructor. In the first code example, there is no such initialization.
  • The navigation property is declared virtual .
  • DbSet members in the generated class context are also virtual .

I tried to create a model from a database (the last tooling at the time of this writing), and it generates objects exactly as shown, so this is not an outdated documentation problem. Thus, the official tool generates different code, and the official documentation suggests writing another (trivial) code - without a partial class, virtual members, initialization of the structure, etc.

My question is how to try to build a model in code, how can I write my code? I like to use ICollection instead of List because it is more general, but other than that, I'm not sure if I need to follow MS documents or tools? Do I need to declare them virtual? Do I need to initialize them in the constructor? etc...

I know from the old EF times that the properties of virtual navigation allow lazy loading, but it is not even supported (yet) in EF Core, and I do not know other uses. Maybe this affects performance? Perhaps the tools are trying to generate future code, so when the lazy-load is implemented, the POCO classes and context will be able to support it? If so, can I omit them since I don’t need lazy loading (all data requests are encapsulated in a repo)?

In brief, please help me understand why there is a difference, and what style should I use when building a model in code?

+7
c # asp.net-core entity-framework entity-framework-core
source share
1 answer

I am trying to give a short answer to each topic you indicate.

  • partial classes are especially useful for code-generated code. Suppose you want to implement a derived property for a model only. For code, first you just do it wherever you want. For the database, the class file will be overwritten first if you update your model. Therefore, if you want to save the extension code, you want to put it in another file outside the managed model - here partial helps you extend the class without the need for automatic code setting.

  • ICollection definitely a good choice, even for code. Your database probably won't support a specific order anyway without a sort instruction.

  • Initializing the constructor is at least a convenience ... suppose you have either an empty collection of data on the database, or the property has not been loaded at all. Without a constructor, you must handle null cases explicitly at arbitrary points in the code. If you have to go with List or HashSet , then I can’t answer right now.

  • virtual allows you to create proxies for database objects, which can help with two things: Lazy Download, as you already mentioned, and change tracking. A proxy server object can track changes in virtual properties right away using the setter, while regular objects in the context should be checked for SaveChanges . In some cases, this may be more effective (not overall).

  • virtual IDbSet Context records simplify the design of test layout contexts for unit tests. Other use cases may also exist.

+11
source share

All Articles