AX2009 EP Custom lookup with joined tables - enterprise

I'm trying to make custom lookup (in .Net) using 2 joined tables:
AxLookup nameLookup = e.LookupControl;
Proxy.QueryBuildDataSource emplTable = null;
Proxy.QueryBuildDataSource dirPartyTable = null;
using (Proxy.SysDataSetBuilder sysDataSetBuilder = proxy.SysDataSetBuilder.constructLookupDataSet(this.AxSession.AxaptaAdapter, TableMetadata.TableNum(this.AxSession, "EmplTable")))
{
nameLookup.LookupDataSet = new DataSet(this.AxSession, sysDataSetBuilder.toDataSet());
}
using (Proxy.Query query = nameLookup.LookupDataSet.DataSetViews[0].MasterDataSource.query())
{
emplTable = query.dataSourceNo(1);
dirPartyTable = emplTable.addDataSource(TableMetadata.TableNum(this.AxSession, "DirPartyTable"));
dirPartyTable.clearLinks();
dirPartyTable.addLink(TableDataFieldMetadata.FieldNum(this.AxSession, "EmplTable", "PartyId"), TableDataFieldMetadata.FieldNum(this.AxSession, "DirPartyTable", "PartyId"));
dirPartyTable.joinMode = 6; //should be an exists join
}
nameLookup.LookupDataSet.Init();
nameLookup.Fields.Add(AxBoundFieldFactory.Create(this.AxSession, nameLookup.LookupDataSetViewMetadata.ViewFields["EmplId"]));
nameLookup.Fields.Add(AxBoundFieldFactory.Create(this.AxSession, nameLookup.LookupDataSetViewMetadata.ViewFields["DirPartyTable!Name"])); //not working!!
nameLookup.SelectField = "EmplId";
Now when I use the lookup it gives me an errror about the Name field in DirPartyTable (key not found) Does someone know how to add a lookupfield in a joined table?
I'm sure the join works ok, made some other lookups with ranges on the joined table (and ofcourse no lookupfields on the second table) and that works ok.
Any help is appreciated!

This has been discussed in the following blog post:
http://palleagermark.blogspot.com/2009/12/data-set-lookups-on-enterprise-portal.html
The answer is that you cannot use a datasource with a join with the AxLookup control as it was built to support only one DataSetView and will throw this exception if you have more than one.

Related

LINQ to Entities returns empty list but it's SQL equivalent returns results

I'm sure this is something fairly common... I'm joining two entities with a mapping table and attempting to load back all relationships.
public IEnumerable<Clinic> ClientClinics(Guid clientId)
{
var clinics =
from c in _appDbContext.Clinics
join ccm in _appDbContext.ClientClinicMappings on c.ClinicId equals ccm.ClinicId
join cli in _appDbContext.Clients on ccm.ClientId equals cli.ClientId
where cli.ClientId == clientId && !c.DeletedAt.HasValue
select c;
return clinics.ToList();
}
I'm getting no results when I should be... I turned on the db logging and here's the generated SQL:
SELECT [c].[ClinicId], [c].[Address], [c].[City], [c].[CreatedAt], [c].[CreatedBy], [c].[DeletedAt], [c].[DeletedBy], [c].[ModifiedAt], [c].[ModifiedBy], [c].[Name], [c].[Phone], [c].[State], [c].[Zip]
FROM [Clinic] AS [c]
INNER JOIN [ClientClinicMapping] AS [ccm] ON [c].[ClinicId] = [ccm].[ClinicId]
INNER JOIN [Client] AS [cli] ON [ccm].[ClientId] = [cli].[ClientId]
WHERE ([cli].[ClientId] = #__clientId_0) AND [c].[DeletedAt] IS NULL
When I paste in my GUID that I'm searching with, I'm able to get results from SQL.
Anyone have any suggestions to where I should be looking or what to be trying?

Eclipselink Inheritance policy causing malformed query

There are three entities namely Employee, Person and Address. There is a parent-child relationship between Person and Employee (Employee IS-A Person).
There is a 1:1 relationship from Person to Address. (A Person is assumed to have one permanent address).
The key properties of Employee class are: 1. employeeId(pk) 2. personId(fk)
The key properties of Person class are: 1. pId(pk) 2. pCode
The key properties of Address class are: 1. addressId(pk) 2. employeeId(fk)
The following are the descriptor code snippets for Person, Employee and Address classes:
public RelationalDescriptor buildPersonDescriptor() {
RelationalDescriptor descriptor = new RelationalDescriptor();
descriptor.setJavaClass(Person.class);
descriptor.addTableName("PERSON");
descriptor.addPrimaryKeyFieldName("PERSON.PID");
// Inheritance properties.
descriptor.getInheritancePolicy().setClassIndicatorFieldName("PERSON.PCODE");
descriptor.getInheritancePolicy().addClassIndicator(Employee.class, "EMP");
// RelationalDescriptor properties.
descriptor.useSoftCacheWeakIdentityMap();
descriptor.setIdentityMapSize(100);
descriptor.useRemoteSoftCacheWeakIdentityMap();
descriptor.setRemoteIdentityMapSize(100);
descriptor.setSequenceNumberFieldName("PERSON.PID");
descriptor.setSequenceNumberName("PERSON_SEQ");
descriptor.setAlias("Person");
// Query manager.
descriptor.getDescriptorQueryManager().checkCacheForDoesExist();
//Named Queries
DirectToFieldMapping productIDMapping = new DirectToFieldMapping();
productIDMapping.setAttributeName("pId");
productIDMapping.setFieldName("PERSON.PID");
descriptor.addMapping(productIDMapping);
DirectToFieldMapping productIDMapping = new DirectToFieldMapping();
productIDMapping.setAttributeName("pCode");
productIDMapping.setFieldName("PERSON.PCODE");
descriptor.addMapping(productIDMapping);
return descriptor;
}
public RelationalDescriptor buildEmployeeDescriptor() {
RelationalDescriptor descriptor = new RelationalDescriptor();
descriptor.setJavaClass(Employee.class);
descriptor.addTableName("EMPLOYEE");
// Inheritance properties.
descriptor.getInheritancePolicy().setParentClass(Person.class);
// RelationalDescriptor properties.
descriptor.setAlias("Employee");
// Query manager.
descriptor.getDescriptorQueryManager().checkCacheForDoesExist();
//Named Queries
// Event manager.
// Mappings.
DirectToFieldMapping employeeIdMapping = new DirectToFieldMapping();
employeeIdMapping.setAttributeName("employeeId");
employeeIdMapping.setFieldName("EMPLOYEE.EID");
descriptor.addMapping(employeeIdMapping);
DirectToFieldMapping personIdMapping = new DirectToFieldMapping();
personIdMapping.setAttributeName("personId");
personIdMapping.setFieldName("EMPLOYEE.PID");
descriptor.addMapping(personIdMapping);
OneToOneMapping addressMapping = new OneToOneMapping();
addressMapping.setAttributeName("address");
addressMapping.setReferenceClass(Address.class);
addressMapping.dontUseIndirection();
addressMapping.addTargetForeignKeyFieldName("ADDRESS.EID", "EMPLOYEE.EID");
descriptor.addMapping(addressMapping);
return descriptor;
}
public RelationalDescriptor buildAddressDescriptor() {
RelationalDescriptor descriptor = new RelationalDescriptor();
descriptor.setJavaClass(com.tropics.application.products.domain.costingandpricing.SellingPriceAddOn.class);
descriptor.addTableName("ADDRESS");
descriptor.addPrimaryKeyFieldName("ADDRESS.AID");
// Descriptor properties.
descriptor.useSoftCacheWeakIdentityMap();
descriptor.setIdentityMapSize(100);
descriptor.useRemoteSoftCacheWeakIdentityMap();
descriptor.setRemoteIdentityMapSize(100);
descriptor.setSequenceNumberFieldName("ADDRESS.AID");
descriptor.setSequenceNumberName("ADDRESS_SEQ");
descriptor.setAlias("address");
// Query manager.
descriptor.getDescriptorQueryManager().checkCacheForDoesExist();
//Mappings
DirectToFieldMapping personIDMapping = new DirectToFieldMapping();
personIDMapping.setAttributeName("employeeId");
personIDMapping.setFieldName("ADDRESS.EID");
descriptor.addMapping(personIDMapping);
DirectToFieldMapping addressIDMapping = new DirectToFieldMapping();
addressIDMapping.setAttributeName("addressId");
addressIDMapping.setFieldName("ADDRESS.AID");
descriptor.addMapping(addressIDMapping);
}
Following is the code snippet for generating the dynamic query:
ExpressionBuilder employee = new ExpressionBuilder();
ReportQuery query = new ReportQuery(Employee.class,employee);
Expression address = employee.getAllowingNull("address");
query.addAttribute("pId");
query.addAttribute("pCode");
query.addAttribute("employeeId");
query.addAttribute("addressId",address.get("addressId"));
query.addNonFetchJoin(employee.leftJoin(address,
address.get("employeeId")));
resultCollection = (Vector) clientSessionHolder.eclipselinkClientSession().executeQuery(query);
On running this program, the query which is generated as per logs:
SELECT t0.PID, t0.PCODE, t1.EID, t2.AID FROM PERSON t0 LEFT OUTER JOIN ADDRESS t2 ON ((t2.EID = t1.EID),EMPLOYEE t1 WHERE ((t1.PID = t0.PID) AND (t0.PCODE = 'EMP'));
The expected query is:
SELECT t0.PID, t0.PCODE, t1.EID, t2.AID FROM PERSON t0,EMPLOYEE t1 LEFT OUTER JOIN ADDRESS t2 ON ((t2.EID = t1.EID) WHERE ((t1.PID = t0.PID) AND (t0.PCODE = 'EMP')));
The table t1 is not correctly applied in the join clause.
Could anyone help me with what's wrong with the expression?
Waiting for a positive reply.
I ran into the same problem. Left join between two not linked entities (person left join address in your case) through another entities (employee).
I used JPQL. These entities could be linked directly by IDs, but eclipselink (2.6.2) added implicit join after the left join. And referred (in left join) to ID from implicit join. It broke the query. I got the exception "ORA-00904: "T3"."ID": invalid identifier".
I solved my problem by removing left join and adding exists and subquery. I did not need to receive entity from left join.
You can try add explicit join on employee before the left join on address. It did not help me. My eclipselink changing join on implicit join. But maybe you are lucky.
Or you can add left join on employee before the left join on address. It helps. But maybe this is not what you need.

Slick function for SQL exists

I want to construct an SQL along this but try not to use sqlu.
select el.oid, el.name, el.res_cat from el
left join bk on (el.cat = bk.cat and bk.oid=100)
where not exists (select 1 from dates bd where
el.oid=bd.lots_oid and bd.bk_oid = bk.oid) and el.e_oid=bk.e_oid
Are there Slick functions for SQL exists or not exists? Thanks
Update 1
I realized my mistakes when I revisited my Slick code again. I want to apologize for the false alarm I set. This is not an answer and hopefully someone can help me to rectify my mistakes. For the time being, I am using Slick's plain SQL to continue my work.
The Slick query I constructed didn't work. It was close to the SQL I wanted. What I did was,
val elQuery = elTable.joinLeft(bkTable)
.on((el, bk) => el.cat === bk.cat && bk.oid === 100)
val query = for {
a <- elQuery if bdTable.filterNot(bd => a._2.map(_.oid === bd.bkOid).isDefined && a._1.oid === bd.elOid).exists
} yield a
finalQuery.result.statements.foreach(x => Logger.debug(s"xx => $x"))
I notice filterNot does not generate a SQL not exists. This is the other portion that lost me.
I don't have enough reputation to make comment yet. But I assume that you want to get all rows that doesn't exit in dates table. I would rewrite your query like below:
SELECT
el.oid, el.name, el.res_cat.cat
FROM
el
LEFT JOIN bk ON bk.cat = el.cat
AND bk.e_oid = el.e_oid
AND bk.oid = 100
LEFT JOIN dates bd ON bd.lots_oid = el.oid
AND bd.bk_oid = bk.oid
WHERE
bd.lots_oid IS NULL
Explanation:
Instead of taking NOT EXISTS, you can achieve the same thing by LEFT JOIN dates and specify on WHERE condition that the primary key (PK) for dates IS NULL. I don't know the PK for dates table, so I just add the column I know. You should adjust it to the PK of dates table.
LEFT JOINing and WHERE PK IS NULL ensures you that the row doesn't exist on the left joined table.

Entity Framework left outer join not working

I have an Entity Framework model with the following classes (I have simplified the classes for simpler viewing):
PuzzleItem
-PuzzleId (int, primary key)
-Title
PuzzleProgress
-ProgressId (int, primary key)
-PuzzleId (FK)
-UserId (FK)
In PuzzleItem I have a number of levels. The PuzzleProgress keeps track of which level the user is at by inserting a record of that level when the user completes a previous level. For a start a new user will have one entry in PuzzleProgress with PuzzleId = 1.
In my code, I am using the following statement to perform a left outer join, so that I will get a list of all puzzles, and indicating to me which puzzle has not been solved. I made reference to this post from StackOverflow.
Here is my code:
var result = from pzs in e.PuzzleItems
join prg in e.PuzzleProgresses on pzs equals prg.PuzzleItem
into pzs_prg_tbl
from pzs_prg in pzs_prg_tbl.DefaultIfEmpty()
where pzs_prg.UserId == userId
select new SimplePuzzleItem()
{
PuzzleId = pzs_prg.PuzzleId,
PuzzleName = pzs_prg.PuzzleItem.Title,
IsUnlocked = (pzs_prg == null?false:true)
};
After running the code, only the first level of this new user is returned (while the PuzzleItem table has 3 records).
I've tried playing around with the code but the one I pasted above is the nearest I can get to, can anybody point me in the correct direction? Thanks!
It's a bit hard to say exactly without seeing more of the code, but where pzs_prg.UserId == userId is probably negating the left outer join.
What I mean is, if you're intending PuzzleItems LEFT JOIN PuzzleProgress then you want all PuzzleItems even when there is no PuzzleProgress. But the where pzs_prg.UserId == userId means PuzzleProgress cannot be null, because it must have a UserId (of value userId). So, you effectively have an inner join.
Personally, I don't like the "correct" way of doing joins (left or inner) in linq, so this is how I would correct the linq statement:
var result = from pz in db.PuzzleItems
from pg in db.PuzzleProgresses
.Where(pg => pg.PuzzleId == pz.PuzzleId)
.Where(pg => pg.UserId == userId)
.DefaultIfEmpty()
select new
{
PuzzleId = pz.PuzzleId,
PuzzleName = pz.Title,
IsUnlocked = (pg != null)
};
This reads a lot more like SQL joins, which I learned a long time ago so it fits my thinking.
If you want to re-factor the join type syntax, look at this 'LINQ Joining in c# with multiple conditions'
I think the where clause is filtering out the records. You need to include the where clause in your left join. Like so:
var result = from pzs in e.PuzzleItems
join prg in e.PuzzleProgresses on new { pzs.PuzzleId, UserId = userId } equals new { prg.PuzzleId, prg.UserId }
into pzs_prg_tbl
from pzs_prg in pzs_prg_tbl.DefaultIfEmpty()
select new SimplePuzzleItem()
{
PuzzleId = pzs_prg.PuzzleId,
PuzzleName = pzs_prg.PuzzleItem.Title,
IsUnlocked = (pzs_prg == null?false:true)
};

How can i do this SQL in Linq? (Left outer join w/ dates)

My LINQ isnt the best, neither is my SQL but im trying to do something like this in LINQ (its kind of in pseudo-code)
select * from CarePlan c
-- only newest Referral based on r.Date (if more than one exists)
left outer join Referral r on r.CarePlanId = c.CarePlanId
where c.PatientId = 'E4A1DA8B-F74D-4417-8AC7-B466E3B3FFD0'
The data looks like this:
A Patient can have a bunch of careplans
each careplan can have 0 to n referrals (I want the newest referral per careplan - if any exist)
Would like to return a list of careplans for each patient (whether or not they have a referral or not, if it has more than one referral - grab the newest one)
Thanks for any help guys
In LINQ you use the DefaultIfEmpty to achieve a left outer join - examples at http://msdn.microsoft.com/en-us/library/bb397895.aspx
Assuming that the referrals are not a (potentially empty) collection on the care plans so you're joining two collections together ...
Your query it would be something like:
Get the latest referral per Care Plan:
var latestReferrals = from r in referrals
group r by r.CarePlanId into lr
select new { CarePlanId = lr.Key, LatestReferral = lr.OrderByDescending(lrd => lrd.Date).First()};
Find the combined details:
var q = from c in CarePlan
where c.PatientId = 'E4A1DA8B-F74D-4417-8AC7-B466E3B3FFD0'
join lr in latestReferrals on c.CarePlanId equals lr.CarePlanId into gj
from subReferral in gj.DefaultIfEmpty()
select new { CarePlanId = c.CarePlanId, LatestReferral = (subReferral == null) ? null : subReferral.LatestReferral };
Depending on whether you want many referral properties or just a few you may or may not want the whole Referral object in the second part or just extract the relevant properties.
You may be able to combine these into a single query but for readability it may be easier to keep them separate. If there is a combined solution you should also compare performance of the two approaches.
EDIT: see comment for joining patients/other tables from care plans
If Patient is joined from Referral (as per comment) then its more complex because you're doing several left outer joins. So switching to the slightly more concise syntax:
var combined = from c in carePlans
where c.PatientId = 'E4A1DA8B-F74D-4417-8AC7-B466E3B3FFD0'
from lr in latestReferral.Where(r => r.CarePlanId == c.CarePlanId).DefaultIfEmpty()
from p in patients.Where(patient => patient.PatientId == ((lr != null) ? lr.LatestReferral.PatientId : -1)).DefaultIfEmpty()
select new { c.CarePlanId, PatientName = (p == null) ? "no patient" : p.PatientName, LatestReferral = (lr == null) ? null : lr.LatestReferral };