Skip to content

Commit

Permalink
Small fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
vonzshik committed Sep 20, 2024
1 parent 962e566 commit 4235ce3
Show file tree
Hide file tree
Showing 8 changed files with 607 additions and 607 deletions.
388 changes: 194 additions & 194 deletions conceptual/EF6.PG/index.md
Original file line number Diff line number Diff line change
@@ -1,194 +1,194 @@
---
layout: doc
title: Entity Framework 6
---

Npgsql has an Entity Framework 6 provider. You can use it by installing the
[EntityFramework6.Npgsql](https://www.nuget.org/packages/EntityFramework6.Npgsql/) nuget.

## Basic Configuration ##

Configuration for an Entity Framework application can be specified in a config file (app.config/web.config) or through code. The latter is known as code-based configuration.

### Code-based ###

To use Entity Framework with Npgsql, define a class that inherits from `DbConfiguration` in the same assembly as your class inheriting `DbContext`. Ensure that you configure provider services, a provider factory, a default connection factory as shown below:

```csharp
using Npgsql;
using System.Data.Entity;

class NpgSqlConfiguration : DbConfiguration
{
public NpgSqlConfiguration()
{
var name = "Npgsql";

SetProviderFactory(providerInvariantName: name,
providerFactory: NpgsqlFactory.Instance);

SetProviderServices(providerInvariantName: name,
provider: NpgsqlServices.Instance);

SetDefaultConnectionFactory(connectionFactory: new NpgsqlConnectionFactory());
}
}
```

### Config file ###

When installing `EntityFramework6.Npgsql` nuget package, the relevant sections in `App.config` / `Web.config` are usually automatically updated. You typically only have to add your `connectionString` with the correct `providerName`.

```xml
<configuration>
<connectionStrings>
<add name="BlogDbContext" connectionString="Server=localhost;port=5432;Database=Blog;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<entityFramework>
<providers>
<provider invariantName="Npgsql" type="Npgsql.NpgsqlServices, EntityFramework6.Npgsql" />
</providers>
<!-- setting the default connection factory is optional -->
<defaultConnectionFactory type="Npgsql.NpgsqlConnectionFactory, EntityFramework6.Npgsql" />
</entityFramework>
<system.data>
<DbProviderFactories>
<add name="Npgsql Provider" invariant="Npgsql" description=".NET Framework Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, Npgsql, Version=4.1.3.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
</configuration>
```

## Guid Support ##

Npgsql EF migrations support uses `uuid_generate_v4()` function to generate guids.
In order to have access to this function, you have to install the extension uuid-ossp through the following command:

```sql
create extension "uuid-ossp";
```

If you don't have this extension installed, when you run Npgsql migrations you will get the following error message:

```text
ERROR: function uuid_generate_v4() does not exist
```

If the database is being created by Npgsql Migrations, you will need to
[run the `create extension` command in the `template1` database](http://stackoverflow.com/a/11584751).
This way, when the new database is created, the extension will be installed already.

## Optimistic Concurrency ##

EntityFramework supports [optimistic concurrency](https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application), through the [system column `xmin`](https://www.postgresql.org/docs/current/ddl-system-columns.html). To use this column as the concurrency token, some [customization is needed](https://github.com/npgsql/EntityFramework6.Npgsql/issues/8). The following code will setup `Department.Version` to map to `xmin`, while the `SqlGenerator` will generate `CREATE/ALTER TABLE` statements omitting system columns.

```csharp
public class Department {
public string Version { get; private set; }
}

[DbConfigurationType(typeof(Configuration))]
public class UniversityDbContext : DbContext
{
public DbSet<Department> Departments { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>()
.Property(p => p.Version)
.HasColumnName("xmin")
.HasColumnType("text")
.IsConcurrencyToken()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
base.OnModelCreating(modelBuilder);
}
}

internal class Configuration : DbConfiguration
{
public Configuration()
{
SetMigrationSqlGenerator("Npgsql", () => new SqlGenerator());
}
}

public class SqlGenerator : NpgsqlMigrationSqlGenerator
{
private readonly string[] systemColumnNames = { "oid", "tableoid", "xmin", "cmin", "xmax", "cmax", "ctid" };

protected override void Convert(CreateTableOperation createTableOperation)
{
var systemColumns = createTableOperation.Columns.Where(x => systemColumnNames.Contains(x.Name)).ToArray();
foreach (var systemColumn in systemColumns)
createTableOperation.Columns.Remove(systemColumn);
base.Convert(createTableOperation);
}
}
```

## Template Database ##

When the Entity Framework 6 provider creates a database, it issues a simple `CREATE DATABASE` command.
In PostgreSQL, this implicitly uses `template1` as the template - anything existing in `template1` will
be copied to your new database. If you wish to change the database used as a template, you can specify
the `EF Template Database` connection string parameter. For more info see the
[PostgreSQL docs](https://www.postgresql.org/docs/current/static/sql-createdatabase.html).

## Customizing DataReader Behavior ##

You can use [an Entity Framework 6 IDbCommandInterceptor](https://msdn.microsoft.com/library/dn469464(v=vs.113).aspx) to wrap the `DataReader` instance returned by Npgsql when Entity Framework executes queries. This is possible using a ```DbConfiguration``` class.

Example use cases:

- Forcing all returned ```DateTime``` and ```DateTimeOffset``` values to be in the UTC timezone.
- Preventing accidental insertion of DateTime values having ```DateTimeKind.Unspecified```.
- Forcing all postgres date/time types to be returned to Entity Framework as ```DateTimeOffset```.

```csharp
[DbConfigurationType(typeof(AppDbContextConfiguration))]
public class AppDbContext : DbContext
{
// ...
}

public class AppDbContextConfiguration : DbConfiguration
{
public AppDbContextConfiguration()
{
this.AddInterceptor(new MyEntityFrameworkInterceptor());
}
}

class MyEntityFrameworkInterceptor : DbCommandInterceptor
{
public override void ReaderExecuted(
DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (interceptionContext.Result == null) return;
interceptionContext.Result = new WrappingDbDataReader(interceptionContext.Result);
}

public override void ScalarExecuted(
DbCommand command,
DbCommandInterceptionContext<object> interceptionContext)
{
interceptionContext.Result = ModifyReturnValues(interceptionContext.Result);
}

static object ModifyReturnValues(object result)
{
// Transform and then
return result;
}
}

class WrappingDbDataReader : DbDataReader, IDataReader
{
// Wrap an existing DbDataReader, proxy all calls to the underlying instance,
// modify return values and/or parameters as needed...
public WrappingDbDataReader(DbDataReader reader)
{
}
}
```
---
layout: doc
title: Entity Framework 6
---

Npgsql has an Entity Framework 6 provider. You can use it by installing the
[EntityFramework6.Npgsql](https://www.nuget.org/packages/EntityFramework6.Npgsql/) nuget.

## Basic Configuration ##

Configuration for an Entity Framework application can be specified in a config file (app.config/web.config) or through code. The latter is known as code-based configuration.

### Code-based ###

To use Entity Framework with Npgsql, define a class that inherits from `DbConfiguration` in the same assembly as your class inheriting `DbContext`. Ensure that you configure provider services, a provider factory, a default connection factory as shown below:

```csharp
using Npgsql;
using System.Data.Entity;

class NpgSqlConfiguration : DbConfiguration
{
public NpgSqlConfiguration()
{
var name = "Npgsql";

SetProviderFactory(providerInvariantName: name,
providerFactory: NpgsqlFactory.Instance);

SetProviderServices(providerInvariantName: name,
provider: NpgsqlServices.Instance);

SetDefaultConnectionFactory(connectionFactory: new NpgsqlConnectionFactory());
}
}
```

### Config file ###

When installing `EntityFramework6.Npgsql` nuget package, the relevant sections in `App.config` / `Web.config` are usually automatically updated. You typically only have to add your `connectionString` with the correct `providerName`.

```xml
<configuration>
<connectionStrings>
<add name="BlogDbContext" connectionString="Server=localhost;port=5432;Database=Blog;User Id=postgres;Password=postgres;" providerName="Npgsql" />
</connectionStrings>
<entityFramework>
<providers>
<provider invariantName="Npgsql" type="Npgsql.NpgsqlServices, EntityFramework6.Npgsql" />
</providers>
<!-- setting the default connection factory is optional -->
<defaultConnectionFactory type="Npgsql.NpgsqlConnectionFactory, EntityFramework6.Npgsql" />
</entityFramework>
<system.data>
<DbProviderFactories>
<add name="Npgsql Provider" invariant="Npgsql" description=".NET Framework Data Provider for PostgreSQL" type="Npgsql.NpgsqlFactory, Npgsql, Version=4.1.3.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7" />
</DbProviderFactories>
</system.data>
</configuration>
```

## Guid Support ##

Npgsql EF migrations support uses `uuid_generate_v4()` function to generate guids.
In order to have access to this function, you have to install the extension uuid-ossp through the following command:

```sql
create extension "uuid-ossp";
```

If you don't have this extension installed, when you run Npgsql migrations you will get the following error message:

```text
ERROR: function uuid_generate_v4() does not exist
```

If the database is being created by Npgsql Migrations, you will need to
[run the `create extension` command in the `template1` database](http://stackoverflow.com/a/11584751).
This way, when the new database is created, the extension will be installed already.

## Optimistic Concurrency ##

EntityFramework supports [optimistic concurrency](https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application), through the [system column `xmin`](https://www.postgresql.org/docs/current/ddl-system-columns.html). To use this column as the concurrency token, some [customization is needed](https://github.com/npgsql/EntityFramework6.Npgsql/issues/8). The following code will setup `Department.Version` to map to `xmin`, while the `SqlGenerator` will generate `CREATE/ALTER TABLE` statements omitting system columns.

```csharp
public class Department {
public string Version { get; private set; }
}

[DbConfigurationType(typeof(Configuration))]
public class UniversityDbContext : DbContext
{
public DbSet<Department> Departments { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>()
.Property(p => p.Version)
.HasColumnName("xmin")
.HasColumnType("text")
.IsConcurrencyToken()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
base.OnModelCreating(modelBuilder);
}
}

internal class Configuration : DbConfiguration
{
public Configuration()
{
SetMigrationSqlGenerator("Npgsql", () => new SqlGenerator());
}
}

public class SqlGenerator : NpgsqlMigrationSqlGenerator
{
private readonly string[] systemColumnNames = { "oid", "tableoid", "xmin", "cmin", "xmax", "cmax", "ctid" };

protected override void Convert(CreateTableOperation createTableOperation)
{
var systemColumns = createTableOperation.Columns.Where(x => systemColumnNames.Contains(x.Name)).ToArray();
foreach (var systemColumn in systemColumns)
createTableOperation.Columns.Remove(systemColumn);
base.Convert(createTableOperation);
}
}
```

## Template Database ##

When the Entity Framework 6 provider creates a database, it issues a simple `CREATE DATABASE` command.
In PostgreSQL, this implicitly uses `template1` as the template - anything existing in `template1` will
be copied to your new database. If you wish to change the database used as a template, you can specify
the `EF Template Database` connection string parameter. For more info see the
[PostgreSQL docs](https://www.postgresql.org/docs/current/static/sql-createdatabase.html).

## Customizing DataReader Behavior ##

You can use [an Entity Framework 6 IDbCommandInterceptor](https://msdn.microsoft.com/library/dn469464(v=vs.113).aspx) to wrap the `DataReader` instance returned by Npgsql when Entity Framework executes queries. This is possible using a ```DbConfiguration``` class.

Example use cases:

- Forcing all returned ```DateTime``` and ```DateTimeOffset``` values to be in the UTC timezone.
- Preventing accidental insertion of DateTime values having ```DateTimeKind.Unspecified```.
- Forcing all postgres date/time types to be returned to Entity Framework as ```DateTimeOffset```.

```csharp
[DbConfigurationType(typeof(AppDbContextConfiguration))]
public class AppDbContext : DbContext
{
// ...
}

public class AppDbContextConfiguration : DbConfiguration
{
public AppDbContextConfiguration()
{
this.AddInterceptor(new MyEntityFrameworkInterceptor());
}
}

class MyEntityFrameworkInterceptor : DbCommandInterceptor
{
public override void ReaderExecuted(
DbCommand command,
DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (interceptionContext.Result == null) return;
interceptionContext.Result = new WrappingDbDataReader(interceptionContext.Result);
}

public override void ScalarExecuted(
DbCommand command,
DbCommandInterceptionContext<object> interceptionContext)
{
interceptionContext.Result = ModifyReturnValues(interceptionContext.Result);
}

static object ModifyReturnValues(object result)
{
// Transform and then
return result;
}
}

class WrappingDbDataReader : DbDataReader, IDataReader
{
// Wrap an existing DbDataReader, proxy all calls to the underlying instance,
// modify return values and/or parameters as needed...
public WrappingDbDataReader(DbDataReader reader)
{
}
}
```
Loading

0 comments on commit 4235ce3

Please sign in to comment.