По большому счёту изменение только одно, но оно вполне тянет на подверсию — полностью переписан конвертер 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 это без атрибутов?
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: BLToolkit 4.1 released
От:
Аноним
Дата:
29.06.11 18:16
Оценка:
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Аноним, Вы писали:
А>>Хорошо бы добавить дополнительную возможность генерить модель в стиле EF Code First. Всё больше появляется разного рода инструментов в VS которая завязана на EF модель. Например EF Code First and Data Scaffolding with the ASP.NET MVC 3
IT>В стиле EF Code First это без атрибутов?
Это может быть с атрибутами и без, лучше конечно с атрибутами и дополнительно ассоциации представленные как виртуальные свойства.
Здравствуйте, IT, Вы писали:
IT>Просьба потестировать новую версию.
Янус. Подписка на форумы. Запрос:
public static IQueryable<T> GetAllForums<T>(
DbManager mgr,
Expression<Func<IServerForum, T>> srvSelector,
Expression<Func<ISubscribedForum, T>> subsSelector)
{
var subsFrms = mgr.SubscribedForums();
return
mgr
.ServerForums()
.Where(f => !subsFrms.Any(of => of.ID == f.ID))
.Select(srvSelector)
.Union(subsFrms.Select(subsSelector));
}
...
var forums =
GetAllForums(
mgr,
f => new {f.ID, f.Name, f.Descript, Subscribed = false},
f => new {f.ID, f.Name, f.Descript, Subscribed = true})
.ToDictionary(forum => forum.ID);
Генерируется sql
(501) SELECT
t501.t496.ID as ID,
t501.t496.Name as Name,
t501.t496.Descript as Descript
FROM
server_forums as t496
WHERE
(NOT (EXISTS((497) SELECT
*
FROM
subscribed_forums as t498
WHERE
(t498.ID = t496.ID) )) )
UNION
(499) SELECT
t499.t500.ID as ID,
t499.t500.Name as Name,
t499.t500.Descript as Descript,
t499.True as c1
FROM
subscribed_forums as t500
Ругань sqlite
SQLite error
SELECTs to the left and right of UNION do not have the same number of result columns
var updated =
msgs
.Into(db.TopicInfos())
.Value(_ => _.MessageID, m => m.ID)
.Value(_ => _.ForumID, m => m.ForumID)
.Value(_ => _.AnswersCount, m => m.TopicAnswers.Count())
.Value(
_ => _.AnswersRates,
m =>
rates
.Where(r => r.Message.TopicID == m.ID && r.RateType > 0)
.Sum(r => (int)r.RateType * r.Multiplier))
.Value(
_ => _.AnswersUnread,
m => m.TopicAnswers.Count(im => !im.IsRead) + (m.IsRead ? 0 : 1))
.Value(
_ => _.AnswersSmiles,
m =>
rates
.Count(r => r.Message.TopicID == m.ID && r.RateType == MessageRates.Smile))
.Value(
_ => _.AnswersAgrees,
m =>
rates
.Count(r => r.Message.TopicID == m.ID && r.RateType == MessageRates.Agree))
.Value(
_ => _.AnswersDisagrees,
m =>
rates
.Count(r => r.Message.TopicID == m.ID && r.RateType == MessageRates.DisAgree))
.Value(
_ => _.AnswersToMeUnread,
m =>
m
.TopicAnswers
.Count(im => !im.IsRead && im.Parent.UserID == Config.Instance.SelfId))
.Value(_ => _.AnswersMarked, m => m.TopicAnswers.Count(im => im.IsMarked))
.Value(_ => _.LastUpdateDate, m => m.TopicAnswers.Max(im => (DateTime?)im.Date) ?? m.Date)
.Value(
_ => _.AnswersModeratorials,
m =>
mods
.Count(
mod =>
mod.Message.TopicID == m.ID
&& (mod.Message.LastModerated == null || mod.Create > mod.Message.LastModerated)))
.Value(_ => _.SelfRates, m => m.Rating())
.Value(_ => _.SelfSmiles, m => m.SmileCount())
.Value(_ => _.SelfAgrees, m => m.AgreeCount())
.Value(_ => _.SelfDisagrees, m => m.DisagreeCount())
.Value(_ => _.SelfModeratorials, m => m.ActiveModeratorialCount())
.Insert();
В ExpressionBuilder.cs:1201 валится с исключением InvalidOperationException — Sequence contains no matching element
method = {m.TopicAnswers.Max(im => ConvertChecked(im.Date))}
predicate = {Method = {Boolean <ConvertSelector>b__43(System.Reflection.MethodInfo)}}
Вызывается из ExpressionBuilder.cs:1051
IT>>С этого момента принимаются пожелания по дальнейшему развитию библиотеки А>Можно расценить это как пожелание .
Можно рассценить как угодно. Но лучший способ добиться результата сегодня — это сделать форк проекта на GitHub, воспроизвести проблему в виде юнит теста в dev версии проекта и послать pull request.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: BLToolkit 4.1 released
От:
Аноним
Дата:
05.07.11 11:31
Оценка:
Здравствуйте, IT, Вы писали: IT>Можно рассценить как угодно. Но лучший способ добиться результата сегодня — это сделать форк проекта на GitHub, воспроизвести проблему в виде юнит теста в dev версии проекта и послать pull request.
Уже есть: Mapping.MemberMapperTest2() (в наст. время закомментирован)
За совет спасибо, в будущем так и буду поступать.
Замыкания выражений становятся частью выражений и кешируются. В результате при любом контексте (любых значениях локальных переменных) выражение всегда вычисляется относительно кешированных значений.
void Foo(Item item)
{
using (var dbManager = new DbManager())
{
var isExistingItem = dbManager.Items.Contains(item);
// аналогично:
// var isExistingItem = dbManager.Items.Any(i => i.Id == item.Id);
}
}
В данном примере при нескольких последовательных вызовах Foo для разных объектов isExistingItem всегда будет иметь одинаковое значение (существует ли первый объект) независимо от наличия текущего объекта в базе.
Получается такой запрос:
SELECT
CASE WHEN EXISTS(
SELECT
*
FROM
[Items] [t1]
WHERE
[t1].[Id] = '<id от первого объекта>'
) THEN 1 ELSE 0 END as [c1]
Здравствуйте, Аноним, Вы писали:
А>СУБД: ssce 3.5.5692.0
Fixed.
Если нам не помогут, то мы тоже никого не пощадим.
Re: BLToolkit 4.1 released
От:
Аноним
Дата:
14.07.11 17:43
Оценка:
Здравствуйте, IT, Вы писали:
IT>С этого момента принимаются пожелания по дальнейшему развитию библиотеки
Хотелось бы чтобы Linq2DB, о которой ты говорил, увидела свет.
Здравствуйте, Аноним, Вы писали:
IT>>С этого момента принимаются пожелания по дальнейшему развитию библиотеки А>Хотелось бы чтобы Linq2DB, о которой ты говорил, увидела свет.
В планах. Уже даже завёл репу. Сейчас пофиксим некоторые баги, добавим некоторые фичи и вплотную этим займёмся.
Если нам не помогут, то мы тоже никого не пощадим.
Re[5]: Expression closures caching issue
От:
Аноним
Дата:
14.07.11 18:03
Оценка:
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Аноним, Вы писали:
А>>СУБД: ssce 3.5.5692.0
IT>Fixed.
Здравствуйте, AndrewVK, Вы писали:
AVK>Янус. Самый страшный запрос.
Fixed.
Если нам не помогут, то мы тоже никого не пощадим.
Re: BLToolkit 4.1 released
От:
Аноним
Дата:
17.07.11 13:48
Оценка:
Здравствуйте, IT, Вы писали:
IT>С этого момента принимаются пожелания по дальнейшему развитию библиотеки
Было бы очень удобно, если бы был модуль интеграции BLT для LINQPad: http://www.linqpad.net/Extensibility.aspx
Здравствуйте, IT, Вы писали:
AVK>>Янус. Подписка на форумы. Запрос:
IT>Fixed.
Не пофиксилось. Теперь запрос такой:
SELECT
[f].[ID],
[f].[Name],
[f].[Descript]
FROM
[server_forums] [f]
WHERE
NOT (EXISTS(
SELECT
*
FROM
[subscribed_forums] [of1]
WHERE
[of1].[ID] = [f].[ID]
))
UNION
SELECT
[f1].[ID],
[f1].[Name],
[f1].[Descript],
1 as [c1]
FROM
[subscribed_forums] [f1]
Отбой. Все пофиксилось. Это чудовище git extensions каким то странным образом умудрилось все удаленные изменения воспринять как локальные, а потом, после апдейта, накатить их на рабочую копию. С тагим чудовищным юзабилити я давненько не сталкивался. Все таки линуксь портит людям моск.
Здравствуйте, AndrewVK, Вы писали:
AVK>Отбой. Все пофиксилось. Это чудовище git extensions каким то странным образом умудрилось все удаленные изменения воспринять как локальные, а потом, после апдейта, накатить их на рабочую копию. С тагим чудовищным юзабилити я давненько не сталкивался. Все таки линуксь портит людям моск.
Это ты явно какую-то не ту кнопку нажал.
ЗЫ. А где теперь репозиторий януса?
ЗЫ2. На nuget не решил переходить? Довольно удобная штука оказалась.
ЗЫ3. Проект януса всё ещё под 2008-й студией?
Если нам не помогут, то мы тоже никого не пощадим.
где-то в последних ревизиях появилась проверка на NULL для аггрегатных функций, и вот такой запрос:
class Number
{
public int Number;
}
db.Number.Where(_ => false/*редуцировано для примера, просто нет записей удовлетворяющих условимям*/ ).Min(_ => _.Number)
выдаст ошибку, что де Min не должна вертать NULL...
раньше же спокойно возвращался 0.
оно так, наверно, правильней, но не всегда...
приходится делать примерно такой финт ушами:
var res = db.Number.Where(_ => false).Min(_ => (int?)_.Number)
if (res.HasValue)
return res.Value;
return 0
что не очень удобно, т.к. само поле-то не может быть NULL, а вот результат аггрегата, так запросто...
Здравствуйте, IT, Вы писали:
IT>>>А где старое место? В svn репозитории? AVK>>Да.
IT>Ну давай я ещё один вопрос задам. А при чём тут тогда GitExtensions?
При том, что речь то не про янус, а про BLT. С репозиторием януса осложнений не было.
... << RSDN@Home 1.2.0 alpha 5 rev. 1526 on Windows 7 6.1.7601.65536>>
Здравствуйте, IT, Вы писали:
IT>Ну давай я ещё один вопрос задам. А при чём тут тогда GitExtensions?
можно я тоже задам (звиняюсь за оффтоп)? у меня их несколько:
1) GitExtensions работает через прокси? А то для Pull/Push приходится через bit bash делать (гугл меня в этой беде не спас...)
2) вопрос скорее идеологический, ибо я никак не могу въехать как оно работает со всякими ветками\комитами\пушами и пулами
разберем на примере
1) я сделал пулл с гитхаба себе
2) наменял всякой ерунды
3) сделал локальный коммит, после пуша он здесь
4) попробовал сделать пуш, на что получил отлуп, мол ты друг аут оф дейт
5) сделал пулл, получил новый кусок изменений, и один пустяковый конфликт, и спокойно его зарешал
6) попробовал сделать пуш, на что мне было сказано, чтобы я не сначала закончил "слияние" методом коммита а уж потом пушился
7) сделал коммит, после пуша он здесь
8) сделал пуш, пуш прошел
так вот меня дико ставит в тупик, то, что я не понял, чего произошло на шаге 7 и в честь чего он мне приписал столь масштабные изменения, которых я вообще-то не делал...
ЗЫ.
пуши и пуллы я делаю из коммандной строки ввиду первого вопроса, может я им каких параметров недоговариваю... но пишу я "git pull" & "git push" а оно уж как-то все само делает...
Здравствуйте, ili, Вы писали: ili>1) GitExtensions работает через прокси? А то для Pull/Push приходится через bit bash делать (гугл меня в этой беде не спас...)
Да, ч/з прокси работает, главное что бы удалённый репозиторий хостил в том числе и по https (с github это так). Т.е. url репозитория https://github.com/nickname/repositoryname.git. А дальше честно говоря я помоему вообще его не настраивал — он подхватил вроде бы стандартные настройки прокси (могу ошибаться конечно), но работает именно так. По поводу остального — не знаю, я на hg.
будет ошибка которую выкинет AggregationBuilder.AggregationContext.CheckNullValue — "Function Min returns non-nullable value, but result is NULL. Use nullable version of the function instead."
потому и приходится делать Min(_ => (int?)_.Number)
хотя, за идею спс
хотя, не люблю я эти ?? и ?: читаются они фигово...
но доколе предусмотрены FirstOrDefault, то логично иметь для таких задач и MinOrDefault, имхо смысл тот же
Здравствуйте, ili, Вы писали:
ili>1) GitExtensions работает через прокси? А то для Pull/Push приходится через bit bash делать (гугл меня в этой беде не спас...)
Видимо потому что GitExtensions тут не при чём. Нужно конфигурировать git. Может здесь что-нибудь будет — http://theseekersquill.wordpress.com/tag/gitextensions/
ili>2) вопрос скорее идеологический, ибо я никак не могу въехать как оно работает со всякими ветками\комитами\пушами и пулами
Работает очень зашибато
ili>разберем на примере ili> 1) я сделал пулл с гитхаба себе ili> 2) наменял всякой ерунды ili> 3) сделал локальный коммит, после пуша он здесь ili> 4) попробовал сделать пуш, на что получил отлуп, мол ты друг аут оф дейт ili> 5) сделал пулл, получил новый кусок изменений, и один пустяковый конфликт, и спокойно его зарешал
Пул делал сразу со слиянием или в отдельную ветку?
ili> 6) попробовал сделать пуш, на что мне было сказано, чтобы я не сначала закончил "слияние" методом коммита а уж потом пушился
Всё верно. Он тебе слил твой коммит с серверным при пуле. В результате получились изменения, которые нужно закоммитить.
ili> 7) сделал коммит, после пуша он здесь ili> 8) сделал пуш, пуш прошел
ili>так вот меня дико ставит в тупик, то, что я не понял, чего произошло на шаге 7 и в честь чего он мне приписал столь масштабные изменения, которых я вообще-то не делал...
На шаге 7 был просто коммит. Произошло, как я понимаю, на шаге 5. Ты сделал пул с одновременным слиянием. Git всё слил, но насоздавал изменений. Их нужно было закомитить, что ты и сделал на шаге 7.
Я так тоже делал раньше, но сейчас решил, что лучше забирать коммиты отдельно, а потом делать слияние явно самому.
ili>пуши и пуллы я делаю из коммандной строки ввиду первого вопроса, может я им каких параметров недоговариваю... но пишу я "git pull" & "git push" а оно уж как-то все само делает...
Попробуй не pull, а fetch.
Если нам не помогут, то мы тоже никого не пощадим.
да, это я читал... в том-то и дело, что из коммандной строки у мя все шуршит, а вот почему-то как только из гуи, так работает только то, что происходит с локальным репозитарием..
ili>>разберем на примере ili>> 1) я сделал пулл с гитхаба себе ili>> 2) наменял всякой ерунды ili>> 3) сделал локальный коммит, после пуша он здесь ili>> 4) попробовал сделать пуш, на что получил отлуп, мол ты друг аут оф дейт ili>> 5) сделал пулл, получил новый кусок изменений, и один пустяковый конфликт, и спокойно его зарешал
IT>Пул делал сразу со слиянием или в отдельную ветку?
да вот на этом-то я мозг и ломаю я делал просто из GitExtensions отурыл репозитарий, тыкнул Git Bash, и там написал git pull
ili>> 6) попробовал сделать пуш, на что мне было сказано, чтобы я не сначала закончил "слияние" методом коммита а уж потом пушился
IT>Всё верно. Он тебе слил твой коммит с серверным при пуле. В результате получились изменения, которые нужно закоммитить.
IT>На шаге 7 был просто коммит. Произошло, как я понимаю, на шаге 5. Ты сделал пул с одновременным слиянием. Git всё слил, но насоздавал изменений. Их нужно было закомитить, что ты и сделал на шаге 7.
IT>Я так тоже делал раньше, но сейчас решил, что лучше забирать коммиты отдельно, а потом делать слияние явно самому.
IT>Попробуй не pull, а fetch.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, ili, Вы писали:
IT>Кстати, похоже, что сломался тест NonUpdatableOnInsert. Надо бы починить.
да, вот только помоему он "починился".
NonUpdatable.OnInsert == false — означает, что при инсерте данное поле должно вставляться, по крайней мере я так понимаю (как минимум для Identity это именно так, там 2 true и оно не обновляется и не вставляется)
т.е. тест закрепляет неверное поведение.
вобщем, я его переделал, и закоммитил в dev (ну или постарался туда закоммититть )
ЗЫ по ходу пока я тебе полрепозитария не перекурочу, я так и не въеду в этот гит... хотя, он вроде крут, и чистить плоды моей деятельности особого труда не составляет...
ЗЫ2 а зачем всетаки TableBuilder-у 2 до боли одинаковых метода MapDataReaderToObject? неужно заради оптимизации?
ЗЫ3 да, посгрису иногда нужно делать все в lower\upper case... хотя, решение и вправду не очень, я как-то пару лет назад уже занимался миграциями и тогда до кучи все делал в ловер кейз, ну и просто дернул те наработки особо не мучаясь, а проблема в том, что посгрис начинает местами быть чувствительным к регистрам таблиц\полей, в особенности если писать их в "", а без "" зачастую низя, т.к. можно легко попасть в зарезервированные слова (у мя вот в базе часто встречается "From" )
скорее оставить надо, но перетащить в Convert... лана, руки дойдут — разберусь
Здравствуйте, ili, Вы писали:
ili>да, вот только помоему он "починился".
Он вообще был неправильный.
ili>вобщем, я его переделал, и закоммитил в dev (ну или постарался туда закоммититть )
Я же вроде уже починил. Ты опять переделал
ili>ЗЫ по ходу пока я тебе полрепозитария не перекурочу, я так и не въеду в этот гит... хотя, он вроде крут, и чистить плоды моей деятельности особого труда не составляет...
В этот раз лучшим решением было бы сделать Rebase на dev, чтобы не было таких загибонов как получилось
ili>ЗЫ2 а зачем всетаки TableBuilder-у 2 до боли одинаковых метода MapDataReaderToObject? неужно заради оптимизации?
Для неё. Один, конечно, можно убрать, но только не тот который ты убрал. Но с этим как-нибудь позже.
ili>ЗЫ3 да, посгрису иногда нужно делать все в lower\upper case... хотя, решение и вправду не очень, я как-то пару лет назад уже занимался миграциями и тогда до кучи все делал в ловер кейз, ну и просто дернул те наработки особо не мучаясь, а проблема в том, что посгрис начинает местами быть чувствительным к регистрам таблиц\полей, в особенности если писать их в "", а без "" зачастую низя, т.к. можно легко попасть в зарезервированные слова (у мя вот в базе часто встречается "From" )
У там тебе на гитхабе в комментариях вопрос задал по поводу upper, lower. Если не ошибаюсь у тебя там маленькая бага.
ili>скорее оставить надо, но перетащить в Convert... лана, руки дойдут — разберусь
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Я же вроде уже починил. Ты опять переделал
война коммитов!
ммм... я не нарочно... честно
IT>В этот раз лучшим решением было бы сделать Rebase на dev, чтобы не было таких загибонов как получилось
ты учти, что говоришь с дремучим парнем из уральского леса так что говорить надо по слогам и очень громко =)
это вот надо на шестую картинку смотреть?
Хотя там есть отдельная комманда Rebase они с 6-й картинкой не одно и то же?
IT>У там тебе на гитхабе в комментариях вопрос задал по поводу upper, lower. Если не ошибаюсь у тебя там маленькая бага.
да, теперь я понял как этим пользоваться =) надо было по ссылке вконце письма сходить =))
Здравствуйте, IT, Вы писали:
IT>Вышла версия 4.1 библиотеки.
Напоролся еще на один баг:
public class Root
{
public int ID { get; set; }
public string Value { get; set; }
public int InfoID { get; set; }
}
public class LeafToRoot
{
public int ID { get; set; }
public int RootID { get; set; }
public int LeafID { get; set; }
}
public class Leaf
{
public int ID { get; set; }
public int LeafRootID { get; set; }
public string LeafValue { get; set; }
}
private void TestInternal(Expression<Func<RootInfo, bool>> selector)
{
using (var db = new DbManager())
{
var roots = db.GetTable<Root>();
roots.Delete();
roots.Insert(() => new Root { ID = 1, Value = "First", InfoID = 1 });
roots.Insert(() => new Root { ID = 2, Value = "Second", InfoID = 1 });
roots.Insert(() => new Root { ID = 3, Value = "Third", InfoID = 1 });
var l2r = db.GetTable<LeafToRoot>();
l2r.Delete();
l2r.Insert(() => new LeafToRoot { ID = 1, LeafID = 1, RootID = 1 });
l2r.Insert(() => new LeafToRoot { ID = 2, LeafID = 2, RootID = 1 });
var leafs = db.GetTable<Leaf>();
leafs.Delete();
leafs.Insert(() => new Leaf { ID = 1, LeafRootID = 1, LeafValue = "blabla" });
leafs.Insert(() => new Leaf { ID = 2, LeafRootID = 2, LeafValue = "blahblah" });
DbManager.TurnTraceSwitchOn();
var q1 = from r in roots
orderby r.Value
select new RootInfo
{
ID = r.ID,
Value = r.Value,
};
q1 = q1.Where(selector);
var q2 = (from l in leafs
join lr in l2r on l.ID equals lr.LeafID
join r in q1 on lr.RootID equals r.ID
select new
{
r.ID,
LeafRootID = lr.ID,
LeafID = l.ID
}).ToList();
}
}
[Test]
public void Test()
{
// const string param = "ir"; // С константой работает!string param = "ir"; // А так -- падает!
TestInternal(i => i.Value.Contains(param));
}
При парсинге q2 наблюдаем exception:
System.ArgumentException : An item with the same key has already been added.
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.ConvertLikePredicate(IBuildContext context, MethodCallExpression expression, String start, String end) in ExpressionBuilder.SqlBuilder.cs: line 1792
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.ConvertPredicate(IBuildContext context, Expression expression) in ExpressionBuilder.SqlBuilder.cs: line 1174
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSearchCondition(IBuildContext context, Expression expression, List`1 conditions) in ExpressionBuilder.SqlBuilder.cs: line 2001
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildWhere(IBuildContext parent, IBuildContext sequence, LambdaExpression condition, Boolean checkForSubQuery) in ExpressionBuilder.SqlBuilder.cs: line 38
at BLToolkit.Data.Linq.Builder.WhereBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in WhereBuilder.cs: line 20
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 178
at BLToolkit.Data.Linq.Builder.JoinBuilder.BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\Builder\JoinBuilder.cs: line 42
at BLToolkit.Data.Linq.Builder.MethodCallBuilder.BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) in MethodCallBuilder.cs: line 22
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.BuildSequence(BuildInfo buildInfo) in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 178
at BLToolkit.Data.Linq.Builder.ExpressionBuilder.Build() in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\Builder\ExpressionBuilder.cs: line 149
at BLToolkit.Data.Linq.Query`1.GetQuery(IDataContextInfo dataContextInfo, Expression expr) in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\Query.cs: line 134
at BLToolkit.Data.Linq.Table`1.GetQuery(Expression expression, Boolean cache) in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\TableT.cs: line 106
at BLToolkit.Data.Linq.Table`1.Execute(IDataContextInfo dataContextInfo, Expression expression) in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\TableT.cs: line 98
at BLToolkit.Data.Linq.Table`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() in C:\Projects\bl-toolkit\bltoolkit.4.1\Source\Data\Linq\TableT.cs: line 193
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList(IEnumerable`1 source)
at BLTUnitTests.Tests.TestInternal(Expression`1 selector) in Tests.cs: line 49
at BLTUnitTests.Tests.Test() in Tests.cs: line 66
Если передать константу, то в методе ConvertLikePredicate() отрабатывает ветка
if (a is SqlValue)
{
...
}
и всё чики-пыки.
Если же передать переменную, то падает в ветке: