Release Notes 2.0.0
Also check provider-specific changes for your provider as this section contains only provider-independent changes.
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.
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.
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.
This release adds native support for common table expressions, including recursive CTE (534, 890). You can read more about this feature here.
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();
}
};
fluentBuilder
.Entity<Entity>()
.Association(
e => e.AssociationProperty,
(thisSide, otherSide) => thisSide.Id == otherSide.ID1);
- complex types mapping fixed (1005)
This new feature will allow you to use dynamic columns in your queries (507, 744, 964,1083). Check this PR for more details.
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;
}
}
- 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)
- 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 useLength = 1
(1091) - improved support of interfaces (1099)
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;
- 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)
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));
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 });
Those optional parameters now available also for following IDataContext extension methods:
InsertOrReplace*
InsertWith*Identity*
Update*
Delete*
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();
}
}
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.
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.
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
}
Added trace callback for DataContext
class similar to one that already exist on DataConnection
class (1131).
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
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 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.
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();
-
CROSS JOIN
andSelectMany
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)
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.
- 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 inInnerException
(1065) - fixed issue when selected of
NULL
value usingSelectMany
method could have resulted indefault(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 havingowner
andschema
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
- 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)
.NET Core DB2 provider support was added.
- schema provider doesn't return procedures and functions if
GetTables = false
specified (1068)
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;
-
BulkCopy
will throw exception ifKeepIdentity = 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
andFirebirdSqlOptimizer
classes made public to help users override default implementation (1000)
- Added delimited identifiers support
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)
Support for beta version of .NET Core provider added.
-
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)
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).
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.
- 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
andNpgsqlInterval
types (1091)
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.
As a part of KeepIdentity
option review, support for it added to SQL CE provider. Check documentation for more details (1037).
-
MERGE
insert operation will respectSkipOnInsert
on identity fields whenInsertWhenNotMatched()
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)
-
DateTime.AddDays()
method toSQL
conversion fixed (998)
-
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 respectSkipOnInsert
on identity fields whenInsertWhenNotMatched()
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
andRetryingDbConnection
(1135)
- 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
methodsToTypeName
andToValidName
made public (944, 963) -
SqlProviderFlags.CustomFlags
list added to allow store custom provider flags (1154) - expose
SqlExtensions
class for provider developers
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!