Skip to content

Release Notes 2.0.0

MaceWindu edited this page May 17, 2018 · 96 revisions

General changes

Also check provider-specific changes for your provider as this section contains only provider-independent changes.

Breaking Changes

Changes to target frameworks

Version 2.0 drops support for legacy frameworks: net4.0, silverlight, windows8 store. List of supported targets now includes:

  • net45
  • netstandard1.6
  • netstandard2.0
  • netcoreapp2.0

Also you can notice that now linq2db nuget package supports all those targets and you don't need to use linq2db.core package if you want to target .net core projects. This package is now deprecated and will not be updated anymore.

Who will be affected by this change:

  • People that used deprecated frameworks. They should continue use 1.x version or migrate their projects.
  • Users of linq2db.core package. They should update their nuget references to use linq2db package.

Default enumeration mapping behavior changes for text columns

Starting from version 2.0, LINQ To DB will use ToString() method to create database value for enums, mapped to text column (1006, 1071). Prior versions used numeric representation of enumeration value, converted to string.

How to find out if I'm affected by this change?

You are affected if you have text columns mapped to enumeration without explicit mappings specified for fields.

See following example:

// enums like that are not affected, because they
// have explicit mappings from enum fields to database values
public enum GoodEnum
{
    [MapValue("1")]
    First,
    [MapValue("Second")]
    Second
}

// enums like that will change their behavior
public enum BadEnum
{
    // v1.x: "0" used as a database value 
    // v2: "First" used as a database value
    First,
    // v1.x: "1" used as a database value 
    // v2: "Second" used as a database value
    Second
}

We want to say that it is generally bad idea to have no explicit enumeration field mappings as you depend on library behavior, which could change, and changes to enumeration (adding/removing/reordering fields) could lead to mapped value change.

I'm affected. What should I do?

First of all you can re-enable old behavior using following configuration flag:

Configuration.UseEnumValueNameForStringColumns = false;

But we recommend to add explicit mappings to your enumeration.

CTE Support

This release adds native support for common table expressions, including recursive CTE (534, 890). You can read more about this feature here.

Mapping

MappingSchema.EntityDescriptorCreatedCallback

This new callback could be used to modify entity mapping descriptor after creation (1074).

E.g. you can use it to change columns name notation to snake-case.

ms.EntityDescriptorCreatedCallback = (mappingSchema, entityDescriptor) =>
{
    // let's imagine we have ToSnakeCase string
    // extension method somewhere in our project
    entityDescriptor.TableName = entityDescriptor.TableName.ToSnakeCase();
    foreach (var entityDescriptorColumn in entityDescriptor.Columns)
    {
        entityDescriptorColumn.ColumnName
            = entityDescriptorColumn.ColumnName.ToSnakeCase();
    }
};

Fluent Mapping

Custom join predicate exression support for associations

961

fluentBuilder
    .Entity<Entity>()
    .Association(
        e => e.AssociationProperty,
        (thisSide, otherSide) => thisSide.Id == otherSide.ID1);

Other Changes and Fixes

  • complex types mapping fixed (1005)

Dynamic Columns

This new feature will allow you to use dynamic columns in your queries (507, 744, 964,1083). Check this PR for more details.

Calculated Columns

You can use expressions to define calculated columns using IsColumn property of ExpressionMethodAttribute attribute (1004).

[Table]
public class Entity
{
    // normal read/write columns
    [Column] public string FirstName { get; set; }
    [Column] public string LastName  { get; set; }

    // read-only expression-based property
    [ExpressionMethod(nameof(FullNameExpr), IsColumn = true)]
    public string FullName { get; set; }

    private static Expression<Fun<Entity, string>> FullNameExpr()
    {
        return e => e.LastName + ", " + e,FirstName;
    }
}

Inheritance Mapping

  • fixed incorrect query filter generation for left join associations for entities with inheritance mapping (956)
  • fixed exception when inherited entity selected into property of base type (1046)
  • Update/Delete/Insert/InsertOrReplace extensions will properly recognize inherited values when passed as parameter of base type. Important: entities without inheritance mapping not affected by this change and query generation will use parameter type as before (1017)
  • LoadWith fixed to properly load derived entities (994)
  • fixed several issues with type conversion in expressions between base and derived types (1057, 1065)
  • fixed exception when LoadWith called for nullable reference to entity with inheritance mapping (996)

Other Changes and Fixes

  • fixed MappingSchema converters were ignored for enums (1006)
  • fixed issue when adding new metadata reader to MappingSchema.Default could result in previously added readers being ignored (1066)
  • default char mapping will now use Length = 1 (1091)
  • improved support of interfaces (1099)

SQL Generation

CROSS/OUTER APPLY support

For databases that support APPLY joins you can disable this functionality using following flag:

// set to true by default
// v2.0-beta5 has typo in property name : PrefereApply
Configuration.PreferApply = false;

Other Changes and Fixes

  • fixed exception generated for some cases when joins optimization enabled and table hints used (949)
  • fixed SQL generation regression for some complex subqueries (928)
  • fixed invalid SQL generated for empty select combined with Take/Skip (817)
  • fixed issue when joined subquery condition were moved to outer query condition (922)

Extensions

New In/NotIn extension methods

SqlExtensions class contains new extension methods In/NotIn, applied to a value. In general it is just a reverse Contains methods to better mimic SQL.

// filter table by id
// old reverse logic approach
db.Table.Where(r => ids.Contains(r.Id));

// now it could be written like you do it in raw SQL
db.Table.Where(r => r.Id.In(ids));

New *Join extension methods (1076, 1088)

Additional extension methods added to define join using two queryable sources, join predicate expression and result selector. Following methods added: InnerJoin, LeftJoin, RightJoin, FullJoin and CrossJoin.

// left outer join
db.Parent.LeftJoin(
    db.Child,
    (p, c) => p.ParentID == c.ParentID,
    (p, c) => new { ParentID = p.ParentID, ChildID = (int?)c.ChildID });

// cross join - note that join condition is not applicable here
db.Parent.CrossJoin(
    db.Child,
    (p, c) => new { ParentID = (int?)p.ParentID, ChildID = (int?)c.ChildID });

Added missing support for table, schema/owner and database name parameters in some methods

Those optional parameters now available also for following IDataContext extension methods:

  • InsertOrReplace*
  • InsertWith*Identity*
  • Update*
  • Delete*

Parameters creation in extension builders

You can add new query parameters from extension builders (973).

class InWithParametersBuilder : Sql.IExtensionCallBuilder
{
    public void Build(Sql.ISqExtensionBuilder builder)
    {
        // get extension parameter
        var values = builder.GetValue<System.Collections.IEnumerable>("values");
        // tell linq2db that query uses non-static parameters
        builder.Query.IsParameterDependent = true;

        foreach (var value in values)
        {
            // create query parameter
            var param = new SqlParameter(value?.GetType() ?? typeof(object), "p", value);
            // add parameter (note that we can add multiple parameters for one placeholder)
            builder.AddParameter("values", param);
        }
    }
}

public static class Extensions
{
    // note that for values we specify comma as parameters delimiter
    [Sql.Extension("{field} IN ({values, ', '})", IsPredicate = true, BuilderType = typeof(InWithParametersBuilder))]
    public static bool In<T>(this Sql.ISqlExtension ext, [ExprParameter] T field, params T[] values)
    {
        throw new NotImplementedException();
    }
}

New extensibility points

LinqExtensions.ProcessSourceQueryable delegate

You can use it to preprocess IQueryable sources, passed to other APIs (1116).

E.g. it is used by our other project https://github.com/linq2db/linq2db.EntityFrameworkCore to replace EntityFramework queryable provider with LINQ To DB provider.

LinqExtensions.ExtensionsAdapter interface

You can override implementation of LINQ To DB async methods using this interface. Note that right now it is all-or-nothing interface, so you need to implement all methods you use, even if you want to change behavior of only one method. In this case you need to call original LINQ To DB method for others.

IEntityServices.OnEntityCreated delegate

This delegate will be called after entity instantiation for contexts that implement IEntityServices interface (1112). It allows you to pre-process or even replace created entity. LINQ To DB already implements this interface for default contexts: DataContext, DataConnection and RemoteDataContextBase classes.

using (var db = new DataConnection())
{
    var cnt = 0;

    // just count how many User entities were instantiated
    db.OnEntityCreated += e =>
    {
        if (e.Entity is User)
            cnt++;
    }

   // do some queries
}

DataContext.OnTraceConnection callback

Added trace callback for DataContext class similar to one that already exist on DataConnection class (1131).

Temp Tables API

New API to create temporary tables added to IDataContext as a set of CreateTempTable() extension methods.

Using this API you can create queryable table, populate it with data, perform queries and then delete by disposing it.

Example below shows you how can you use merge with client-side source in more effective way by adding those records into temp table and merge them into main table.

public void MergePersons(this IDataContext db, IEnumerable<Person> persons)
{
    // create new table for existing Person mapping and populate it using bulk copy
    using (var tmp = db.CreateTempTable<Person>(persons, tableName: "PersonTemp"))
    {
        db.Persons // target table
            .Merge()
            .Using(tmp) // use data from temp table
            .OnTargetKey()
            .InsertWhenNotMatched() // insert new records
            .UpdateWhenMatched() // update known records
            .DeleteWhenNotMatchedBySource() // delete others
            .Merge();

    } // here dispose will delete temp table
}

API allows you:

  • specify new table, owner/schema, database name using corresponding parameters
  • populate table using data from IQueryable source parameter
  • populate table using bulk copy from IEnumerable source parameter and BulkCopyOptions
  • adjust entity mapping for temporary table using fluent mapping delegate using setTable parameter
  • call some action on created table before populating it with data using action parameter

Bulk Copy

Documentation

New wiki page created for BulkCopy API. It still miss documentation on some options, but most important information is already here. It will be improved in next releases.

KeepIdentity option

KeepIdentity option were documented and existing implementations tested/fixed to follow it (1037).

Setting this option to true for RowByRow copy mode was never supported and if you have it set to true - starting from version 2.0 it will start throwing exception instead of silently ignore it. Check copy mode support table to see what mode actually used for your provider, as BulkCopy will downgrade copy mode, if requested one is not supported by provider.

Merge

Partial projections in source query improvements

Now merge will properly detect and throw exception when source query element type contains more fields than query returns.

class Person
{
    [PrimaryKey, Column, Identity] public int Id { get; set; }
    [Column] public string FirstName { get; set; }
    [Column] public string LastName { get; set; }
    [Column] public string Title { get; set; }
}

// bad query
db.Persons.Merge()
    // note that Id and Title columns is not selected by query
    .Using(db.NewPersons.Select(p => new Person()
        {
            FirstName = p.LastName,
            LastName = p.FirstName
        }))
    // will throw exception that Id key field missing in source
    .OnTargetKey()
    // will throw exception that Title field missing in source
    .InsertWhenNotMatched(s => new Person() { Title = s.Title })
    .Merge();

Other Changes and Fixes

  • CROSS JOIN and SelectMany support in source query (896)
  • Merge call will not be available anymore if you didn't specified any operations yet
  • fixed exception of empty local source with source type != target type (1153)

Schema Provider

Requesting schema for procedures and functions will wrap it internally in transaction with rollback, when called without transaction. This is done to avoid situations when some providers execute (sic!) procedures instead of just returning their schema. Taking into account such bugs it is recommended to never wrap schema provider calls into transaction and let LINQ To DB handle it.

Other Changes and Fixes

  • errors during mapping of data from database to mapping class field on selects will now be wrapped into LinqToDBException with details what field failed with original error in InnerException (1065)
  • fixed issue when selected of NULL value using SelectMany method could have resulted in default(T) value for value types even with cast to T? (1012)
  • fixed exception passing binary data over WCF (925)
  • spelling error fixed for SchemaProvider.ForeignKeyInfo (941)
  • enforce server-side evaluation of Sql.Lower/Sql.Upper functions (819)
  • fixed case when async code could be blocked on synchronous Connection.Open call (1023)
  • fixed support for type casts in LoadWith expression (1069)
  • fixed issue when insert query with sub-query data source will fail on next calls if it has nullable parameter and first call uses null for parameter value (1098)
  • T4.Models repository was obsolete project and moved to linq2db repo
  • removed use of database object owner name from many APIs to reduce confusion with having owner and schema overrides at the same time, meaning the same concept
  • T4 templates support in .NET Core projects (1067)
  • new DataConnection.GetRegisteredProviders method to return list of all registered data providers

Provider-specific changes

Access

  • handle exceptions from OleDb provider on schema read calls when ACE provider used (10)
  • schema provider will now return system tables too (TableInfo.IsProviderSpecific == true) (1119)

DB2

IBM.Data.DB2.Core provider support

.NET Core DB2 provider support was added.

Other Changes and Fixes

  • schema provider doesn't return procedures and functions if GetTables = false specified (1068)

Firebird

Default identifier quotation mode change

Default identifier quotation mode changed from FirebirdIdentifierQuoteMode.None to FirebirdIdentifierQuoteMode.Auto (1120).

Normally it shouldn't affect anybody, as Auto mode will quote only invalid and reserved identifiers and they will not work anyway in None mode.

Still, technically it could be a case for people who used quoted identifier in mappings. This is not something you should do, as mapping should contain raw identifers and quotation should be handled by LINQ To DB. If you did that we want to hear why you need to do it and how we can improve LINQ To DB in this area.

If you are affected by this change, just restore old quotation mode using following code:

FirebirdSqlBuilder.IdentifierQuoteMode = FirebirdIdentifierQuoteMode.None;

Other changes and fixes

  • BulkCopy will throw exception if KeepIdentity = true option specified as this option is not supported for Firebird (1037). Check BulkCopy documentation for more details
  • DropTable API will check if dropped objects exist before dropping them (1120)
  • fixed support of seconds and milliseconds by Sql.DatePart function (967)
  • detect and escape identifiers that use reserved words in FirebirdIdentifierQuoteMode.Auto mode (1095, 1110)
  • CreateTable/DropTable/TruncateTable will respect identifier quotation mode during query generation (1120)
  • FirebirdDataProvider and FirebirdSqlOptimizer classes made public to help users override default implementation (1000)

Informix

  • Added delimited identifiers support

MySQL and MariaDB

Procedures and functions support by schema provider

Schema provider for MySQL/MariaDB was updated to return procedures and functions (991). Requesting procedures and functions from transaction will throw exception because schema provider need to wrap it internally into transaction to avoid procedure execution due to bug in provider (792)

Oracle

.NET Core provider support

Support for beta version of .NET Core provider added.

Other changes and fixes

  • date literal generation fixes (969)
  • schema provider doesn't return procedures and functions if GetTables = false specified (1068)
  • detect and escape identifiers that use reserved words (1095, 1110)

PostgreSQL

Native bulk copy support

Version 2.0 adds support for native bulk copy method (935). It is available through existing BulkCopy LINQ To DB API by specifying BulkCopyOptions.BulkCopyType = BulkCopyType.ProviderSpecific. It is a high-level wrapper over COPY command.

Note that if you already used this mode for your bulk copy operations it could be a breaking change because now it will use COPY instead of silent fallback to BulkCopyType.MultipleRows in previous versions.

Why it could be a breaking change? Because COPY command (we use BINARY mode) demands that proper column types specified and will fail if types doesn't match. You will need to add type information to your mappings or switch to other copy method.

You can read about type requirements more in out new article about BulkCopy API here.

Implementation supports COPY API from both npgsql 3.x and npgsql 4.x (4.0 brings breaking changes to API).

Upsert support

InsertOrUpdate API will use INSERT ON CONFLICT UPDATE statement for PostgreSQL 9.5+ instead of several statements on previous versions (948).

You will need to use PostgreSQLVersion.v95 provider version if you don't use version autodetect.

Other changes and fixes

  • improved support for some database types as a part of BulkCopy improvements (1091)
  • fixed support for following types in CreateTable API: Int16/Int64 identity columns, System.Linq.Binary, DataType.VarBinary, DataType.NChar(1), char (1091)
  • support for interval type mapping to both NpgsqlTimeSpan and NpgsqlInterval types (1091)

SAP HANA

BulkCopy KeepIdentity option support

As a part of KeepIdentity option review, support for it added to SAP HANA provider. Check documentation for more details (1037).

Note that this option requires support from provider, so make sure you use recent provider version with enum HanaBulkCopyOptions having KeepIdentity field and not all versions had it. Otherwise BulkCopy will throw exception.

SQL CE

BulkCopy KeepIdentity option support

As a part of KeepIdentity option review, support for it added to SQL CE provider. Check documentation for more details (1037).

Sybase

  • MERGE insert operation will respect SkipOnInsert on identity fields when InsertWhenNotMatched() without custom setter used and allow database to generate field's value (914)
  • requesting procedures and functions from schema provider will throw exception if called from transaction to avoid database corruction due to bug in provider (792)

SQLite

  • DateTime.AddDays() method to SQL conversion fixed (998)

SQL Server

  • varchar/nvarchar parameters will use 8000/4000 as length to improve query plans caching (989)
  • MERGE will use parameters instead of literals for binary data in client-side (IEnumerable) source
  • MERGE insert operation will respect SkipOnInsert on identity fields when InsertWhenNotMatched() without custom setter used and allow database to generate field's value (914)
  • legacy MERGE API will not try to update identity columns anymore on update operation anymore (1007)
  • fixed DropTable method not dropping table in another database (1030)
  • fixed DateTime literal generation (1107)
  • fixed incompatibility between BulkCopy and RetryingDbConnection (1135)

Changes for developers

  • Query AST was refactored. See SelectQuery class (936, 938)
  • Tests configuration changed format and use *DataProviders.json files instead of *DataPRoviders.txt. More details
  • Project migrated to support latest C# version
  • You can use new ActiveIssueAttribute to mark tests for non-fixed issues. This will allow to merge test-only PRs immediately
  • SchemaProviderBase methods ToTypeName and ToValidName made public (944, 963)
  • SqlProviderFlags.CustomFlags list added to allow store custom provider flags (1154)
  • expose SqlExtensions class for provider developers

I Use Entity Framework (:feelsbadman:)

Don't wory, check this new project we created recently. It is still an early prototype so don't expect it to work flawlessly. We will appreciate your feedback!

Clone this wiki locally