NHibernate Linq Expression dynamic projection - nhibernate

How can i dynamically change the selected columns in the generated sql query when using a linq expression?
Its a new session for each time the query is executed.
Even when I set the MapExp as null after first creation an then changing the bool value to false, it still generates the column in the sql query.
The code runs in a wpf application.
System.Linq.Expressions.Expression<Func<Entity, Model>> MapExp = x => new Model
{
Id=xId,
Count= LoadFormulaField ? x.Count: null,
...
};
var result = session.Query<Entity>().Select(MapExp))

Your problem seems to be the ternary-conditional as part of the expression which is causing the "Count" column to always be queried.
One option to avoid this could be:
var query = session.Query<Entity>();
IQueryable<Model> result = null;
if (LoadFormulaField)
{
result = query.Select(x => new Model
{
Id = x.Id,
Count = x.Count,
});
}
else
{
result = query.Select(x => new Model
{
Id = x.Id,
});
}
Which would get a little less ugly if you separate in a couple of methods I think.

Related

Linq2DB can't translate a mapped column in Where clause

I'm working with a legacy Oracle database that has a column on a table which stores boolean values as 'Y' or 'N' characters.
I have mapped/converted this column out like so:
MappingSchema.Default.SetConverter<char, bool>(ConvertToBoolean);
MappingSchema.Default.SetConverter<bool, char>(ConvertToChar);
ConvertToBoolean & ConvertToChar are simply functions that map between the types.
Here's the field:
private char hasDog;
[Column("HAS_DOG")]
public bool HasDog
{
get => ConvertToBoolean(hasDog);
set => hasDog = ConvertToChar(value);
}
This has worked well for simply retrieving data, however, it seems the translation of the following:
var humanQuery = (from human in database.Humans
join vetVisit in database.VetVisits on human.Identifier equals vetVisit.Identifier
select new HumanModel(
human.Identifier
human.Name,
human.HasDog,
vetVisit.Date,
vetVisit.Year,
vetVisit.PaymentDue
));
// humanQuery is filtered by year here
var query = from vetVisits in database.VetVisits
select new VetPaymentModel(
(humanQuery).First().Year,
(humanQuery).Where(q => q.HasDog).Sum(q => q.PaymentDue), -- These 2 lines aren't correctly translated to Y/N
(humanQuery).Where(q => !q.HasDog).Sum(q => q.PaymentDue)
);
As pointed out above, the .Where clause here doesn't translate the boolean comparison of HasDog being true/false to the relevant Y/N values, but instead a 0/1 and results in the error
ORA-01722: invalid number
Is there any way to handle this case? I'd like the generated SQL to check that HAS_DOG = 'Y' for instance with the specified Where clause :)
Notes
I'm not using EntityFramework here, the application module that this query exists in doesn't use EF/EFCore
You can define new mapping schema for your particular DataConnection:
var ms = new MappingSchema();
builder = ms.GetFluentMappingBuilder();
builder.Entity<Human>()
.Property(e => e.HasDog)
.HasConversion(v => v ? 'Y' : 'N', p => p == 'Y');
Create this schema ONCE and use when creating DataConnection

second entity query is executed with errors

I have 2 linq queries. First query does nothing because of unique index and this is OK. But second also does nothing while it should add records . If I bypass first query second query works. Should I refresh entity ? How ?
foreach (var product in productList)
{
cc2nexo_SubiektProduct newproduct = new cc2nexo_SubiektProduct();
newproduct.Name = product.Name;
newproduct.VAT = product.VAT;
newproduct.Id = product.Id;
foreach (var stawkaVAT in myNexo_ExitoEntities.StawkiVat)
{
if (stawkaVAT.Stawka * 100 == tryconvert_dec(newproduct.VAT))
{
newproduct.VAT_Id = stawkaVAT.Id;
}
}
myNexo_ExitoEntities.cc2nexo_SubiektProduct.Add(newproduct);
SurroundWithTryCatchDB(() =>
{
myNexo_ExitoEntities.SaveChanges();
});
}
var orders = (from myorders in myNexo_ExitoEntities.temp_SubiektOrderList
select myorders).ToList();
foreach (var order in orders)
{
cc2nexo_SubiektOrderList neworder = new cc2nexo_SubiektOrderList();
neworder.Data_utworzenia_sprawy = tryconvert_date(order.Data_utworzenia_sprawy);
neworder.Data_modyfikacji_sprawy = tryconvert_date(order.Data_modyfikacji_sprawy);
neworder.Data_umowy = tryconvert_date(order.Data_umowy);
neworder.Id = order.Id;
myNexo_ExitoEntities.cc2nexo_SubiektOrderList.Add(neworder);
SurroundWithTryCatchDB(() =>
{
myNexo_ExitoEntities.SaveChanges();
});
Debug.WriteLine(neworder.LastName);
}
I am receiving an error
Cannot insert duplicate key row in object 'dbo.cc2nexo_SubiektProduct' with unique index 'K_ID'. The duplicate key value is (1).The statement has been terminated
The reason of problem was SurroundWithTryCatchDB procedure not listed in my question. Because first query caused exception due to unique index all next SaveChanges did not work. I have changed my first query to work with unique values and now everything is OK.

Convert SQL to LINQ with group by

I'm stumped trying to convert the following sql to linq:
SELECT t.* FROM(SELECT mwfieldid,MAX([TimeStamp]) AS MaxValue, BatchDocumentID
FROM mw_BatchField
GROUP BY mwfieldid,BatchDocumentID) x
JOIN mw_BatchField t ON x.mwfieldid = t.mwfieldid
AND x.MaxValue = t.TimeStamp
and x.BatchDocumentID = t.BatchDocumentID
So far I had to convert it to a stored proc to get it to work. I'd rather know how to write this correctly in linq. I tried using a sql to linq converter (http://www.sqltolinq.com/) which produced this code that had errors in it: (Are these converters any good? It didn't seem to produce anything useful with a few tries.)
From x In (
(From mw_BatchFields In db.mw_BatchFields
Group mw_BatchFields By
mw_BatchFields.MWFieldID,
mw_BatchFields.BatchDocumentID
Into g = Group
Select
MWFieldID,
MaxValue = CType(g.Max(Function(p) p.TimeStamp),DateTime?),
BatchDocumentID)
)
Join t In db.mw_BatchFields
On New With { .MWFieldID = CInt(x.MWFieldID), .MaxValue = CDate(x.MaxValue), .BatchDocumentID = CInt(x.BatchDocumentID) }
Equals New With { .MWFieldID = t.MWFieldID, .MaxValue = t.TimeStamp, .BatchDocumentID = t.BatchDocumentID }
Select
BatchFieldID = t.BatchFieldID,
BatchDocumentID = t.BatchDocumentID,
MWFieldID = t.MWFieldID,
TimeStamp = t.TimeStamp,
value = t.value,
DictionaryValue = t.DictionaryValue,
AutoFilled = t.AutoFilled,
employeeID = t.employeeID
Seems like a lot of code for such a simple query, and it doesn't compile.
So for every combination of mwfieldid and BatchDocumentID you want all columns of the row with the highest TimeStamp? This is something which is much easier to express in LINQ than SQL so I'm not surprised that an automated converter is making a meal of it.
You should be able to do:
Mw_BatchFields.GroupBy(x => new { x.Mwfieldid, x.BatchDocumentId })
.SelectMany(x => x.Where(y => y.TimeStamp == x.Max(z => z.TimeStamp)))
This (like your SQL) will return multiple rows per grouping key if there is more than one row in the group that shares the same maximum TimeStamp. If you only want row per key, you could use:
Mw_BatchFields.GroupBy(x => new { x.Mwfieldid, x.BatchDocumentId })
.Select(x => x.OrderByDescending(y => y.TimeStamp).First())
Edit:
Sorry, just twigged that you're working in VB, not C#, so not quite what you were looking for, but if you can live with the lambda syntax style, I think the above can be translated as:
Mw_BatchFields.GroupBy(Function(x) New With {x.Mwfieldid, x.BatchDocumentId}).Select(Function(x) x.OrderByDescending(Function(y) y.TimeStamp).First())
and:
Mw_BatchFields.GroupBy(Function(x) New With {x.Mwfieldid, x.BatchDocumentId}).SelectMany(Function(x) x.Where(Function(y) y.TimeStamp = x.Max(Function(z) z.TimeStamp)))

Group By Sum Linq to SQL in C#

Really stuck with Linq to SQL grouping and summing, have searched everywhere but I don't understand enough to apply other solutions to my own.
I have a view in my database called view_ProjectTimeSummary, this has the following fields:
string_UserDescription
string_ProjectDescription
datetime_Date
double_Hours
I have a method which accepts a to and from date parameter and first creates this List<>:
List<view_UserTimeSummary> view_UserTimeSummaryToReturn =
(from linqtable_UserTimeSummaryView
in datacontext_UserTimeSummary.GetTable<view_UserTimeSummary>()
where linqtable_UserTimeSummaryView.datetime_Week <= datetime_To
&& linqtable_UserTimeSummaryView.datetime_Week >= datetime_From
select linqtable_UserTimeSummaryView).ToList<view_UserTimeSummary>();
Before returning the List (to be used as a datasource for a datagridview) I filter the string_UserDescription field using a parameter of the same name:
if (string_UserDescription != "")
{
view_UserTimeSummaryToReturn =
(from c in view_UserTimeSummaryToReturn
where c.string_UserDescription == string_UserDescription
select c).ToList<view_UserTimeSummary>();
}
return view_UserTimeSummaryToReturn;
How do I manipulate the resulting List<> to show the sum of the field double_Hours for that user and project between the to and from date parameters (and not separate entries for each date)?
e.g. a List<> with the following fields:
string_UserDescription
string_ProjectDescription
double_SumOfHoursBetweenToAndFromDate
Am I right that this would mean I would have to return a different type of List<> (since it has less fields than the view_UserTimeSummary)?
I have read that to get the sum it's something like 'group / by / into b' but don't understand how this syntax works from looking at other solutions... Can someone please help me?
Thanks
Steve
Start out by defining a class to hold the result:
public class GroupedRow
{
public string UserDescription {get;set;}
public string ProjectDescription {get;set;}
public double SumOfHoursBetweenToAndFromDate {get;set;}
}
Since you've already applied filtering, the only thing left to do is group.
List<GroupedRow> result =
(
from row in source
group row by new { row.UserDescription, row.ProjectDescription } into g
select new GroupedRow()
{
UserDescription = g.Key.UserDescription,
ProjectDescription = g.Key.ProjectDescription,
SumOfHoursBetweenToAndFromDate = g.Sum(x => x.Hours)
}
).ToList();
(or the other syntax)
List<GroupedRow> result = source
.GroupBy(row => new {row.UserDescription, row.ProjectDescription })
.Select(g => new GroupedRow()
{
UserDescription = g.Key.UserDescription,
ProjectDescription = g.Key.ProjectDescription,
SumOfHoursBetweenToAndFromDate = g.Sum(x => x.Hours)
})
.ToList();

Avoid repeating an expression in a LINQ to Entities query

I have the following query:
val = val.Select(item => new SimpleBill { CTime = item.CTime, Description = item.Description, ID = item.ID,
IDAccount = item.IDAccount, OrderNumber = item.OrderNumber, PSM = item.PSM, Sum = item.Sum,
Type = (BillType)item.Type,
ByteStatus = ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault().Status,
LastPaymentDate = ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault().CTime,
LastPaymentSum = ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault().Sum });
}
Is it possible to avoid repeating the ((Bill)item).Payments.OrderByDescending(payment => payment.ID).FirstOrDefault() part 3 times? I tried turning it into a method and into a delegate - the code compiled in both cases, but produced an exception at runtime.
You can use the let contstruct as follows:
val = from item in val
let lastPayment = ((Bill)item).Payments
.OrderByDescending(payment => payment.ID)
.FirstOrDefault()
select new SimpleBill
{
lastPayment.CTime,
//Rest of fields
}
However, as you may noticed this uses the LINQ Query syntax vs. Method syntax. IIRC let is only available in the former.