По большому счёту изменение только одно, но оно вполне тянет на подверсию — полностью переписан конвертер Linq выражений. В результате большинство багов, связанных с неправильной обработкой выражений должно исчезнуть.
Просьба потестировать новую версию. Если с ней возникнут какие-либо трудности, то на старый конвертер можно переключиться, добавив символ условной компиляции 'OLD_PARSER'.
С этого момента принимаются пожелания по дальнейшему развитию библиотеки
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Просьба потестировать новую версию.
Сломался код, использующий InsertBatch в транзакции.
Откатился до версии от 10 мая 05:59:40 (Commit hash: 2dfd9a94d59f291f667eea21a8eb3949edffdf7b, Comment: Working on Linq) — работает, обновляюсь до текущей — валится с таким исключением Unexpected existing transaction.
System.InvalidOperationException : Unexpected existing transaction.
at System.Data.SqlClient.SqlBulkCopy.AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet internalResults)
at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternal()
at System.Data.SqlClient.SqlBulkCopy.WriteRowSourceToServer(Int32 columnCount)
at System.Data.SqlClient.SqlBulkCopy.WriteToServer(IDataReader reader)
at BLToolkit.Data.DataProvider.SqlDataProviderBase.InsertBatch(DbManager db, String insertText, IEnumerable`1 collection, MemberMapper[] members, Int32 maxBatchSize, ParameterProvider`1 getParameters) in d:\Projects\BLToolkit\Source\Data\DataProvider\SqlDataProviderBase.cs: line 233
at BLToolkit.DataAccess.SqlQuery`1.Insert(DbManager db, Int32 maxBatchSize, IEnumerable`1 list) in d:\Projects\BLToolkit\Source\DataAccess\SqlQueryT.cs: line 171
at BLToolkit.Data.Linq.Extensions.InsertBatch(DbManager dataContext, Int32 maxBatchSize, IEnumerable`1 list) in d:\Projects\BLToolkit\Source\Data\Linq\Extensions.cs: line 201
at BLToolkit.Data.Linq.Extensions.InsertBatch(DbManager dataContext, IEnumerable`1 list) in d:\Projects\BLToolkit\Source\Data\Linq\Extensions.cs: line 206
Код для воспроизведения
CREATE TABLE [dbo].[SqlBulkCopyTest](
[ID] int IDENTITY(1,1) NOT NULL,
[Value] nvarchar(50) NOT NULL,
CONSTRAINT [PK_SqlBulkCopyTest] PRIMARY KEY CLUSTERED ([ID] ASC)
)
public class SqlBulkCopyTest
{
[Identity, PrimaryKey]
public int ID { get; set; }
public string Value { get; set; }
}
[Test]
public void InsertBatchTest()
{
var list = new[]
{
new SqlBulkCopyTest { Value = "1" },
new SqlBulkCopyTest { Value = "2" },
new SqlBulkCopyTest { Value = "3" },
new SqlBulkCopyTest { Value = "4" }
};
using (var db = new DbManager())
{
db.BeginTransaction();
db.InsertBatch(list);
db.CommitTransaction();
}
}
Здравствуйте, IT, Вы писали:
IT>Просьба потестировать новую версию.
О багах.
1. Сломался такой код. В режиме 'OLD_PARSER' работает.
public override void AddUsersToRoles(string[] userNames, string[] roleNames)
{
if (userNames == null) throw new ArgumentNullException("userNames");
if (roleNames == null) throw new ArgumentNullException("roleNames");
if (userNames.Length == 0 || roleNames.Length == 0)
return;
using (var db = _dbFactory.CreateDbManager())
{
var q =
from r in db.Roles()
from a in db.Accounts()
join ar in db.AccountRoles()
on new { AccountID = a.ID, RoleID = r.ID } equals new { ar.AccountID, ar.RoleID } into lj
from ar in lj.DefaultIfEmpty()
where
!((int?)ar.RoleID).HasValue
&& roleNames.Contains(r.Name)
&& userNames.Contains(a.Email)
select new AccountRole
{
AccountID = a.ID,
RoleID = r.ID
};
q.Insert(
db.AccountRoles(),
ar => new AccountRole {AccountID = ar.AccountID, RoleID = ar.RoleID});
}
}
Exception: Sequence 'value(AspRoleProvider+<>c__DisplayClass19).db.Accounts()' cannot be converted to SQL.
BLToolkit.Data.Linq.LinqException : Sequence 'value(AspRoleProvider+<>c__DisplayClass19).db.Accounts()' cannot be converted to SQL.
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 160
at BLToolkit.Data.Linq.Builder.SelectManyBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\SelectManyBuilder.cs: line 34
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 147
at BLToolkit.Data.Linq.Builder.JoinBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\JoinBuilder.cs: line 40
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 147
at BLToolkit.Data.Linq.Builder.SelectManyBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\SelectManyBuilder.cs: line 21
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 147
at BLToolkit.Data.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\WhereBuilder.cs: line 18
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 147
at BLToolkit.Data.Linq.Builder.SelectBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\SelectBuilder.cs: line 36
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 147
at BLToolkit.Data.Linq.Builder.InsertBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\InsertBuilder.cs: line 20
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 147
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.Build() in d:\Projects\BLToolkit\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 118
at BLToolkit.Data.Linq.Query`1.GetQuery(IDataContextInfo dataContextInfo, Expression expr) in d:\Projects\BLToolkit\Source\Data\Linq\Query.cs: line 136
at BLToolkit.Data.Linq.Table`1.GetQuery(Expression expression, Boolean cache) in d:\Projects\BLToolkit\Source\Data\Linq\TableT.cs: line 106
at BLToolkit.Data.Linq.Table`1.System.Linq.IQueryProvider.Execute(Expression expression) in d:\Projects\BLToolkit\Source\Data\Linq\TableT.cs: line 179
at BLToolkit.Data.Linq.LinqExtensions.Insert(IQueryable`1 source, Table`1 target, Expression`1 setter) in d:\Projects\BLToolkit\Source\Data\Linq\LinqExtensions.cs: line 441
at AspRoleProvider.AddUsersToRoles(String[] userNames, String[] roleNames) in AspRoleProvider.cs: line 242
at Tests.Security.MembershipTest.AccountTest() in MembershipTest.cs: line 94
Код сущностей:
[TableName("Roles")]
public class Role : EntityBase<Role>
{
[Identity, PrimaryKey] public int ID { get; set; }
[Required] public string Name { get; set; }
}
[TableName("Accounts")]
public class Account : EntityBase<Account>
{
[Identity, PrimaryKey] public int ID { get; set; }
[Required] public string Email { get; set; }
}
[TableName("AccountRoles")]
public class AccountRole : EntityBase<AccountRole>
{
[Required, PrimaryKey(1)] public int AccountID { get; set; }
[Required, PrimaryKey(2)] public int RoleID { get; set; }
}
INSERT TestTable(Data)
SELECT 'value'
WHERE
NOT EXISTS(SELECT Data FROM TestTable WHERE Data = 'value');
Хотя если будет MERGE, то уже не обязательно мне кажется делать перегрузку Insert/InsertWithIdentity с предикатом, как предлагалось, или может всё-таки нужно?
2. Есть такой, например, код:
using (var db = new DbManager())
{
var q =
from ar in db.AccountRoles()join r in db.Roles() on ar.RoleID equals r.ID
join a in db.Accounts() on ar.AccountID equals a.ID
where
roleNames.Contains(r.Name) && userNames.Contains(a.Email)
select ar;
q.Delete();
}
То, какой код будет сгенерирован зависит Delete от того, что первым идет в linq-запросе, в данном случае будет такое:
DELETE[ar]FROM
[AccountRoles] [ar]
INNER JOIN [Roles] [t1] ON [ar].[RoleID] = [t1].[ID]
INNER JOIN [Accounts] [t2] ON [ar].[AccountID] = [t2].[ID]
WHERE
[t1].[Name] IN ('admin') AND [t2].[Email] IN ('admin@site.com')
Если поменять запрос
using (var db = new DbManager())
{
var q =
from r in db.Roles()join ar in db.AccountRoles() on r.ID equals ar.RoleID
join a in db.Accounts() on ar.AccountID equals a.ID
where
roleNames.Contains(r.Name) && userNames.Contains(a.Email)
select ar;
q.Delete();
}
то получим
DELETE[r]FROM
[Roles] [r]
INNER JOIN [AccountRoles] [t1] ON [r].[ID] = [t1].[RoleID]
INNER JOIN [Accounts] [t2] ON [t1].[AccountID] = [t2].[ID]
WHERE
[r].[Name] IN ('admin') AND [t2].[Email] IN ('admin@site.com')
Можно с этим что-то сделать?
ЗЫ. Пока отлаживался нашлась опечатка в файле Source\DataAccess\SqlQueryInfo.cs, строка 91? memberss
ЗЫЫ. Попробывал сделать pull-request, отредактировав прям на странице гитхаба, но видать из-за разницы в окончаниях строк он пометил весь файл как измененный. Я наверное что-то не так делаю, но мой запрос не появился в реквестах
Здравствуйте, rameel, Вы писали:
R>BLToolkit.Data.Linq.LinqException : Sequence 'value(AspRoleProvider+<>c__DisplayClass19).db.Accounts()' cannot be converted to SQL.
А как выглядит метод Accounts?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, rameel, Вы писали:
R>Можно с этим что-то сделать?
Разве что завести ещё один дополнительный параметр.
R>ЗЫ. Пока отлаживался нашлась опечатка в файле Source\DataAccess\SqlQueryInfo.cs, строка 91? memberss
Fixed.
R>ЗЫЫ. Попробывал сделать pull-request, отредактировав прям на странице гитхаба, но видать из-за разницы в окончаниях строк он пометил весь файл как измененный. Я наверное что-то не так делаю, но мой запрос не появился в реквестах
Может быть как-то можно настроить, но я не знаю. Лучше, конечно, по честному коммитить.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Holms, Вы писали:
H>интересно узнать как насчёт скорости, как новая версия себя ведёт?
Так же. Скорость от разбора выражения практически не зависит.
H>Есть ли какой-то road-map для новых фич?
Пока в планах сделать поддержку чего-то вроде Include из EF и поддержку UpdateOrInset.
H>кстати, какой адрес svn с новой версией? а то здесь вроже как нету изменений?
Здравствуйте, IT, Вы писали:
IT>С этого момента принимаются пожелания по дальнейшему развитию библиотеки
Хорошо бы добавить дополнительную возможность генерить модель в стиле EF Code First. Всё больше появляется разного рода инструментов в VS которая завязана на EF модель. Например EF Code First and Data Scaffolding with the ASP.NET MVC 3
Здравствуйте, Аноним, Вы писали:
А>Хорошо бы добавить дополнительную возможность генерить модель в стиле EF Code First. Всё больше появляется разного рода инструментов в VS которая завязана на EF модель. Например EF Code First and Data Scaffolding with the ASP.NET MVC 3
В стиле EF Code First это без атрибутов?
Если нам не помогут, то мы тоже никого не пощадим.