X++ Coming Out Of QueryRun In Fetch Method - reporting

I can't seem to find the resolution for this. I have modified the Fetch method in a report, so that if the queryRun is changed, and the new ID is fetched, then the while loop starts over and a new page appears and 2 elements are executed. This part works fine, the next part does not, in each ID there are several Records which I am using Element.Execute(); and element.Send(); to process. What happens is, the first ID is selected, the element (body) of the reports is executed and the element is sent as expected, however the while loop does not go onto the next ID?
Here is the code;
public boolean fetch()
{
APMPriorityId oldVanId, newVanId;
LogisticsControlTable lLogisticsControlTable;
int64 cnt, counter;
;
queryRun = new QueryRun(this);
if (!queryRun.prompt() || !element.prompt())
{
return false;
}
while (queryRun.next())
{
if (queryRun.changed(tableNum(LogisticsControlTable)))
{
lLogisticsControlTable = queryRun.get(tableNum(LogisticsControlTable));
if (lLogisticsControlTable)
{
info(lLogisticsControlTable.APMPriorityId);
cnt = 0;
oldVanId = newVanId;
newVanId = lLogisticsControlTable.APMPriorityId;
if(newVanId)
{
element.newPage();
element.execute(1);
element.execute(2);
}
}
if (lLogisticsControlTable.APMPriorityId)
select count(recId) from lLogisticsControlTable where lLogisticsControlTable.APMPriorityId == newVanId;
counter = lLogisticsControlTable.RecId;
while select lLogisticsControlTable where lLogisticsControlTable.APMPriorityId == newVanId
{
cnt++;
if(lLogisticsControlTable.APMPriorityId == newVanId && cnt <= counter)
{
element.execute(3);
element.send(lLogisticsControlTable);
}
}
}
}
return true;
}

You are using lLogisticsControlTable as a target of both a queryRun.get() and a while select. However these two uses interfere; there are two SQL cursors to control.
Use two different record variables.

Related

Code Rewite for tuple and if else statements by using LINQ

In my C# application i am using linq. I need a help what is the syntax for if-elseif- using linq in single line. Data, RangeDate are the inputs. Here is the code:
var Date1 = RangeData.ToList();
int record =0;
foreach (var tr in Date1)
{
int id =0;
if (tr.Item1 != null && tr.Item1.port != null)
{
id = tr.Item1.port.id;
}
else if (tr.Item2 != null && tr.Item2.port != null)
{
id = tr.Item2.port.id;
}
if (id >0)
{
if(Data.Trygetvalue(id, out cdat)
{
// Do some operation. (var cdata = SumData(id, tr.item2.port.Date)
record ++;
}
}
}
I think your code example is false, your record variable is initialized to 0 on each loop so increment it is useless .
I suppose that you want to count records in your list which have an id, you can achieve this with one single Count() :
var record = Date1.Count(o => (o.Item1?.port?.id ?? o.Item2?.port?.id) > 0);
You can use following code:
var count = RangeData.Select(x => new { Id = x.Item1?.port?.id ?? x.Item2?.port?.id ?? 0, Item = x })
.Count(x =>
{
int? cdate = null; // change int to your desired type over here
if (x.Id > 0 && Data.Trygetvalue(x.Id, out cdat))
{
// Do some operation. (var cdata = SumData(x.Id, x.Item.Item2.port.Date)
return true;
}
return false;
});
Edit:
#D Stanley is completely right, LINQ is wrong tool over here. You can refactor few bits of your code though:
var Date1 = RangeData.ToList();
int record =0;
foreach (var tr in Date1)
{
int? cdat = null; // change int to your desired type over here
int id = tr.Item1?.port?.id ?? tr.Item2?.port?.id ?? 0;
if (id >0 && Data.Trygetvalue(id, out cdat))
{
// Do some operation. (var cdata = SumData(id, tr.Item2.port.Date)
record ++;
}
}
Linq is not the right tool here. Linq is for converting or querying a collection. You are looping over a collection and "doing some operation". Depending on what that operation is, trying to shoehorn it into a Linq statement will be harder to understand to an outside reader, difficult to debug, and hard to maintain.
There is absolutely nothing wrong with the loop that you have. As you can tell from the other answers, it's difficult to wedge all of the information you have into a "single-line" statement just to use Linq.

LINQ ToList() causes all records to be the last one off a coroutine

I seem to have an misunderstanding because the following code works correctly if I don't append the ToList() command:
IEnumerable<ProcessorInfo> query = (
from n in InfoGet(EMachineInfoDepth.LogicalProcessor)
select n
)
.ToList();
InfoGet looks like this:
internal static IEnumerable<ProcessorInfo> InfoGet(EMachineInfoDepth depth)
{
ProcessorInfo result = new ProcessorInfo();
// loop through all workgroups
foreach (Workgroup wg in workgroups_S)
{
result.Workgroup = wg;
if (depth >= EMachineInfoDepth.NUMANode)
{
// loop through all NUMANodes
foreach (NUMANode node in wg.NUMANodes)
{
result.NUMANode = node;
if (depth >= EMachineInfoDepth.CPU)
{
// loop through all CPUs
foreach (CPU cpu in node.CPUs)
{
result.CPU = cpu;
if (depth >= EMachineInfoDepth.Core)
{
// loop through all Cores
foreach (Core core in cpu.Cores)
{
result.Core = core;
if (depth >= EMachineInfoDepth.LogicalProcessor)
{
// loop through all LogicalProcessors
foreach (LogicalProcessor lp in core.LogicalProcessors)
{
result.LogicalProc = lp;
yield return result;
}
}
else
{
yield return result;
}
}
}
else
{
yield return result;
}
}
}
else
{
yield return result;
}
}
}
else
{
yield return result;
}
}
}
With ToList() I get the correct count, but all records equal the final element in the sequence. While I get that this could be a variable scope error in my complex coroutine, as in all iterations see the final value, why does the code work without ToList()?
My question is: what am I misunderstanding?
Problem is, you're returning reference to the same variable all the time:
ProcessorInfo result = new ProcessorInfo();
That's the only place you're actually creating new ProcessorInfo object. You only change it's properties values later, but return still the same object.
You should consider adding copy constructor into your ProcessorInfo() class, and replace every yield return result; call with yield return new ProcessorInfo(result);. That would be the easiest way to make it work.
Update
It could look like it works e.g. when you've saved some variable state somewhere during loop:
foreach(var item in query)
{
itemsList.Add(item);
propertyList.Add(item.IntProperty);
}
After that call itemsList will contain incorrect data, while propertyList will be just fine.

Retrieve records by page size

I have a big table if I use a normal query it has a timeout exception. So I want to select top 1000 then output it, the next step is to retrieve from 1001 to 2000 and log it and so on.
I am not sure how to add a parameter in my query.
int pageNumer = 0;
var query = DBContext.MyTable.Where(c=>c.FacilityID == facilityID)
.OrderBy(c=>c.FilePath)
.Skip(pageNumer*1000)
.Take(1000);
foreach(var x in query)
{
// Console.WriteLine(x.Name);
}
// I want pageNumber is incremented until it goes to the bottom of the table.
// I don't know how many records in the table.
Try this out:
int pageNumber = 0;
bool hasHitEnd = false;
while (!hasHitEnd)
{
var query = DBContext.MyTable.Where(c=>c.FacilityID == facilityID)
.OrderBy(c=>c.FilePath)
.Skip(pageNumber*1000)
.Take(1000);
foreach(var x in query)
{
// Do something
}
if (query.Count < 1000)
{
hasHitEnd = true;
}
pageNumber++;
}

How to get out of repetitive if statements?

While looking though some code of the project I'm working on, I've come across a pretty hefty method which does
the following:
public string DataField(int id, string fieldName)
{
var data = _dataRepository.Find(id);
if (data != null)
{
if (data.A == null)
{
data.A = fieldName;
_dataRepository.InsertOrUpdate(data);
return "A";
}
if (data.B == null)
{
data.B = fieldName;
_dataRepository.InsertOrUpdate(data);
return "B";
}
// keep going data.C through data.Z doing the exact same code
}
}
Obviously having 26 if statements just to determine if a property is null and then to update that property and do a database call is
probably very naive in implementation. What would be a better way of doing this unit of work?
Thankfully C# is able to inspect and assign class members dynamically, so one option would be to create a map list and iterate over that.
public string DataField(int id, string fieldName)
{
var data = _dataRepository.Find(id);
List<string> props = new List<string>();
props.Add("A");
props.Add("B");
props.Add("C");
if (data != null)
{
Type t = typeof(data).GetType();
foreach (String entry in props) {
PropertyInfo pi = t.GetProperty(entry);
if (pi.GetValue(data) == null) {
pi.SetValue(data, fieldName);
_dataRepository.InsertOrUpdate(data);
return entry;
}
}
}
}
You could just loop through all the character from 'A' to 'Z'. It gets difficult because you want to access an attribute of your 'data' object with the corresponding name, but that should (as far as I know) be possible through the C# reflection functionality.
While you get rid of the consecutive if-statements this still won't make your code nice :P
there is a fancy linq solution for your problem using reflection:
but as it was said before: your datastructure is not very well thought through
public String DataField(int id, string fieldName)
{
var data = new { Z = "test", B="asd"};
Type p = data.GetType();
var value = (from System.Reflection.PropertyInfo fi
in p.GetProperties().OrderBy((fi) => fi.Name)
where fi.Name.Length == 1 && fi.GetValue(data, null) != null
select fi.Name).FirstOrDefault();
return value;
}
ta taaaaaaaaa
like that you get the property but the update is not yet done.
var data = _dataRepository.Find(id);
If possible, you should use another DataType without those 26 properties. That new DataType should have 1 property and the Find method should return an instance of that new DataType; then, you could get rid of the 26 if in a more natural way.
To return "A", "B" ... "Z", you could use this:
return (char)65; //In this example this si an "A"
And work with some transformation from data.Value to a number between 65 and 90 (A to Z).
Since you always set the lowest alphabet field first and return, you can use an additional field in your class that tracks the first available field. For example, this can be an integer lowest_alphabet_unset and you'd update it whenever you set data.{X}:
Init:
lowest_alphabet_unset = 0;
In DataField:
lowest_alphabet_unset ++;
switch (lowest_alphabet_unset) {
case 1:
/* A is free */
/* do something */
return 'A';
[...]
case 7:
/* A through F taken */
data.G = fieldName;
_dataRepository.InsertOrUpdate(data);
return 'G';
[...]
}
N.B. -- do not use, if data is object rather that structure.
what comes to my mind is that, if A-Z are all same type, then you could theoretically access memory directly to check for non null values.
start = &data;
for (i = 0; i < 26; i++){
if ((typeof_elem) *(start + sizeof(elem)*i) != null){
*(start + sizeof(elem)*i) = fieldName;
return (char) (65 + i);
}
}
not tested but to give an idea ;)

Axapta Validation Override Always Executes Twice

In most cases, validation methods I've overridden execute twice each time the parent field is changed. Everything still works, but the InfoLog displays double messages every time.
Is there any way to prevent this?
Thanks
public boolean validate()
{
boolean ret;
int exlowValue;
int lowValue;
int highValue;
int exhighValue;
str errorMessage;
;
ret = super();
//Make sure a numeric value was entered
if (ABC_RegExValidator::validateMe("integer", int2str (ABC_Checks_checkExtremeLow.value())))
{
//get the form values
exlowValue = ABC_Checks_checkExtremeLow.value();
lowValue = str2int(ABC_Checks_checkLow.valueStr());
highValue = str2int(ABC_Checks_checkHigh.valueStr());
exhighValue = str2int(ABC_Checks_checkExtremeHigh.valueStr());
//Extreme Low must be 0 or less than all others
if (exlowValue != 0)
{
//A non-zero value was entered; it must be less than all other fields
if ((exlowValue >= lowValue && lowValue > 0) || (exlowValue >= highValue && highValue > 0) || (exlowValue >= exhighValue && exhighValue > 0))
{
//Return an error
ret = checkfailed(strFmt("#ABC197", int2str(exlowValue)));
}
else
{
//Not greater than any other value
//Success!
ret = true;
} //Greater than all others?
}
else
{
//No errors
ret = true;
} // 0?
}
else
{
//Regular expression failed
//Return an error
ret = checkfailed("#ABC192");
} //Regular expression
return ret;
}
Your description of the problem is not really clear. One can override the valite method on a form control, the validate method on a form datasource and the validatefield method on the table. That's my knowledge of version 3.0.
And how do you mean the "parent field"? I presume the table field?
If I put info messages in each of these methods they only execute once when I modify a value. That's the case in 3.0. I don't know which version you're using.
Maybe you could be more precise about which validation method you're testing?

Resources