For the same case, i have an another error :
No converter registered for type 'public.group',
I have, user, group and user_group tables.
$sql = <<<SQL
SELECT :projection
FROM "user"
LEFT JOIN user_group ON "user".id=user_group.user_id
LEFT JOIN "group" ON "group".id=user_group.group_id
WHERE "user".id = $*
GROUP BY "user".id
SQL;
$projection = $this->createProjection()
->setField('groups', 'array_agg("group") AS groups', 'public.group[]')
;
$sql = strtr($sql, [':projection' => (string) $projection->formatFields('"user"')]);
return $this->query($sql, [1], $projection);
In services.yaml :
App\Infrastructure\Model\MyDB\PublicSchema\GroupModel:
tags: [pomm.model]
App\Infrastructure\Model\MyDB\PublicSchema\UserGroupModel:
tags: [pomm.model]
App\Infrastructure\Model\MyDB\PublicSchema\UserModel:
tags: [pomm.model]
To ovoid having to register a converter for each table of the database, converters are loaded when tables model classes are loaded.
Adding the following line at the beginning of your method should resolve your problem:
$groupModel = $this->getSession()->getModel(GroupModel::class);
This also gives you the ability not to deal with the group relation name as it is stored in the structure that comes with the model.
Related
Hi guys please help solve this problem.
Need to convert this query to Laravel ORM.
$user = Auth::user();
$datas = DB::select('select * from kvits k
left join (select o.kvit_id, sum(o.amount * o.price) as sum from operations o
group by o.kvit_id) s on k.id = s.kvit_id
left join users u on k.user_id = u.id
left join users h on k.hamkor_id = h.id
left join stores s2 on k.store_id = s2.id
where k.user_id = ' $user->id ');
Main problem is how to convert this query to Larave ORM.
Basically this part
left join (select o.kvit_id, sum(o.amount * o.price) as sum from operations o
group by o.kvit_id) s on k.id = s.kvit_id
Second problem is:
Why Laravel getting the last join id in the column without taking all the ids (e.g. k.id, ****, u.id, ***, s.id on this query). And does not display data from all other joined columns.
How to get them.
I want to get data all joined tables also, like k.id, k.date, ... u.id, u.name, .... s.id, s.name, s.notes . Like belowed table.
First, Why Laravel getting the last join id in the column without taking all the ids (e.g. k.id, ****, u.id, ***, s.id on this query)., Laravel uses PDO to fetch data from the database, and PDO fetches the data by the column name and not by the table_name.column_name index.
Basically what you have to do is create your Model (the model that will contain all this data together) like that (it is a model example):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class MyUserModel extends Model {
public function addresses() {
// query all addresses of this user and returns it
}
public function phone () {
// query User's phone from database and returns it
}
// ...
}
and then when you want to use some value, for example, you want to get the User's phone ID, you just use $user->phone->id and Laravel will run the phone() method from MyUserModel class. To learn more about go to Laravel Eloquent Relationships Queries Documentation
if you want to always load these data together whenever a query with this model is made, you can use the protected $with model variable to can fetch all data. see more on Laravel Documentation.
CAUTION: If you want to customize the method query from outside the method you must call the method as a Function, for example $user->phone()->where('active', 1), and not as a Dynamic Property like $user->phone->where('active', 1).
In my ASP.NET MVC 5 application I want to list the roles of a user. I downloaded some samples that seem to be broken. Basically I want both the role ID and role name of the roles of a selected user (not the current user!).
ApplicationUser.Roles gives me an IdentityUserRole object with only RoleId and UserId.
ApplicationDbContext.Roles gives me an IdentityRole with RoleId, RoleName etc. of ALL application roles.
So what I want is a result set with the intersection of both sets while retaining full role information so that I can use both its role ID and role name.
I tried Intersect() but that didn't work because both objects are of different type. I tried the dumb style of iterating but got an exception saying the DAta Reader was already active so I am stumped :(
I tried the following on LinQPad (with the appropriate conenctions and namespaces):
string UserName = "user#email.com";
ApplicationDbContext ctx = new ApplicationDbContext();
var allroles = ctx.Roles.OrderBy(r => r.Id);
allroles.Dump(); // dumps well, 6 roles
ApplicationUser user = ctx.Users.Where(u => u.UserName.Equals(UserName, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();
var myroles = user.Roles;
myroles.Dump(); // dumps well, 3 roles
IEnumerable<IdentityRole> list = from roles in allroles
join uroles in myroles
on roles.Id equals uroles.RoleId
select roles;
list.Dump(); // exception
And while the query seems to produce no error during execution, its dumping does regardless of whether I use Dump() or an explicit foreach (IdentityRole item in list). The error I get in this case is
"Unable to reate a constant value of type 'Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole'. Only primitive types or enumeration types are supported in this context".
The only problem here is that you are not calling ToList() method which execute the query immediately (everything will be held in the memory).
For better understanding - ToList() method converts an IEnumerable<T> to a List<T>.
So, your code will look like this:
var allroles = ctx.Roles.OrderBy(r => r.Id).ToList();
var myroles = user.Roles.ToList();
You could use a combination of the two approaches you tried, where you get roles from the context that are present in the ApplicationUser's Roles property...
var roles = ApplicationDbContext.Roles
.Where(ar =>
ApplicationUser.Roles
.Select(ur =>
ur.RoleId)
.Contains(ar.RoleId));
You can do this way :
var rolesList = allroles.ToList().Join(myroles.ToList(),
left => left.Id,
right => right.RoleId,
(left,right) => left);
This way it is working for me for different scenario.
You're trying to join an in-memory list, myroles, with an IQueryable, allroles, which produces a new IQueryable: list. However, this new IQueryable is translated into SQL, so myroles must be translated into SQL as well. This is not supported for lists of non-primitive types.
The solution is to join two IQueryables:
var myroles = ctx.Users.Where(u => u.UserName == UserName).SelectMany(u => u.Roles);
var list = from role in allroles
join urole in myroles
on role.Id equals urole.RoleId
select role;
My entity "TimeRecord" has a collection of "WayPoints" and two one-to-one properties "Location" and "WayData".
Each property can be null.
I need to export all Time Records with initialized properties for a specific User.
I actually had a working solution but then I started to use NHibernateProiler and first I noticed that this code results in a ridiculous number of query’s against db.
var query = (from timeRecord in Session.Query<TimeRecord>()
.Where(tr => tr.User.Id == userid)
select timeRecord);
Then I changed my code to:
var query = (from post in Session.Query<TimeRecord>()
.Fetch(x => x.Location)
.Fetch(x => x.WayData)
.FetchMany(x => x.WayPoints)
.Where(tr => tr.User.Id == userid)
select post);
Which lead me to the Cartesian product problem.
Right now I’m experimenting with this piece of code:
var sql1 = "from TimeRecord b left outer join fetch b.Location where b.User.Id=:User_id";
var sql2 = "from TimeRecord b left outer join fetch b.WayData where b.User.Id=:User_id";
var sql3 = "from TimeRecord b left inner join fetch b.WayPoints where b.User.Id=:User_id";
var result = Session.CreateMultiQuery()
.Add(Session.CreateQuery(sql1))
.Add(Session.CreateQuery(sql2))
.Add(Session.CreateQuery(sql3))
.SetParameter("User_id", userid)
.List();
But I can’t say if this is the correct approach or if this is even possible with nHibernate. Can someone help me with that?
The 1+N issue is a usual with the entity/collection mapping and ORM tools. But NHibernate has a very nice solution how to manage it properly. It is called:
19.1.5. Using batch fetching
This setting will allow:
To continue querying the root entity (TimeRecord in our case)
No fetching inside of the query (Session.Query<TimeRecord>()). That means we do have support for correct paging. (Take(), Skip() will be executed over the flat root table)
All the collections will be loaded with their own SELECT statements (could seem as disadvantage but se below)
There will be much more less SELECTs then 1+N. All of them will be batched. E.g. by 25 records
all the native mapping (lazy loading of collections) will still be in place...
The xml mapping example:
-- class level
<class name="Location" batch-size="25 ...
-- collection level
<batch name="Locations" batch-size="25" ...
I would suggest to apply that over all your collections/classes. With Fluent mapping it could be done also with conventions
The fluent mapping:
// class
public LocationMap()
{
Id(x => x....
...
BatchSize(25);
// collection
HasMany(x => x.Locations)
...
.BatchSize(25);
Using Linq to Entity (Entity Framework) in MVC 3 project.
My model:
Table - Users
UserID (PK)
...
Table - Clients
ClientID (PK)
Table - PropertyItems
PropertyItemID (PK)
Table - MemberContactPreference (Contains PropertyItems selected by Users- many to many)
UserID(FK)
PropertyItemID(FK)
Table ClientProperties (Contains PropertyItems that belong to Clients - many to many)
ClientID (FK)
PropertyItemID (FK)
I want to list all the distinct users that have selected all the properties selected by clients.
My Approach :
I got a list of all properties for a particular client in
Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID)
Iqueryable<User> UsersMatchingClientProperties = GetAllUsers();
foreach (ClientProperty property in clientproperties)
{
UsersMatchingClientProperties = (from uem in UsersMatchingClientProperties
join ucp in GetAllMemberContactPreferences on
ucp.UserID == uem.UserID
where uem.MemberContactPreferences.SelectMany(
mcp => mcp.PropertyItemID == property.PropertyItemID)
select uem).Distinct;
}
It gives the right result only first time. As it doesn't reduce the number of items in UsersMatchingClientProperties with each iteration. actually it replaces the collection with new resultset. I want to filter out this collection with each iteration.
Also, any suggestions to do this in Lambda expression without using Linq.
Thanks
That generation of an iqueryable in a for loop seems like a dangerous thing, which could end up in a monster sql join being executed at once.
Anyway, I don't think you need that. How about something like this?
// for a given client, find all users
// that selected ALL properties this client also selected
Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID)
Iqueryable<User> allUsers= GetAllUsers();
Iqueryable<MemberContactPreference> allMemberContactProperties = GetAllMemberContactPreferences();
Iqueryable<User> UsersMatchingClientProperties = allUsers
.Where(user => allMemberContactProperties
.Where(membP => membP.UserID==user.UserID)
.All(membP => clientProperties
.Select(clientP => clientP.PropertyID)
.Contains(membP.PropertyID)
)
);
Here is an alternative query in case you want the users that selected ANY property for a given client
// for a given client, find all users
// that selected ANY properties this client also selected
Iqueryable<ClientProperty> clientProperties = GetClientProperties(ClientID)
Iqueryable<User> allUsers= GetAllUsers();
Iqueryable<MemberContactPreference> allMemberContactProperties = GetAllMemberContactPreferences();
Iqueryable<User> UsersMatchingClientProperties = clientproperties
.Join(allMembersContactProperties, // join clientproperties with memberproperties
clientP => clientP.PropertyItemID,
membP => membP.PropertyItemID,
(clientP, membP) => membP)) // after the join, ignore the clientproperties, keeping only memberproperties
.Distinct() // distinct is optional here. but perhaps faster with it?
.Join(allUsers, //join memberproperties with users
membP => membP.UserID,
user => user.UserID,
(membP, user) => user)) // after the join, ignore the member properties, keeping only users
.Distinct();
I trust Hugo did a good job suggesting ways to improve your query (+1). But that does not yet explain the cause of your problem, which is the modified closure pitfall.
I think that after your loop there is some code that actually executes the query in UsersMatchingClientProperties. At that moment the query is executed with the last value of the loop variable property! (The loop variable is the closure in each query delegate that is created in an iteration, and it is modified by each iteration).
Change the loop like this:
foreach (ClientProperty property in clientproperties)
{
var property1 = property;
...
and use property1 in the query. That should solve the cause of the problem. But as said, it looks like the whole process can be improved.
I have this sql that i want to have written in linq extension method returning an entity from my edm:
SELECT p.[Id],p.[Firstname],p.[Lastname],prt.[AddressId],prt.[Street],prt.[City]
FROM [Person] p
CROSS APPLY (
SELECT TOP(1) pa.[AddressId],a.[ValidFrom],a.[Street],a.[City]
FROM [Person_Addresses] pa
LEFT OUTER JOIN [Addresses] AS a
ON a.[Id] = pa.[AddressId]
WHERE p.[Id] = pa.[PersonId]
ORDER BY a.[ValidFrom] DESC ) prt
Also could this be re-written in linq extension method using 3 joins?
Assuming you have set the Person_Addresses table up as a pure relation table (i.e., with no data besides the foreign keys) this should do the trick:
var persons = model.People
.Select(p => new { p = p, a = p.Addresses.OrderByDescending(a=>a.ValidFrom).First() })
.Select(p => new { p.p.Id, p.p.Firstname, p.p.LastName, AddressId = p.a.Id, p.a.Street, p.a.City });
The first Select() orders the addresses and picks the latest one, and the second one returns an anonymous type with the properties specified in your query.
If you have more data in your relation table you're gonna have to use joins but this way you're free from them. In my opinion, this is more easy to read.
NOTE: You might get an exception if any entry in Persons have no addresses connected to them, although I haven't tried it out.