Skip to content

Commit 54e90a4

Browse files
authored
Docs on the new enum/plugin config (#352)
Original commit: c12440d
1 parent c8c27ee commit 54e90a4

File tree

6 files changed

+135
-70
lines changed

6 files changed

+135
-70
lines changed

efcore/mapping/enum.html

+55-33
Original file line numberDiff line numberDiff line change
@@ -87,67 +87,89 @@ <h1 id="enum-type-mapping">Enum Type Mapping</h1>
8787

8888
<p>By default, any enum properties in your model will be mapped to database integers. EF Core 2.1 also allows you to map these to strings in the database with value converters.</p>
8989
<p>However, the Npgsql provider also allows you to map your CLR enums to <a href="https://www.postgresql.org/docs/current/static/datatype-enum.html">database enum types</a>. This option, unique to PostgreSQL, provides the best of both worlds: the enum is internally stored in the database as a number (minimal storage), but is handled like a string (more usable, no need to remember numeric values) and has type safety.</p>
90-
<h2 id="creating-your-database-enum">Creating your database enum</h2>
90+
<h2 id="setting-up-your-enum-with-ef">Setting up your enum with EF</h2>
91+
<div class="NOTE">
92+
<h5>Note</h5>
93+
<p>Enum mapping has changed considerably in EF 9.0.</p>
94+
</div>
95+
<p>If you're using EF 9.0 or above, simply call <code>MapEnum</code> inside your <code>UseNpgsql</code> invocation.</p>
96+
<div class="tabGroup" id="tabgroup_bHGHmlrG6S">
97+
<ul role="tablist">
98+
<li role="presentation">
99+
<a href="#tabpanel_bHGHmlrG6S_with-connection-string" role="tab" aria-controls="tabpanel_bHGHmlrG6S_with-connection-string" data-tab="with-connection-string" tabindex="0" aria-selected="true">With a connection string</a>
100+
</li>
101+
<li role="presentation">
102+
<a href="#tabpanel_bHGHmlrG6S_with-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S_with-datasource" data-tab="with-datasource" tabindex="-1">With an external NpgsqlDataSource</a>
103+
</li>
104+
</ul>
105+
<section id="tabpanel_bHGHmlrG6S_with-connection-string" role="tabpanel" data-tab="with-connection-string">
106+
107+
<p>If you're passing a connection string to <code>UseNpgsql</code>, simply add the <code>MapEnum</code> call as follows:</p>
108+
<pre><code class="lang-c#">builder.Services.AddDbContext&lt;MyContext&gt;(options =&gt; options.UseNpgsql(
109+
&quot;&lt;connection string&gt;&quot;,
110+
o =&gt; o.MapEnum&lt;Mood&gt;(&quot;mood&quot;)));
111+
</code></pre>
112+
<p>This configures all aspects of Npgsql to use your <code>Mood</code> enum - both at the EF and the lower-level Npgsql layer - and ensures that the enum is created in the database in EF migrations.</p>
113+
</section>
114+
<section id="tabpanel_bHGHmlrG6S_with-datasource" role="tabpanel" data-tab="with-datasource" aria-hidden="true" hidden="hidden">
115+
116+
<p>If you're creating an external NpgsqlDataSource and passing it to <code>UseNpgsql</code>, you must make sure to map your enum on that data independently of the EF-level setup:</p>
117+
<pre><code class="lang-c#">var dataSourceBuilder = new NpgsqlDataSourceBuilder(&quot;&lt;connection string&gt;&quot;);
118+
dataSourceBuilder.MapEnum&lt;Mood&gt;();
119+
var dataSource = dataSourceBuilder.Build();
120+
121+
builder.Services.AddDbContext&lt;MyContext&gt;(options =&gt; options.UseNpgsql(
122+
dataSource,
123+
o =&gt; o.MapEnum&lt;Mood&gt;(&quot;mood&quot;)));
124+
</code></pre>
125+
</section>
126+
</div>
127+
<h3 id="older-ef-versions">Older EF versions</h3>
128+
<p>On versions of EF prior to 9.0, enum setup is more involved and consists of several steps; enum mapping has to be done at the lower-level Npgsql layer, and also requires explicit configuration in the EF model for creation in the database via migrations.</p>
129+
<h4 id="creating-your-database-enum">Creating your database enum</h4>
91130
<p>First, you must specify the PostgreSQL enum type on your model, just like you would with tables, sequences or other databases objects:</p>
92131
<pre><code class="lang-c#">protected override void OnModelCreating(ModelBuilder builder)
93132
=&gt; builder.HasPostgresEnum&lt;Mood&gt;();
94133
</code></pre>
95134
<p>This causes the EF Core provider to create your enum type, <code>mood</code>, with two labels: <code>happy</code> and <code>sad</code>. This will cause the appropriate migration to be created.</p>
96-
<p>If you are using <code>context.Database.Migrate()</code> to create your enums, you need to instruct Npgsql to reload all types after applying your migrations:</p>
97-
<pre><code class="lang-c#">await context.Database.MigrateAsync(token);
98-
99-
if (context.Database.GetDbConnection() is NpgsqlConnection npgsqlConnection)
100-
{
101-
await npgsqlConnection.OpenAsync(token);
102-
try
103-
{
104-
await npgsqlConnection.ReloadTypesAsync();
105-
}
106-
finally
107-
{
108-
await npgsqlConnection.CloseAsync();
109-
}
110-
}
111-
</code></pre>
112-
<h2 id="mapping-your-enum">Mapping your enum</h2>
135+
<h4 id="mapping-your-enum">Mapping your enum</h4>
113136
<p>Even if your database enum is created, Npgsql has to know about it, and especially about your CLR enum type that should be mapped to it:</p>
114-
<div class="tabGroup" id="tabgroup_bHGHmlrG6S">
137+
<div class="tabGroup" id="tabgroup_bHGHmlrG6S-1">
115138
<ul role="tablist">
116139
<li role="presentation">
117-
<a href="#tabpanel_bHGHmlrG6S_with-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S_with-datasource" data-tab="with-datasource" tabindex="0" aria-selected="true">NpgsqlDataSource</a>
140+
<a href="#tabpanel_bHGHmlrG6S-1_with-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S-1_with-datasource" data-tab="with-datasource" tabindex="0" aria-selected="true">NpgsqlDataSource</a>
118141
</li>
119142
<li role="presentation">
120-
<a href="#tabpanel_bHGHmlrG6S_without-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S_without-datasource" data-tab="without-datasource" tabindex="-1">Without NpgsqlDatasource</a>
143+
<a href="#tabpanel_bHGHmlrG6S-1_without-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S-1_without-datasource" data-tab="without-datasource" tabindex="-1">Without NpgsqlDatasource</a>
121144
</li>
122145
</ul>
123-
<section id="tabpanel_bHGHmlrG6S_with-datasource" role="tabpanel" data-tab="with-datasource">
146+
<section id="tabpanel_bHGHmlrG6S-1_with-datasource" role="tabpanel" data-tab="with-datasource">
124147

125148
<p>Since version 7.0, NpgsqlDataSource is the recommended way to use Npgsql. When using NpgsqlDataSource, map your enum when building your data source:</p>
126-
<pre><code class="lang-c#">// Call UseNodaTime() when building your data source:
149+
<pre><code class="lang-c#">// Call MapEnum() when building your data source:
127150
var dataSourceBuilder = new NpgsqlDataSourceBuilder(/* connection string */);
128151
dataSourceBuilder.MapEnum&lt;Mood&gt;();
129152
var dataSource = dataSourceBuilder.Build();
130153

131154
builder.Services.AddDbContext&lt;MyContext&gt;(options =&gt; options.UseNpgsql(dataSource));
132155
</code></pre>
133156
</section>
134-
<section id="tabpanel_bHGHmlrG6S_without-datasource" role="tabpanel" data-tab="without-datasource" aria-hidden="true" hidden="hidden">
157+
<section id="tabpanel_bHGHmlrG6S-1_without-datasource" role="tabpanel" data-tab="without-datasource" aria-hidden="true" hidden="hidden">
135158

136159
<p>Since version 7.0, NpgsqlDataSource is the recommended way to use Npgsql. However, if you're not yet using NpgsqlDataSource, map enums by adding the following code, <em>before</em> any EF Core operations take place. An appropriate place for this is in the static constructor on your DbContext class:</p>
137160
<pre><code class="lang-c#">static MyDbContext()
138161
=&gt; NpgsqlConnection.GlobalTypeMapper.MapEnum&lt;Mood&gt;();
139162
</code></pre>
140-
</section>
141-
</div>
142-
143-
<p>This code lets Npgsql know that your CLR enum type, <code>Mood</code>, should be mapped to a database enum called <code>mood</code>. Note that if your enum is in a custom schema (not <code>public</code>), you must specify that schema in the call to <code>MapEnum</code>.</p>
144-
<p>If you're curious as to inner workings, this code maps the enum with the ADO.NET provider - <a href="http://www.npgsql.org/doc/types/enums_and_composites.html">see here for the full docs</a>. When the Npgsql EF Core first initializes, it calls into the ADO.NET provider to get all mapped enums, and sets everything up internally at the EF Core layer as well.</p>
145163
<div class="NOTE">
146164
<h5>Note</h5>
147165
<p>If you have multiple context types, all <code>MapEnum</code> invocations must be done before <em>any</em> of them is used; this means that the code cannot be in your static constructors, but must be moved to the program start.</p>
148166
</div>
167+
</section>
168+
</div>
169+
170+
<p>This code lets Npgsql know that your CLR enum type, <code>Mood</code>, should be mapped to a database enum called <code>mood</code>. Note that if your enum is in a custom schema (not <code>public</code>), you must specify that schema in the call to <code>MapEnum</code>.</p>
149171
<h2 id="using-enum-properties">Using enum properties</h2>
150-
<p>Once your enum is mapped and created in the database, you can use your CLR enum type just like any other property:</p>
172+
<p>Once your enum is properly set up with EF, you can use your CLR enum type just like any other property:</p>
151173
<pre><code class="lang-c#">public class Blog
152174
{
153175
public int Id { get; set; }
@@ -166,12 +188,12 @@ <h2 id="using-enum-properties">Using enum properties</h2>
166188
</code></pre>
167189
<h2 id="altering-enum-definitions">Altering enum definitions</h2>
168190
<p>The Npgsql provider only allow adding new values to existing enums, and the appropriate migrations will be automatically created as you add values to your CLR enum type. However, PostgreSQL itself doesn't support removing enum values (since these may be in use), and while renaming values is supported, it isn't automatically done by the provider to avoid using unreliable detection heuristics. Renaming an enum value can be done by including <a href="https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/managing?tabs=dotnet-core-cli#arbitrary-changes-via-raw-sql">raw SQL</a> in your migrations as follows:</p>
169-
<pre><code class="lang-c#">migrationBuilder.Sql(@&quot;ALTER TYPE mood RENAME VALUE 'happy' TO 'thrilled';&quot;);
191+
<pre><code class="lang-c#">migrationBuilder.Sql(&quot;ALTER TYPE mood RENAME VALUE 'happy' TO 'thrilled';&quot;);
170192
</code></pre>
171193
<p>As always, test your migrations carefully before running them on production databases.</p>
172194
<h2 id="scaffolding-from-an-existing-database">Scaffolding from an existing database</h2>
173-
<p>If you're creating your model from an existing database, the provider will recognize enums in your database, and scaffold the appropriate <code>HasPostgresEnum()</code> lines in your model. However, the scaffolding process has no knowledge of your CLR type, and will therefore skip your enum columns (warnings will be logged). You will have to create the CLR type, add the global mapping and add the properties to your entities.</p>
174-
<p>In the future it may be possible to scaffold the actual enum type (and with it the properties), but this doesn't happen at the moment.</p>
195+
<p>If you're creating your model from an existing database, the provider will recognize enums in your database, and scaffold the appropriate <code>HasPostgresEnum()</code> lines in your model. However, the scaffolding process has no knowledge of your CLR type, and will therefore skip your enum columns (warnings will be logged). You will have to create the CLR type and perform the proper setup as described above.</p>
196+
<p>In the future it may be possible to scaffold the actual enum type (and with it the properties), but this isn't supported at the moment.</p>
175197
</article>
176198
</div>
177199

efcore/mapping/nodatime.html

+24-14
Original file line numberDiff line numberDiff line change
@@ -94,40 +94,50 @@ <h2 id="what-is-nodatime">What is NodaTime</h2>
9494
<li>NodaTime types can fully represent PostgreSQL's microsecond precision, and can represent dates outside the BCL's date limit (1AD-9999AD).</li>
9595
</ul>
9696
<h2 id="setup">Setup</h2>
97-
<p>To set up the NodaTime plugin, add the <a href="https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime">Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime nuget</a> to your project. Then, configure NodaTime as follows:</p>
97+
<p>To set up the NodaTime plugin, add the <a href="https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime">Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime nuget</a> to your project. Then, configure the NodaTime plugin as follows:</p>
9898
<div class="tabGroup" id="tabgroup_bHGHmlrG6S">
9999
<ul role="tablist">
100100
<li role="presentation">
101-
<a href="#tabpanel_bHGHmlrG6S_with-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S_with-datasource" data-tab="with-datasource" tabindex="0" aria-selected="true">NpgsqlDataSource</a>
101+
<a href="#tabpanel_bHGHmlrG6S_ef9-with-connection-string" role="tab" aria-controls="tabpanel_bHGHmlrG6S_ef9-with-connection-string" data-tab="ef9-with-connection-string" tabindex="0" aria-selected="true">EF 9.0, with a connection string</a>
102102
</li>
103103
<li role="presentation">
104-
<a href="#tabpanel_bHGHmlrG6S_without-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S_without-datasource" data-tab="without-datasource" tabindex="-1">Without NpgsqlDatasource</a>
104+
<a href="#tabpanel_bHGHmlrG6S_with-datasource" role="tab" aria-controls="tabpanel_bHGHmlrG6S_with-datasource" data-tab="with-datasource" tabindex="-1">With an external NpgsqlDataSource</a>
105+
</li>
106+
<li role="presentation">
107+
<a href="#tabpanel_bHGHmlrG6S_legacy-with-connection-string" role="tab" aria-controls="tabpanel_bHGHmlrG6S_legacy-with-connection-string" data-tab="legacy-with-connection-string" tabindex="-1">Older EF versions, with a connection string</a>
105108
</li>
106109
</ul>
107-
<section id="tabpanel_bHGHmlrG6S_with-datasource" role="tabpanel" data-tab="with-datasource">
110+
<section id="tabpanel_bHGHmlrG6S_ef9-with-connection-string" role="tabpanel" data-tab="ef9-with-connection-string">
111+
112+
<p>If you're passing a connection string to <code>UseNpgsql</code>, simply add the <code>UseNodaTime</code> call as follows:</p>
113+
<pre><code class="lang-c#">builder.Services.AddDbContext&lt;MyContext&gt;(options =&gt; options.UseNpgsql(
114+
&quot;&lt;connection string&gt;&quot;,
115+
o =&gt; o.UseNodaTime()));
116+
</code></pre>
117+
<p>This configures all aspects of Npgsql to use the NodaTime plugin - both at the EF and the lower-level Npgsql layer.</p>
118+
</section>
119+
<section id="tabpanel_bHGHmlrG6S_with-datasource" role="tabpanel" data-tab="with-datasource" aria-hidden="true" hidden="hidden">
108120

109-
<p>Since version 7.0, NpgsqlDataSource is the recommended way to use Npgsql. When using NpsgqlDataSource, NodaTime currently has to be configured twice - once at the EF level, and once at the underlying ADO.NET level (there are plans to improve this):</p>
110-
<pre><code class="lang-c#">// Call UseNodaTime() when building your data source:
111-
var dataSourceBuilder = new NpgsqlDataSourceBuilder(/* connection string */);
121+
<p>If you're creating an external NpgsqlDataSource and passing it to <code>UseNpgsql</code>, you must call <code>UseNodaTime</code> on your NpgsqlDataSourceBuilder independently of the EF-level setup:</p>
122+
<pre><code class="lang-c#">var dataSourceBuilder = new NpgsqlDataSourceBuilder(&quot;&lt;connection string&gt;&quot;);
112123
dataSourceBuilder.UseNodaTime();
113124
var dataSource = dataSourceBuilder.Build();
114125

115-
// Then, when configuring EF Core with UseNpgsql(), call UseNodaTime() there as well:
116-
builder.Services.AddDbContext&lt;MyContext&gt;(options =&gt;
117-
options.UseNpgsql(dataSource, o =&gt; o.UseNodaTime()));
126+
builder.Services.AddDbContext&lt;MyContext&gt;(options =&gt; options.UseNpgsql(
127+
dataSource,
128+
o =&gt; o.UseNodaTime()));
118129
</code></pre>
119130
</section>
120-
<section id="tabpanel_bHGHmlrG6S_without-datasource" role="tabpanel" data-tab="without-datasource" aria-hidden="true" hidden="hidden">
131+
<section id="tabpanel_bHGHmlrG6S_legacy-with-connection-string" role="tabpanel" data-tab="legacy-with-connection-string" aria-hidden="true" hidden="hidden">
121132

122-
<p>Since version 7.0, NpgsqlDataSource is the recommended way to use Npgsql. However, if you're not yet using NpgsqlDataSource, configure NodaTime as follows:</p>
123-
<pre><code class="lang-c#">// Configure NodaTime at the ADO.NET level.
133+
<pre><code class="lang-c#">// Configure UseNodaTime at the ADO.NET level.
124134
// This code must be placed at the beginning of your application, before any other Npgsql API is called; an appropriate place for this is in the static constructor on your DbContext class:
125135
static MyDbContext()
126136
=&gt; NpgsqlConnection.GlobalTypeMapper.UseNodaTime();
127137

128138
// Then, when configuring EF Core with UseNpgsql(), call UseNodaTime():
129139
builder.Services.AddDbContext&lt;MyContext&gt;(options =&gt;
130-
options.UseNpgsql(/* connection string */, o =&gt; o.UseNodaTime()));
140+
options.UseNpgsql(&quot;&lt;connection string&gt;&quot;, o =&gt; o.UseNodaTime()));
131141
</code></pre>
132142
</section>
133143
</div>

0 commit comments

Comments
 (0)