Entity Framework 5 CF Firebird - Same Code creates different queries - entity-framework

I got a huge problem with the SQL generation in my REST-Service:
The following Code is generating 2 different SQL-Statements (PC Win8 vs Server 2008 R2):
private void GetData()
{
ctPostBox = new CTPostBox(GetDBConnection(DatabaseKind.dkGlobal));
var lQry = (from d in ctPostBox.CONTACTUSER.Include("CONTACT").Include("CONTACT.DBLINK").Include("CONTACT.CONTACTLASTUSED").Include("CONTACT.EMAILACCOUNT")
where ((d.CONTACT.ID_DBLINK == 0 || d.CONTACT.ID_DBLINK == null) || (d.CONTACT.DBLINK.MANDNR == UserConfig.mandNr))
select new
{
READDATE = d.READDATE,
ID = d.CONTACT.ID,
DBID = d.CONTACT.DBID,
DATEINSERT = d.CONTACT.DATEINSERT,
FROMUSER = d.CONTACT.FROMUSER,
FILENAME = d.CONTACT.FILENAME,
KIND = d.CONTACT.KIND,
MSGID = d.CONTACT.MSGID,
ID_DBLINK = d.CONTACT.ID_DBLINK,
DBID_DBLINK = d.CONTACT.DBID_DBLINK,
SUBJECT = d.CONTACT.SUBJECT,
STATE = d.CONTACT.STATE,
DATEINCOME = d.CONTACT.DATEINCOME,
DATESEND = d.CONTACT.DATESEND,
ID_EMAILACCOUNT = d.CONTACT.ID_EMAILACCOUNT,
DBID_EMAILACCOUNT = d.CONTACT.DBID_EMAILACCOUNT,
GESCHST = d.CONTACT.DBLINK.GESCHST,
d.CONTACT.DBLINK.KEYTYP,
KONTO = d.CONTACT.EMAILACCOUNT.NAME
});
WriteFile(#"C:\Temp\log.log",lQry.ToString());
}
SQL Development PC (everything is fine):
SELECT
"C"."ID_CONTACT" AS "ID_CONTACT",
"C"."READDATE" AS "READDATE",
"C"."DBID_CONTACT" AS "DBID_CONTACT",
"D"."DATEINSERT" AS "DATEINSERT",
"D"."FROMUSER" AS "FROMUSER",
"D"."FILENAME" AS "FILENAME",
"D"."KIND" AS "KIND",
"D"."MSGID" AS "MSGID",
"D"."ID_DBLINK" AS "ID_DBLINK",
"D"."DBID_DBLINK" AS "DBID_DBLINK",
"D"."SUBJECT" AS "SUBJECT",
"D"."STATE" AS "STATE",
"D"."DATEINCOME" AS "DATEINCOME",
"D"."DATESEND" AS "DATESEND",
"D"."ID_EMAILACCOUNT" AS "ID_EMAILACCOUNT",
"D"."DBID_EMAILACCOUNT" AS "DBID_EMAILACCOUNT",
"F"."GESCHST" AS "GESCHST",
"F"."KEYTYP" AS "KEYTYP",
"H"."NAME" AS "NAME"
FROM "CONTACTUSER" AS "C"
INNER JOIN "CONTACT" AS "D" ON ("C"."ID_CONTACT" = "D"."ID") AND ("C"."DBID_CONTACT" = "D"."DBID")
LEFT OUTER JOIN "DBLINK" AS "F" ON ("D"."ID_DBLINK" = "F"."ID") AND ("D"."DBID_DBLINK" = "F"."DBID")
INNER JOIN "EMAILACCOUNT" AS "H" ON ("D"."ID_EMAILACCOUNT" = "H"."ID") AND ("D"."DBID_EMAILACCOUNT" = "H"."DBID")
WHERE ((0 = "D"."ID_DBLINK") OR ("D"."ID_DBLINK" IS NULL)) OR ("F"."MANDNR" = #p__linq__0)
SQL Server (multible JOINS on DBLINK, very slow):
SELECT
"C"."ID_CONTACT" AS "ID_CONTACT",
"C"."READDATE" AS "READDATE",
"C"."DBID_CONTACT" AS "DBID_CONTACT",
"F"."DATEINSERT" AS "DATEINSERT",
"F"."FROMUSER" AS "FROMUSER",
"F"."FILENAME" AS "FILENAME",
"F"."KIND" AS "KIND",
"F"."MSGID" AS "MSGID",
"F"."ID_DBLINK" AS "ID_DBLINK",
"F"."DBID_DBLINK" AS "DBID_DBLINK",
"F"."SUBJECT" AS "SUBJECT",
"F"."STATE" AS "STATE",
"F"."DATEINCOME" AS "DATEINCOME",
"F"."DATESEND" AS "DATESEND",
"F"."ID_EMAILACCOUNT" AS "ID_EMAILACCOUNT",
"F"."DBID_EMAILACCOUNT" AS "DBID_EMAILACCOUNT",
"J"."GESCHST" AS "GESCHST",
"L"."KEYTYP" AS "KEYTYP",
"N"."NAME" AS "NAME"
FROM "CONTACTUSER" AS "C"
INNER JOIN "CONTACT" AS "D" ON ("C"."DBID_CONTACT" = "D"."DBID") AND ("C"."ID_CONTACT" = "D"."ID")
LEFT OUTER JOIN "CONTACT" AS "F" ON ("C"."DBID_CONTACT" = "F"."DBID") AND ("C"."ID_CONTACT" = "F"."ID")
LEFT OUTER JOIN "DBLINK" AS "H" ON ("F"."DBID_DBLINK" = "H"."DBID") AND ("F"."ID_DBLINK" = "H"."ID")
LEFT OUTER JOIN "DBLINK" AS "J" ON ("F"."DBID_DBLINK" = "J"."DBID") AND ("F"."ID_DBLINK" = "J"."ID")
LEFT OUTER JOIN "DBLINK" AS "L" ON ("F"."DBID_DBLINK" = "L"."DBID") AND ("F"."ID_DBLINK" = "L"."ID")
LEFT OUTER JOIN "EMAILACCOUNT" AS "N" ON ("F"."DBID_EMAILACCOUNT" = "N"."DBID") AND ("F"."ID_EMAILACCOUNT" = "N"."ID")
WHERE ((0 = "D"."ID_DBLINK") OR ("F"."ID_DBLINK" IS NULL)) OR ("H"."MANDNR" = #p__linq__0)
And here the code first data:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove();
modelBuilder.Entity<CONTACT>()
.HasOptional(b => b.DBLINK)
.WithMany()
.HasForeignKey(b => new { b.ID_DBLINK, b.DBID_DBLINK });
modelBuilder.Entity<CONTACT>()
.HasKey(d => new { d.ID, d.DBID })
.HasMany(d => d.CONTACTUSER)
.WithRequired(d => d.CONTACT)
.HasForeignKey(l => new { l.ID_CONTACT, l.DBID_CONTACT });
modelBuilder.Entity<CONTACT>()
.HasRequired(b => b.EMAILACCOUNT)
.WithMany()
.HasForeignKey(b => new { b.ID_EMAILACCOUNT, b.DBID_EMAILACCOUNT });
modelBuilder.Entity<CONTACT>().HasKey(a => new { a.ID, a.DBID });
modelBuilder.Entity<DBLINK>().HasKey(a => new { a.ID, a.DBID });
modelBuilder.Entity<CONTACTUSER>().HasKey(a => new { a.ID, a.DBID });
modelBuilder.Entity<EMAILACCOUNT>().HasKey(a => new { a.ID, a.DBID });
base.OnModelCreating(modelBuilder);
}
I'm searching for days now. How can the same code generate different SQL-Statements?
IIS Version (Development PC: IIS 8, Server IIS7.5)
Both environments are using the same Database-Server (Firebird 2.5)
I copied the whole project to the server, no changes...
The Projekt is set to .NET 4.5
I created a sample WPF-Projekt with this function and it resulted using the RIGHT SQL on the server
FirebirdSql.Data.FirebirdClient.dll Version 3.0.2.0 EntityFramework.dll 5.0.0
Do you have any idea what can infuence the query creation this way?

Related

EF Core Filtered Include with Select

Sorry for my English.
I ran into a mistake with EF Core 5.0.9 when is use Filtered Include and Select in same time and i don't know is a bug or feature. :)
return await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToListAsync();
In this time the first elements is select is not from filtered include just from a normal include. SQL Script from Profiler:
SELECT
[u].[Id],
[u].[FirstName],
[u].[LastName],
(
SELECT TOP(1) [e].[Id]
FROM [dbo].[EventUser] AS [e]
WHERE [u].[Id] = [e].[UserId]) AS [EventUserId],
(
SELECT TOP(1) [e0].[IsCelebrated]
FROM [dbo].[EventUser] AS [e0]
WHERE [u].[Id] = [e0].[UserId]) AS [IsCelebrated],
(
SELECT TOP(1) [e1].[IsEventAdmin]
FROM [dbo].[EventUser] AS [e1]
WHERE [u].[Id] = [e1].[UserId]) AS [IsEventAdmin],
(
SELECT TOP(1) [e2].[IsInviteAccepted]
FROM [dbo].[EventUser] AS [e2]
WHERE [u].[Id] = [e2].[UserId]) AS [IsInviteAccepted],
(
SELECT TOP(1) [e3].[IsInvited]
FROM [dbo].[EventUser] AS [e3]
WHERE [u].[Id] = [e3].[UserId]) AS [IsInvited]
FROM [dbo].[User] AS [u]
WHERE ([u].[TOROLT] <> CAST(1 AS bit))
AND [u].[Id] IN (2, 1, 3, 4, 5)
But if is separate filtered include and select, than it work's fine, but this select complete record and not the part of him from database:
var a = await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.ToListAsync();
return a.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToList();
Any idea why is this, and how can i solve solve this?
THX
Include is completely ignored if you have custom projection Select, so your filter will be also ignored. It is not a bug, Include works only when you get whole entity from query.
Anyway consider to rewrite your query:
var query =
from u in _dbContext.User
where !u.TOROLT && ids.Contains(u.Id)
from eu in u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId)
.Take(1)
.DefaultIfEmpty()
select new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = eu.Id,
IsCelebrated = eu.IsCelebrated,
IsEventAdmin = eu.IsEventAdmin,
IsInviteAccepted = eu.IsInviteAccepted,
IsInvited = eu.IsInvited,
};
var result = await query.ToListAsync();

Linq EF --> Contains condition does not work

I have a problem: I have this table
ID
Title
GenreID
Genre
Duration
1
Movie1
1,2
Cartoon, Family
80
2
Movie2
3,4
Horror, Drama
76
3
Movie3
4
Drama
110
4
Movie4
1,2
Cartoon, Family
200
I need to filter by Genre, I make my query in this way, For example if I want all movies with Genre Cartoon (2), I make:
request.genre = 2
var query1 = from q in Movies
select new Response.MovieResponse
{
MovieId = q.MovieId,
Name = q.Title,
Genres = q.Genre,
GenreIds = q.GenreID
};
if (request != null)
{
if (!string.IsNullOrEmpty(request.Title))
query1 = query1.Where(co => co.Name.StartsWith(request.name));
if (!string.IsNullOrEmpty(request.genre))
if (int.Parse(request.genre) > 0)
{
query1.AsEnumerable().Where(ca =>
request.genre .Any(x => ca.GenreIds.Contains(x)));
}
}
List<Response.StoreResponse> stores = new List<Response.StoreResponse>();
stores = await query1.ToListAsync();
I expected 2 records inside but the filter on genre does not work. I always have back all movies. The filter by title run
I tried
if (!string.IsNullOrEmpty(request.category))
if (int.Parse(request.category) > 0)
{
string[] cat = { request.category };
var q1 = query1.Where(ca => ca.CategoriesIds.Contains(cat.ToString()));
l = q1.ToList();
}
but I have error:
The LINQ expression 'DbSet<Store>()
.Join(
inner: DbSet<Country>(),
outerKeySelector: s => s.CountryId,
innerKeySelector: c => (Nullable<int>)c.CountryId,
resultSelector: (s, c) => new TransparentIdentifier<Store, Country>(
Outer = s,
Inner = c
))
.Join(
inner: DbSet<City>(),
outerKeySelector: ti => ti.Outer.CityId,
innerKeySelector: c0 => (Nullable<long>)c0.CityId,
resultSelector: (ti, c0) => new TransparentIdentifier<TransparentIdentifier<Store, Country>, City>(
Outer = ti,
Inner = c0
))
.Where(ti0 => ti0.Outer.Outer.StatusId == (Nullable<int>)2)
.Where(ti0 => DbSet<Operator>()
.Where(o => EF.Property<Nullable<Guid>>(ti0.Outer.Outer, "StoreId") != null && object.Equals(
objA: (object)EF.Property<Nullable<Guid>>(ti0.Outer.Outer, "StoreId"),
objB: (object)EF.Property<Nullable<Guid>>(o, "StoreId")))
.Count() > 0)
.Where(ti0 => string.Join<long>(
separator: ",",
values: DbSet<StoresCategory>()
.Where(s0 => EF.Property<Nullable<Guid>>(ti0.Outer.Outer, "StoreId") != null && object.Equals(
objA: (object)EF.Property<Nullable<Guid>>(ti0.Outer.Outer, "StoreId"),
objB: (object)EF.Property<Nullable<Guid>>(s0, "StoreId")))
.Select(s0 => s0.CategoryId)
.ToList()).Contains(__ToString_0))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

How to dynamically change property in lambda expression

I have written a function below. In second line of code I have written "Where(t => t.PickTicket == EmployeeId)". I want to change (t.PickTicket) property dynamically. How can I achieve this??? And how can I make below code more better and shorter?
Thanks in advance.
public List<TicketJoin> GetTicketJoin(string EmployeeId)
{
var query = _context.tickets.Where(t => t.PickTicket == EmployeeId).Join
(
_context.employees, t => t.CreatedBy, e => e.EmployeeId, (t, e) => new
{
t.Id,
e.Employee_Name,
e.Department,
e.Location,
t.Subject,
t.MainCatId,
t.SubCatId,
t.PickTicket,
t.Status,
t.CreateDate
}).Join
(
_context.MainCategory, t => t.MainCatId, m => m.MainCatId, (t, m) => new
{
t.Id,
t.Employee_Name,
t.Department,
t.Location,
t.Subject,
t.MainCatId,
m.MainCatName,
t.SubCatId,
t.PickTicket,
t.Status,
t.CreateDate
}).Join
(
_context.SubCategory, t => t.SubCatId, s => s.SubCatId, (t, s) => new
{
t.Id,
t.Employee_Name,
t.Department,
t.Location,
t.Subject,
t.MainCatId,
t.MainCatName,
t.SubCatId,
s.SubCatName,
t.PickTicket,
t.Status,
t.CreateDate
}).ToList();
var TicketJoin = query.ToList().Select(r => new TicketJoin
{
EmployeeName = r.Employee_Name,
Subject = r.Subject,
Location = r.Location,
MainCatName = r.MainCatName,
SubCatName = r.SubCatName,
CreateDate = r.CreateDate
}).ToList();
return TicketJoin;
}
}
How can I make it shorter?
By using database view to hide all those joins. In such case you will have have only simple query with single condition.
How can I make it dynamic?
Do you really need it dynamic? You can just do this:
public List<TicketJoin> GetTicketJoin(Expression<Func<Ticket, bool>> condition)
{
var query = _context.Tickets.Where(condition)...
}
and call it
var result = this.GetTicketJoin(t => t.PickTicket == employeeId);
It is not fully dynamic but it allows calling code specifying the condition.
Fully dynamic solution where you would be able to pass the property name as string requires using Expression trees to build query. There also used to be Dynamic-Linq library for this purpose.

zf1: chained joins

I'm getting an error with a query, my question is: can i chain joins?
My first join is to the primary table, but my second join is to the table joined to the primary table. This is the query:
$query = $this->getDbTable()->select()
->from(array('ca' => 'contracts_allotment'),
array('id',
'contracts_rooms_id' => new Zend_Db_Expr("CONCAT(room_type_desc, '-', room_characteristics_desc)")
))
->join(array('cr' => 'contracts_rooms'),
'ca.contract_rooms_id = cr.id',
array())
->join(array('rt' => 'room_types'),
'cr.room_id = rt.id',
array('room_type_desc'))
->join(array('rc' => 'room_characteristics'),
'cr.char_id = rc.id',
array('room_characteristics_desc'))
->where('contract_id = ?', $contractId);
var_dump($this->getDbTable()->fetchAll($query));die;
I'm getting:
Select query cannot join with another table"
The error comes from Zend/Db/Table/Select::assemble()
Here you have some inside assemble():
// Check each column to ensure it only references the primary table
if ($column) {
if (!isset($from[$table]) || $from[$table]['tableName'] != $primary) {
var_dump($from[$table]['tableName'], $primary);die;
require_once 'Zend/Db/Table/Select/Exception.php';
throw new Zend_Db_Table_Select_Exception('Select query cannot join with another table');
}
}
The var_dump() prints:
string(10) "room_types" string(19) "contracts_allotment"
Any idea?
Don't forget to lock the tables when doing joins:
$query = $this->getDbTable()->select()
->setIntegrityCheck(false)
->from(array('ca' => 'contracts_allotment'),
array('id',
'contracts_rooms_id' => new Zend_Db_Expr("CONCAT(room_type_desc, '-', room_characteristics_desc)")
))
->join(array('cr' => 'contracts_rooms'),
'ca.contract_rooms_id = cr.id',
array())
->join(array('rt' => 'room_types'),
'cr.room_id = rt.id',
array('room_type_desc'))
->join(array('rc' => 'room_characteristics'),
'cr.char_id = rc.id',
array('room_characteristics_desc'))
->where('contract_id = ?', $contractId);
->setIntegrityCheck(false) should at least get you a new error.

DBIx::Class query fails, yet dump from DBIC_TRACE works

I cannot rewrite the current DB schema right now BTW, but that's beside the point to the issue I've hit, so please ignore the table structure :D
I'm running this DB query:
my $rs = $dbx->resultset('Result')->search(
{
'result_hnd' => 16078055,
'seasons.outdoor' => 'venue.outdoors',
'seasons.start_date' => { '<=' => 'meet.date_end' },
'seasons.end_date' => { '>=' => 'meet.date_begin' },
},
{
'join' => [
{
'team' => {
'league_teams' => {
'league' => 'seasons',
},
},
},
{
'meet' => 'venue'
},
],
'+select' => ['seasons.season_hnd','seasons.name','seasons.start_date','seasons.end_date','meet.date_begin','meet.date_end'],
'+as' => ['season_hnd','season_name','s_start','s_end','m_start','m_end'],
columns => ['result_hnd'],
group_by => ['seasons.season_hnd'],
}
);
When I run this, I get no results. With DBIC_TRACE on, I see the generated SQL as:
SELECT me.result_hnd, seasons.season_hnd, seasons.name, seasons.start_date, seasons.end_date, meet.date_begin, meet.date_end FROM track.result me JOIN track.team team ON team.team_hnd = me.team_hnd LEFT JOIN track.league_team league_teams ON league_teams.team_hnd = team.team_hnd LEFT JOIN track.league league ON league.league_hnd = league_teams.league_hnd LEFT JOIN track.season seasons ON seasons.league_hnd = league.league_hnd OR seasons.league_hnd = league.parent_league_hnd JOIN track.meet meet ON meet.meet_hnd = me.meet_hnd JOIN track.venue venue ON venue.venue_hnd = meet.venue_hnd WHERE ( ( result_hnd = ? AND seasons.end_date >= ? AND seasons.outdoor = ? AND seasons.start_date <= ? ) ) GROUP BY seasons.season_hnd: '16078055', 'meet.date_begin', 'venue.outdoors', 'meet.date_end'
When I copy and paste this statement into my MYSQL client (and interpolate the placeholders), like this:
SELECT me.result_hnd, seasons.season_hnd, seasons.name, seasons.start_date, seasons.end_date, meet.date_begin, meet.date_end
FROM track.result me
JOIN track.team team ON team.team_hnd = me.team_hnd
LEFT JOIN track.league_team league_teams ON league_teams.team_hnd = team.team_hnd
LEFT JOIN track.league league ON league.league_hnd = league_teams.league_hnd
LEFT JOIN track.season seasons ON seasons.league_hnd = league.league_hnd OR seasons.league_hnd = league.parent_league_hnd
JOIN track.meet meet ON meet.meet_hnd = me.meet_hnd
JOIN track.venue venue ON venue.venue_hnd = meet.venue_hnd
WHERE ( ( result_hnd = 16078055 AND seasons.end_date >= meet.date_begin AND seasons.outdoor = venue.outdoors AND seasons.start_date <= meet.date_end ) )
GROUP BY season_hnd;
I get the exact result I expect (7 records).
This is really bizarre. To all intents and purposes, isn't that exactly the same query? Am I missing something in my debugging? Or is something else happening at the DBIx::Class::ResultSet layer that isn't being dumped?
To tell SQL::Abstract that the value on the right is actually an identifier, you can do the following (as outlined in the docs):
{
'result_hnd' => 16078055,
'seasons.outdoor' => { -ident => 'venue.outdoors' },
'seasons.start_date' => { '<=' => { -ident => 'meet.date_end' } },
'seasons.end_date' => { '>=' => { -ident => 'meet.date_begin' } },
},
To all intents and purposes, isn't that exactly the same query?
(For all intents and purposes, ...)
No. In the log, you have something equivalent to
result_hnd = '16078055'
AND seasons.end_date >= 'meet.date_begin'
AND seasons.outdoor = 'venue.outdoors'
AND seasons.start_date <= 'meet.date_end'
or maybe
result_hnd = 16078055
AND seasons.end_date >= 'meet.date_begin'
AND seasons.outdoor = 'venue.outdoors'
AND seasons.start_date <= 'meet.date_end'
In your attempt, you used
result_hnd = 16078055
AND seasons.end_date >= meet.date_begin
AND seasons.outdoor = venue.outdoors
AND seasons.start_date <= meet.date_end
Sorry, I don't have the solution for you.