Skip to content

Commit

Permalink
Improvements to value generation docs (#2998)
Browse files Browse the repository at this point in the history
* Improvements to the value generation page
* Create new page on SQL Server value generation, with information
  on IDENTITY, seed/increment, etc.
* Delete "explicit values for generated properties" page, moving
  relevant content to the new SQL Server page and to the fixed-up
  generated properties page.

Fixes #2980
Fixes #2947
Closes #2999

Co-authored-by: Andriy Svyryd <AndriySvyryd@users.noreply.github.com>
  • Loading branch information
roji and AndriySvyryd committed Jan 13, 2021
1 parent f5c5ead commit 5a60b35
Show file tree
Hide file tree
Showing 17 changed files with 238 additions and 323 deletions.
4 changes: 1 addition & 3 deletions .markdownlint.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"default": true,
"MD013": false,
"MD024": {
"allow_different_nesting": true
},
"MD024": false,
"MD025": { "front_matter_title": "" },
"MD028": false,
"MD033": false,
Expand Down
5 changes: 5 additions & 0 deletions .openpublishing.redirection.json
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,11 @@
"source_path": "entity-framework/core/miscellaneous/context-pooling.md",
"redirect_url": "/ef/core/performance/advanced-performance-topics#dbcontext-pooling",
"redirect_document_id": false
},
{
"source_path": "core/saving/explicit-values-generated-properties.md",
"redirect_url": "/ef/core/modeling/generated-properties#overriding-values",
"redirect_document_id": false
}
]
}
2 changes: 1 addition & 1 deletion entity-framework/core/change-tracking/explicit-tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ Post {Id: 2} Unchanged
This is exactly the same end-state as the previous example that used explicit key values.

> [!TIP]
> An explicit key value can still be set even when using generated key values. EF Core will then attempt to insert using this key value. Some database configurations, including SQL Server with Identity columns, do not support such inserts and will throw.
> An explicit key value can still be set even when using generated key values. EF Core will then attempt to insert using this key value. Some database configurations, including SQL Server with Identity columns, do not support such inserts and will throw ([see these docs for a workaround](xref:core/providers/sql-server/value-generation#inserting-explicit-values-into-identity-columns)).
## Attaching existing entities

Expand Down
127 changes: 75 additions & 52 deletions entity-framework/core/modeling/generated-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,48 @@
title: Generated Values - EF Core
description: How to configure value generation for properties when using Entity Framework Core
author: AndriySvyryd
ms.date: 11/06/2019
ms.date: 1/10/2021
uid: core/modeling/generated-properties
---

# Generated Values

## Value generation patterns
Database columns can have their values generated in various ways: primary key columns are frequently auto-incrementing integers, other columns have default or computed values, etc. This page details various patterns for configuration value generation with EF Core.

There are three value generation patterns that can be used for properties:
## Default values

* No value generation
* Value generated on add
* Value generated on add or update
On relational databases, a column can be configured with a default value; if a row is inserted without a value for that column, the default value will be used.

### No value generation
You can configure a default value on a property:

No value generation means that you will always supply a valid value to be saved to the database. This valid value must be assigned to new entities before they are added to the context.
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/DefaultValue.cs?name=DefaultValue&highlight=5)]

### Value generated on add
You can also specify a SQL fragment that is used to calculate the default value:

Value generated on add means that a value is generated for new entities.
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/DefaultValueSql.cs?name=DefaultValueSql&highlight=5)]

Depending on the database provider being used, values may be generated client side by EF or in the database. If the value is generated by the database, then EF may assign a temporary value when you add the entity to the context. This temporary value will then be replaced by the database generated value during `SaveChanges()`.
## Computed columns

If you add an entity to the context that has a value assigned to the property, then EF will attempt to insert that value rather than generating a new one. A property is considered to have a value assigned if it is not assigned the CLR default value (`null` for `string`, `0` for `int`, `Guid.Empty` for `Guid`, etc.). For more information, see [Explicit values for generated properties](xref:core/saving/explicit-values-generated-properties).
On most relational databases, a column can be configured to have its value computed in the database, typically with an expression referring to other columns:

> [!WARNING]
> How the value is generated for added entities will depend on the database provider being used. Database providers may automatically set up value generation for some property types, but others may require you to manually set up how the value is generated.
>
> For example, when using SQL Server, values will be automatically generated for `GUID` properties (using the SQL Server sequential GUID algorithm). However, if you specify that a `DateTime` property is generated on add, then you must set up a way for the values to be generated. One way to do this, is to configure a default value of `GETDATE()`, see [Default Values](#default-values).
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/ComputedColumn.cs?name=DefaultComputedColumn&highlight=3)]

### Value generated on add or update
The above creates a *virtual* computed column, whose value is computed every time it is fetched from the database. You may also specify that a computed column be *stored* (sometimes called *persisted*), meaning that it is computed on every update of the row, and is stored on disk alongside regular columns:

Value generated on add or update means that a new value is generated every time the record is saved (insert or update).
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/ComputedColumn.cs?name=StoredComputedColumn&highlight=3)]

Like `value generated on add`, if you specify a value for the property on a newly added instance of an entity, that value will be inserted rather than a value being generated. It is also possible to set an explicit value when updating. For more information, see [Explicit values for generated properties](xref:core/saving/explicit-values-generated-properties).
> [!NOTE]
> Support for creating stored computed columns was added in EF Core 5.0.
> [!WARNING]
> How the value is generated for added and updated entities will depend on the database provider being used. Database providers may automatically set up value generation for some property types, while others will require you to manually set up how the value is generated.
>
> For example, when using SQL Server, `byte[]` properties that are set as generated on add or update and marked as concurrency tokens, will be set up with the `rowversion` data type - so that values will be generated in the database. However, if you specify that a `DateTime` property is generated on add or update, then you must set up a way for the values to be generated. One way to do this, is to configure a default value of `GETDATE()` (see [Default Values](#default-values)) to generate values for new rows. You could then use a database trigger to generate values during updates (such as the following example trigger).
>
> [!code-sql[Main](../../../samples/core/Modeling/FluentAPI/ValueGeneratedOnAddOrUpdate.sql)]
## Primary keys

By convention, non-composite primary keys of type short, int, long, or Guid are set up to have values generated for inserted entities if a value isn't provided by the application. Your database provider typically takes care of the necessary configuration; for example, a numeric primary key in SQL Server is automatically set up to be an IDENTITY column.

## Value generated on add
For more information, [see the documentation about keys](xref:core/modeling/keys).

By convention, non-composite primary keys of type short, int, long, or Guid are set up to have values generated for inserted entities, if a value isn't provided by the application. Your database provider typically takes care of the necessary configuration; for example, a numeric primary key in SQL Server is automatically set up to be an IDENTITY column.
## Explicitly configuring value generation

You can configure any property to have its value generated for inserted entities as follows:
We saw above that EF Core automatically sets up value generation for primary keys - but we may want to do the same for non-key properties. You can configure any property to have its value generated for inserted entities as follows:

### [Data Annotations](#tab/data-annotations)

Expand All @@ -62,54 +55,84 @@ You can configure any property to have its value generated for inserted entities

***

> [!WARNING]
> This just lets EF know that values are generated for added entities, it does not guarantee that EF will set up the actual mechanism to generate values. See [Value generated on add](#value-generated-on-add) section for more details.
Similarly, a property can be configured to have its value generated on add or update:

### Default values
### [Data Annotations](#tab/data-annotations)

On relational databases, a column can be configured with a default value; if a row is inserted without a value for that column, the default value will be used.
[!code-csharp[Main](../../../samples/core/Modeling/DataAnnotations/ValueGeneratedOnAddOrUpdate.cs?name=ValueGeneratedOnAddOrUpdate&highlight=5)]

You can configure a default value on a property:
### [Fluent API](#tab/fluent-api)

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/DefaultValue.cs?name=DefaultValue&highlight=5)]
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/ValueGeneratedOnAddOrUpdate.cs?name=ValueGeneratedOnAddOrUpdate&highlight=5)]

You can also specify a SQL fragment that is used to calculate the default value:
***

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/DefaultValueSql.cs?name=DefaultValueSql&highlight=5)]
> [!WARNING]
> Unlike with default values or computed columns, we are not specifying *how* the values are to be generated; that depends on the database provider being used. Database providers may automatically set up value generation for some property types, but others may require you to manually set up how the value is generated.
>
> For example, on SQL Server, when a GUID property is configured as value generated on add, the provider automatically performs value generation client-side, using an algorithm to generate optimal sequential GUID values. However, specifying `ValueGeneratedOnAdd()` on a DateTime property will have no effect ([see the section below for DateTime value generation](#datetime-value-generation)).
>
> Similarly, byte[] properties that are configured as generated on add or update and marked as concurrency tokens are set up with the rowversion data type, so that values are automatically generated in the database. However, specifying `ValueGeneratedOnAddOrUpdate()` will again have no effect.
>
> [!NOTE]
> Depending on the database provider being used, values may be generated client side by EF or in the database. If the value is generated by the database, then EF may assign a temporary value when you add the entity to the context; this temporary value will then be replaced by the database generated value during `SaveChanges()`. For more information, [see the docs on temporary values](xref:core/change-tracking/explicit-tracking#temporary-values).
Specifying a default value will implicitly configure the property as value generated on add.
## Date/time value generation

## Value generated on add or update
A common request is to have a database column which contains the date/time for when the column was first inserted (value generated on add), or for when it was last updated (value generated on add or update). As there are various strategies to do this, EF Core providers usually don't set up value generation automatically for date/time columns - you have to configure this yourself.

### [Data Annotations](#tab/data-annotations)
### Creation timestamp

[!code-csharp[Main](../../../samples/core/Modeling/DataAnnotations/ValueGeneratedOnAddOrUpdate.cs?name=ValueGeneratedOnAddOrUpdate&highlight=5)]
Configuring a date/time column to have the creation timestamp of the row is usually a matter of configuring a default value with the appropriate SQL function. For example, on SQL Server you may use the following:

### [Fluent API](#tab/fluent-api)
[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/DefaultValueSql.cs?name=DefaultValueSql&highlight=5)]

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/ValueGeneratedOnAddOrUpdate.cs?name=ValueGeneratedOnAddOrUpdate&highlight=5)]
Be sure to select the appropriate function, as several may exist (e.g. `GETDATE()` vs. `GETUTCDATE()`).

***
### Update timestamp

> [!WARNING]
> This just lets EF know that values are generated for added or updated entities, it does not guarantee that EF will set up the actual mechanism to generate values. See [Value generated on add or update](#value-generated-on-add-or-update) section for more details.
Although stored computed columns seem like a good solution for managing last-updated timestamps, databases usually don't allow specifying functions such as `GETDATE()` in a computed column. As an alternative, you can set up a database trigger to achieve the same effect:

### Computed columns
```sql
CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;

On most relational databases, a column can be configured to have its value computed in the database, typically with an expression referring to other columns:
IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/ComputedColumn.cs?name=DefaultComputedColumn)]
DECLARE @Id INT

The above creates a *virtual* computed column, whose value is computed every time it is fetched from the database. You may also specify that a computed column be *stored* (sometimes called *persisted*), meaning that it is computed on every update of the row, and is stored on disk alongside regular columns:
SELECT @Id = INSERTED.BlogId
FROM INSERTED

UPDATE dbo.Blogs
SET LastUpdated = GETDATE()
WHERE BlogId = @Id
END
```

For information on creating triggers, [see the documentation on using raw SQL in migrations](xref:core/managing-schemas/migrations/managing#adding-raw-sql).

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/ComputedColumn.cs?name=StoredComputedColumn)]
## Overriding value generation

Although a property is configured for value generation, in many cases you may still explicitly specify a value for it. Whether this will actually work depends on the specific value generation mechanism that has been configured; while you may specify an explicit value instead of using a column's default value, the same cannot be done with computed columns.

To override value generation with an explicit value, simply set the property to any value that is not the CLR default value for that property's type (`null` for `string`, `0` for `int`, `Guid.Empty` for `Guid`, etc.).

> [!NOTE]
> Support for creating stored computed columns was added in EF Core 5.0.
> Trying to insert explicit values into SQL Server IDENTITY fails by default; [see these docs for a workaround](xref:core/providers/sql-server/value-generation#inserting-explicit-values-into-identity-columns).
To provide an explicit value for properties that have been configured as value generated on add or update, you must also configure the property as follows:

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/ValueGeneratedOnAddOrUpdateWithPropertySaveBehavior.cs?name=ValueGeneratedOnAddOrUpdateWithPropertySaveBehavior&highlight=5)]

## No value generation

Disabling value generation on a property is typically necessary if a convention configures it for value generation. For example, if you have a primary key of type int, it will be implicitly set configured as value generated on add; you can disable this via the following:
Apart from specific scenarios such as those described above, properties typically have no value generation configured; this means that it's up to the application to always supply a value to be saved to the database. This value must be assigned to new entities before they are added to the context.

However, in some cases you may want to disable value generation that has been set up by convention. For example, a primary key of type int is usually implicitly configured as value-generated-on-add (e.g. identity column on SQL Server). You can disable this via the following:

### [Data Annotations](#tab/data-annotations)

Expand Down
14 changes: 10 additions & 4 deletions entity-framework/core/modeling/keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
title: Keys - EF Core
description: How to configure keys for entity types when using Entity Framework Core
author: AndriySvyryd
ms.date: 11/06/2019
ms.date: 1/10/2021
uid: core/modeling/keys
---
# Keys

A key serves as a unique identifier for each entity instance. Most entities in EF have a single key, which maps to the concept of a *primary key* in relational databases (for entities without keys, see [Keyless entities](xref:core/modeling/keyless-entity-types)). Entities can have additional keys beyond the primary key (see [Alternate Keys](#alternate-keys) for more information).

## Configuring a primary key

By convention, a property named `Id` or `<type name>Id` will be configured as the primary key of an entity.

[!code-csharp[Main](../../../samples/core/Modeling/Conventions/KeyId.cs?name=KeyId&highlight=3,11)]
Expand All @@ -18,11 +20,11 @@ By convention, a property named `Id` or `<type name>Id` will be configured as th
You can configure a single property to be the primary key of an entity as follows:

## [Data Annotations](#tab/data-annotations)
### [Data Annotations](#tab/data-annotations)

[!code-csharp[Main](../../../samples/core/Modeling/DataAnnotations/KeySingle.cs?name=KeySingle&highlight=3)]

## [Fluent API](#tab/fluent-api)
### [Fluent API](#tab/fluent-api)

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/KeySingle.cs?name=KeySingle&highlight=4)]

Expand All @@ -32,6 +34,10 @@ You can also configure multiple properties to be the key of an entity - this is

[!code-csharp[Main](../../../samples/core/Modeling/FluentAPI/KeyComposite.cs?name=KeyComposite&highlight=4)]

## Value generation

For non-composite numeric and GUID primary keys, EF Core sets up value generation for you by convention. For example, a numeric primary key in SQL Server is automatically set up to be an IDENTITY column. For more information, see [the documentation on value generation](xref:core/modeling/generated-properties).

## Primary key name

By convention, on relational databases primary keys are created with the name `PK_<type name>`. You can configure the name of the primary key constraint as follows:
Expand All @@ -45,7 +51,7 @@ While EF Core supports using properties of any primitive type as the primary key
Key properties must always have a non-default value when adding a new entity to the context, but some types will be [generated by the database](xref:core/modeling/generated-properties). In that case EF will try to generate a temporary value when the entity is added for tracking purposes. After [SaveChanges](/dotnet/api/Microsoft.EntityFrameworkCore.DbContext.SaveChanges) is called the temporary value will be replaced by the value generated by the database.

> [!Important]
> If a key property has its value generated by the database and a non-default value is specified when an entity is added, then EF will assume that the entity already exists in the database and will try to update it instead of inserting a new one. To avoid this turn off value generation or see [how to specify explicit values for generated properties](xref:core/saving/explicit-values-generated-properties).
> If a key property has its value generated by the database and a non-default value is specified when an entity is added, then EF will assume that the entity already exists in the database and will try to update it instead of inserting a new one. To avoid this, turn off value generation or see [how to specify explicit values for generated properties](xref:core/modeling/generated-properties#overriding-value-generation).
## Alternate Keys

Expand Down
Loading

0 comments on commit 5a60b35

Please sign in to comment.