Refactoring the C# function to reduce its cognitive complexity - c#

I'm getting the following error from SonarCube: "Refactor this method to reduce its Cognitive Complexity from 28 to the 15 allowed".
Thing is, with this high Cognitive Complexity score, I am not sure how to drive it down to required 15. I have tried to move three if statements to different methods, but it has lowered the score by only 6 pts. How should I refactor given code in order to meet the requirement?
[HttpGet]
public IActionResult GetTileViewer(string productId = "", string edgeId = "", string sizeId = "", double? thickness = null)
{
if (string.IsNullOrWhiteSpace(productId))
{
return new ObjectResult(new { Error = "You need to specify a product id" })
{ StatusCode = (int)HttpStatusCode.InternalServerError };
}
var commerceService = new CommerceService();
var isImperial = commerceService.IsImperialMeasure();
var id = 0;
Guid guid;
RockstarProductTile product = null;
if (Guid.TryParse(productId, out guid))
{
product = ProductUtilities.GetProduct<RockstarProductTile>(guid);
}
else if (int.TryParse(productId, out id))
{
product = ProductUtilities.GetProduct<RockstarProductTile>(id);
}
if (product != null)
{
double? length = null;
double? width = null;
try
{
if (!string.IsNullOrWhiteSpace(sizeId))
{
var split = sizeId.Split('-');
length = double.Parse(split[0]);
width = double.Parse(split[1]);
}
var variants = product.Variants
.Where(x => string.IsNullOrWhiteSpace(edgeId) || commerceService.GetRelatedEntries(x, CommerceAssociationGroups.Edge)
.Where(y => y.ContentLink.ID.ToString() == edgeId || y.ContentGuid.ToString() == edgeId).Any());
if (isImperial)
{
variants = variants
.Where(x => length == null || x.Rockstar_RockstarProductTileVariant_TileLengthInches == length || x.Rockstar_RockstarProductTileVariant_TileLengthFeet == length)
.Where(x => width == null || x.Rockstar_RockstarProductTileVariant_TileWidthInches == width || x.Rockstar_RockstarProductTileVariant_TileWidthFeet == width)
.Where(x => thickness == null || x.Rockstar_RockstarProductTileVariant_TileThicknessInches == thickness || x.Rockstar_RockstarProductTileVariant_TileThicknessFeet == thickness);
}
else
{
variants = variants
.Where(x => length == null || x.Rockstar_RockstarProductTileVariant_TileLengthInches == length)
.Where(x => width == null || x.Rockstar_RockstarProductTileVariant_TileWidthInches == width)
.Where(x => thickness == null || x.Rockstar_RockstarProductTileVariant_TileThicknessInches == thickness);
}
var variant = variants.FirstOrDefault();
if (variant != null)
{
var tileViewer = commerceService.GetRelatedEntries(variant, CommerceAssociationGroups.TileViewer).FirstOrDefault() as RockstarTileViewer;
var images = tileViewer?.Rockstar_TileViewer_Images?.Items.Select(x => _urlUtilities.GetRelativeUrl(x.ContentLink)).ToList();
var tempId = 0;
Guid tempGuid;
RockstarProductEdge edge = null;
if (Guid.TryParse(edgeId, out tempGuid))
{
edge = ProductUtilities.GetProduct<RockstarProductEdge>(tempGuid);
}
else if (int.TryParse(edgeId, out tempId))
{
edge = ProductUtilities.GetProduct<RockstarProductEdge>(tempId);
}
if (images != null)
{
return new JsonResult(new TileViewerDataObject { Label = edge?.Rockstar_RockstarProductEdge_EdgeName?.SupAndSubOnly() ?? product.DisplayName, Images = images });
}
return new JsonResult(new TileViewerDataObject { Label = edge?.Rockstar_RockstarProductEdge_EdgeName?.SupAndSubOnly() ?? product.DisplayName });
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during get tile viewer: {Message}", ex.Message);
}
}
return new JsonResult(new TileViewerDataObject());
}

AFAIK among the largest contributors to the cognitive complexity are nested expressions, so try reduce nesting. For example invert if - from if (product != null) to:
if (product == null)
{
return new JsonResult(new TileViewerDataObject());
}
double? length = null; // also can be moved into the try block, not needed here
double? width = null;// also can be moved into the try block, not needed here
try
{
...
}
...
Then getting product should be moved to separate method (also for quite some time you can inline the variable declaration for out variables like id and guid) :
private RockstarProductTile GetProduct(string productId)
{
RockstarProductTile result = null;
if (Guid.TryParse(productId, out var guid))
{
product = ProductUtilities.GetProduct<RockstarProductTile>(guid);
}
else if (int.TryParse(productId, out var id))
{
product = ProductUtilities.GetProduct<RockstarProductTile>(id);
}
return result;
}
And in the method body just call it:
RockstarProductTile product = GetProduct(productId);
Then apply the same approaches to the try body (also possibly move to separate method) - try inverting at least some of the if's (if (variant != null) is a good candidate) and move getting edge by edgeId into separate method.
Also try to get methods to be less then one screen (by breaking down it in smaller ones, finding variant can be moved to separate function too).

Related

How to overcome foreach loop for list object dynamically

I'm swapping my values in List Object on some conditions and update List Object value.
Currently, what I'm doing is
- Looping on each object through List
- Check If condition is net
- Swap values
public static void SwapMinMaxIfNull<T>(this IEnumerable<T> rows, string reportfor)
{
if (reportfor.Equals("Comparison"))
{
var row = rows as IEnumerable<RiskBoardDataToExport>;
try
{
if (rows.Any())
{
var Tests = row.Where(min => min.MinGaitSpeed == null && min.MaxGaitSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinGaitSpeed = test.MaxGaitSpeed;
test.MaxGaitSpeed = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinTUGTime == null && min.MaxTUGTime != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinTUGTime = test.MaxTUGTime;
test.MaxTUGTime = null;
}
}
// again check for next object
Tests = row.Where(min => min.MinBergScoreSpeed == null && min.MaxBergScoreSpeed != null).ToList();
if (Tests != null)
{
foreach (RiskBoardDataToExport test in Tests)
{
test.MinBergScoreSpeed = test.MaxBergScoreSpeed;
test.MaxBergScoreSpeed = null;
}
}
//.. for brevity
}
}
}
Can I do it in better way? I know about PropertyInfo i.e. Can check property name and get value etc, but, not having any hint to get this done.
Thanks
It's not exactly what you're asking for, but you can combine the clauses in your Where statements and then have a few if statements in the body:
public static void SwapMinMaxIfNull(this IEnumerable<RiskBoardDataToExport> rows,
string reportfor)
{
if (rows = null) return;
if (reportfor.Equals("Comparison", StringComparison.OrdinalIgnoreCase))
{
foreach (var row in rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null)))
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
}
}
As this is an operation where order of the items in the list does not matter, you can easily speed this up by parallelization (you can read up on that here).
So, what you should do, is handle this foreach loop in a parallel way and combine it with Rufus L's optimized code for the fastest result.
var rows = rows.Where(r =>
(r.MinGaitSpeed == null && r.MaxGaitSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null) ||
(r.MinBergScoreSpeed == null && r.MaxBergScoreSpeed != null))
Parallel.ForEach(rows, (row) => {
{
if (row.MinGaitSpeed == null)
{
row.MinGaitSpeed = row.MaxGaitSpeed;
row.MaxGaitSpeed = null;
}
if (row.MinTUGTime == null)
{
row.MinTUGTime = row.MaxTUGTime;
row.MaxTUGTime = null;
}
if (row.MinBergScoreSpeed == null)
{
row.MinBergScoreSpeed = row.MaxBergScoreSpeed;
row.MaxBergScoreSpeed = null;
}
}
Note that this requires the System.Threading.Tasks namespace, that's where the Parallel class is.

Acumatica: Ability to override DefaultEndpointImpl to add advanced custom logic to contract REST API

I'm wondering if it possible to override DefaultEndpointImpl.cs or add my own API logic in another file?
I'm struggling with a few API calls which require the logic in this file to be overridden or added to. For example I am able to create a purchase receipt for a PO via the api successfully, however I'm not able to add a "Transfer Receipt" purchase receipt in the same way.
I've included various non-standard fields to the API endpoints that reference the original transfer, transfer order and shipment but have been unsuccessful. The API calls succeeds but no lines are added. I've been able to get lines to update their quantity once generated but can't add or delete current lines.
The problem looks to stem from this code, which seems to add functionality that correctly handles adding Purchase order lines logic of the graph but doesn't do anything special for other receipt types.
[FieldsProcessed(new[] { "POLineNbr", "POOrderType", "POOrderNbr" })]
protected void PurchaseReceiptDetail_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity) {
var receiptEntry = (POReceiptEntry)graph;
var lineNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "POLineNbr") as EntityValueField;
var orderType = targetEntity.Fields.SingleOrDefault(f => f.Name == "POOrderType") as EntityValueField;
var orderNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "POOrderNbr") as EntityValueField;
bool insertViaAddPO = lineNbr != null && orderNbr != null && orderType != null;
if (!insertViaAddPO && (lineNbr != null || orderType != null || orderNbr != null)) {
throw new PXException(PO.Messages.POTypeNbrLineNbrMustBeFilled);
}
var detailsCache = receiptEntry.transactions.Cache;
if (insertViaAddPO) {
receiptEntry.filter.Cache.Remove(receiptEntry.filter.Current);
receiptEntry.filter.Cache.Insert(new POReceiptEntry.POOrderFilter());
var filter = receiptEntry.filter.Current; var state = receiptEntry.filter.Cache.GetStateExt(filter, "OrderType") as PXStringState;
if (state != null && state.AllowedLabels.Contains(orderType.Value)) {
orderType.Value = state.ValueLabelDic.Single(p => p.Value == orderType.Value).Key;
}
receiptEntry.filter.Cache.SetValueExt(filter, "OrderType", orderType.Value);
receiptEntry.filter.Cache.SetValueExt(filter, "OrderNbr", orderNbr.Value);
receiptEntry.filter.Update(filter);
var orders = receiptEntry.poLinesSelection.Select().Select(r => r.GetItem<POReceiptEntry.POLineS>());
var order = orders.FirstOrDefault(o => o.LineNbr == int.Parse(lineNbr.Value));
if (order == null) {
throw new PXException(PO.Messages.PurchaseOrderLineNotFound);
}
order.Selected = true;
receiptEntry.poLinesSelection.Update(order);
receiptEntry.Actions["AddPOOrderLine2"].Press();
} else {
detailsCache.Current = detailsCache.Insert();
}
}
After a lot of trial and error I have managed to get everything working as I need. However I'm concerned that since this is not documented information that it may be subject to change in the near future.
The methods in the class DefaultEndpointImpl are called via reflection. To add custom functionality for my default endpoint I have extended the default class and added the necessary attributes to my class and functions.
My custom class adds the following:
Overides the default PurchaseReceiptDetail_Insert function to add transfer receipt functionality.
Adds ReceiptDetail_Insert method to allow adding an IN receipt referencing
The first one was pretty straight forward and I'm happy with the outcome. The second one took a lot of time and debugging to get right, is hacky and probably doesn't work with serial tracked items. The reason it's taken so long is because there are many unknowns and I've essentially been guessing based on the code in the base class. For example I'm not even sure at which stage this function fires. It looks the functions completely override some other default logic which is invisible.
So take it or leave it! Maybe I will reach out to Acumatica to see if I can get more information.
using PX.Api;
using PX.Api.ContractBased;
using PX.Api.ContractBased.Models;
using PX.Data;
using PX.Objects.PO;
using PX.Objects.IN;
using System;
using System.Linq;
using PX.Objects.CM;
using PX.Objects.CS;
namespace AcuStock
{
[PXVersion("5.30.001", "AcuStock")]
[PXVersion("6.00.001", "AcuStock")]
[PXVersion("17.200.001", "AcuStock")]
public class AcuStockEndpointImpl : PX.Objects.DefaultEndpointImpl
{
[FieldsProcessed(new[] { "POLineNbr", "POOrderType", "POOrderNbr", "OrigLineNbr", "OrigRefNbr" })]
protected new void PurchaseReceiptDetail_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity){
var lineNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "OrigLineNbr") as EntityValueField;
var refNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "OrigRefNbr") as EntityValueField;
var receiptQty = targetEntity.Fields.SingleOrDefault(f => f.Name == "ReceiptQty") as EntityValueField;
var location = targetEntity.Fields.SingleOrDefault(f => f.Name == "Location") as EntityValueField;
var inventoryID = targetEntity.Fields.SingleOrDefault(f => f.Name == "InventoryID") as EntityValueField;
bool insertViaAddTR = lineNbr != null && refNbr != null;
var receiptEntry = (POReceiptEntry) graph;
if (insertViaAddTR){
receiptEntry.addReceipt.Cache.Remove(receiptEntry.addReceipt.Current);
receiptEntry.addReceipt.Cache.Insert(new POReceiptEntry.POReceiptLineS());
var filter = receiptEntry.addReceipt.Current;
receiptEntry.addReceipt.Cache.SetValueExt(filter, "OrigRefNbr", refNbr.Value);
receiptEntry.addReceipt.Cache.SetValueExt(filter, "OrigLineNbr", lineNbr.Value);
receiptEntry.addReceipt.Cache.SetValueExt(filter, "InventoryID", inventoryID.Value);
receiptEntry.addReceipt.Cache.SetValueExt(filter, "ReceiptQty", receiptQty.Value);
if (location != null)
receiptEntry.addReceipt.Cache.SetValueExt(filter, "LocationID", location.Value);
receiptEntry.addReceipt.Update(filter);
var lines = receiptEntry.addReceipt.Select().Select(r => r.GetItem<POReceiptEntry.POReceiptLineS>());
var line = lines.FirstOrDefault(o => o.OrigLineNbr == int.Parse(lineNbr.Value));
if (line == null){
throw new PXException(PX.Objects.PO.Messages.PurchaseOrderLineNotFound);
}
receiptEntry.addPOReceiptLine2.Press();
} else {
base.PurchaseReceiptDetail_Insert(graph, entity, targetEntity);
}
var allocations = (targetEntity.Fields.SingleOrDefault(f => string.Equals(f.Name, "Allocations")) as EntityListField).Value ?? new EntityImpl[0];
if (allocations.Any(a => a.Fields != null && a.Fields.Length > 0)){
// Remove automatically added allocation
if (receiptEntry.splits.Current != null){
receiptEntry.splits.Delete(receiptEntry.splits.Current);
}
}
}
[FieldsProcessed(new[] { "OrigLineNbr", "OrigRefNbr", "Quantity", "Location" })]
protected void ReceiptDetails_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity) {
var lineNbr = targetEntity.Fields.SingleOrDefault(f => f.Name == "OrigLineNbr") as EntityValueField;
var receiptQty = targetEntity.Fields.SingleOrDefault(f => f.Name == "Quantity") as EntityValueField;
var location = targetEntity.Fields.SingleOrDefault(f => f.Name == "Location") as EntityValueField;
var allocations = (targetEntity.Fields.SingleOrDefault(f => string.Equals(f.Name, "Allocations")) as EntityListField).Value ?? new EntityImpl[0];
var hasAllocations = allocations.Any(a => a.Fields != null && a.Fields.Length > 0);
var receiptEntry = (INReceiptEntry) graph;
string transferNbr = receiptEntry.receipt.Current.TransferNbr;
var detailsCache = receiptEntry.transactions.Cache;
if (lineNbr == null || transferNbr == null){
detailsCache.Current = detailsCache.Insert();
return;
}
INTran newtran = null;
decimal newtranqty = Decimal.Parse(receiptQty.Value);
decimal newtrancost = 0m;
receiptEntry.ParseSubItemSegKeys();
using (new PXReadBranchRestrictedScope())
{
foreach (PXResult<INTransitLine, INLocationStatus2, INTransitLineLotSerialStatus, INSite, InventoryItem, INTran> res in
PXSelectJoin<INTransitLine,
InnerJoin<INLocationStatus2, On<INLocationStatus2.locationID, Equal<INTransitLine.costSiteID>>,
LeftJoin<INTransitLineLotSerialStatus,
On<INTransitLine.transferNbr, Equal<INTransitLineLotSerialStatus.transferNbr>,
And<INTransitLine.transferLineNbr, Equal<INTransitLineLotSerialStatus.transferLineNbr>>>,
InnerJoin<INSite, On<INSite.siteID, Equal<INTransitLine.toSiteID>>,
InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<INLocationStatus2.inventoryID>>,
InnerJoin<INTran,
On<INTran.docType, Equal<INDocType.transfer>,
And<INTran.refNbr, Equal<INTransitLine.transferNbr>,
And<INTran.lineNbr, Equal<INTransitLine.transferLineNbr>,
And<INTran.invtMult, Equal<shortMinus1>>>>>>>>>>,
Where<INTransitLine.transferNbr, Equal<Required<INTransitLine.transferNbr>>, And<INTransitLine.transferLineNbr, Equal<Required<INTransitLine.transferLineNbr>>>>,
OrderBy<Asc<INTransitLine.transferNbr, Asc<INTransitLine.transferLineNbr>>>>
.Select(receiptEntry, transferNbr, lineNbr.Value))
{
INTransitLine transitline = res;
INLocationStatus2 stat = res;
INTransitLineLotSerialStatus lotstat = res;
INSite site = res;
InventoryItem item = res;
INTran tran = res;
if (stat.QtyOnHand == 0m || (lotstat != null && lotstat.QtyOnHand == 0m))
continue;
if (newtran == null) {
if (!object.Equals(receiptEntry.receipt.Current.BranchID, site.BranchID))
{
INRegister copy = PXCache<INRegister>.CreateCopy(receiptEntry.receipt.Current);
copy.BranchID = site.BranchID;
receiptEntry.receipt.Update(copy);
}
newtran = PXCache<INTran>.CreateCopy(tran);
newtran.OrigBranchID = newtran.BranchID;
newtran.OrigTranType = newtran.TranType;
newtran.OrigRefNbr = transitline.TransferNbr;
newtran.OrigLineNbr = transitline.TransferLineNbr;
newtran.BranchID = site.BranchID;
newtran.DocType = receiptEntry.receipt.Current.DocType;
newtran.RefNbr = receiptEntry.receipt.Current.RefNbr;
newtran.LineNbr = (int)PXLineNbrAttribute.NewLineNbr<INTran.lineNbr>(receiptEntry.transactions.Cache, receiptEntry.receipt.Current);
newtran.InvtMult = (short)1;
newtran.SiteID = transitline.ToSiteID;
newtran.LocationID = transitline.ToLocationID;
newtran.ToSiteID = null;
newtran.ToLocationID = null;
newtran.BaseQty = 0m;
newtran.Qty = 0m;
newtran.UnitCost = 0m;
newtran.Released = false;
newtran.InvtAcctID = null;
newtran.InvtSubID = null;
newtran.ReasonCode = null;
newtran.ARDocType = null;
newtran.ARRefNbr = null;
newtran.ARLineNbr = null;
newtran.ProjectID = null;
newtran.TaskID = null;
newtran.CostCodeID = null;
newtran.TranCost = 0m;
receiptEntry.splits.Current = null;
newtran = receiptEntry.transactions.Insert(newtran);
receiptEntry.transactions.Current = newtran;
if (receiptEntry.splits.Current != null)
{
receiptEntry.splits.Delete(receiptEntry.splits.Current);
}
}
if (hasAllocations){
newtranqty = 0m;
foreach (var allocation in allocations) {
var newsplitqty = allocation.Fields.SingleOrDefault(f => f.Name == "Quantity") as EntityValueField;
var newsplitlocation = allocation.Fields.SingleOrDefault(f => f.Name == "Location") as EntityValueField;
INTranSplit newsplit = this.addReceiptSplitLine(receiptEntry, stat, lotstat, transitline, newtran, item, Decimal.Parse(newsplitqty.Value), newsplitlocation.Value);
newtrancost += newsplit.BaseQty.Value * newsplit.UnitCost.Value;
newtranqty += newsplit.BaseQty.Value;
}
break;
} else {
INTranSplit newsplit = this.addReceiptSplitLine(receiptEntry, stat, lotstat, transitline, tran, item, newtranqty, null);
newtrancost += newsplit.BaseQty.Value * newsplit.UnitCost.Value;
newtranqty += newsplit.BaseQty.Value;
}
}
receiptEntry.UpdateTranCostQty(newtran, newtranqty, newtrancost);
}
}
[FieldsProcessed(new[] { "OrigLineNbr" })]
protected void ReceiptAllocations_Insert(PXGraph graph, EntityImpl entity, EntityImpl targetEntity) {
// no-op
}
private INTranSplit addReceiptSplitLine(INReceiptEntry receiptEntry, INLocationStatus2 stat, INTransitLineLotSerialStatus lotstat, INTransitLine transitline, INTran tran, InventoryItem item, Decimal qty, string location){
INTranSplit newsplit;
decimal newsplitqty;
if (lotstat.QtyOnHand == null)
{
newsplit = new INTranSplit();
newsplit.InventoryID = stat.InventoryID;
newsplit.IsStockItem = true;
newsplit.FromSiteID = transitline.SiteID;
newsplit.SubItemID = stat.SubItemID;
newsplit.LotSerialNbr = null;
newsplitqty = qty;
}
else
{
newsplit = new INTranSplit();
newsplit.InventoryID = lotstat.InventoryID;
newsplit.IsStockItem = true;
newsplit.FromSiteID = lotstat.FromSiteID;
newsplit.SubItemID = lotstat.SubItemID;
newsplit.LotSerialNbr = lotstat.LotSerialNbr;
newsplitqty = qty;
}
newsplit.DocType = receiptEntry.receipt.Current.DocType;
newsplit.RefNbr = receiptEntry.receipt.Current.RefNbr;
newsplit.LineNbr = tran.LineNbr;
newsplit.SplitLineNbr = (int)PXLineNbrAttribute.NewLineNbr<INTranSplit.splitLineNbr>(receiptEntry.splits.Cache, receiptEntry.receipt.Current);
newsplit.UnitCost = 0m;
newsplit.InvtMult = (short)1;
newsplit.SiteID = transitline.ToSiteID;
newsplit.PlanID = null;
newsplit.Released = false;
newsplit.ProjectID = null;
newsplit.TaskID = null;
if (location == null) {
newsplit.LocationID = lotstat.ToLocationID ?? transitline.ToLocationID;
} else {
receiptEntry.splits.SetValueExt<INTranSplit.locationID>(newsplit, location);
}
newsplit = receiptEntry.splits.Insert(newsplit);
newsplit.MaxTransferBaseQty = newsplitqty;
newsplit.BaseQty = newsplitqty;
newsplit.Qty = newsplit.BaseQty.Value;
receiptEntry.UpdateCostSubItemID(newsplit, item);
receiptEntry.SetCostAttributes(tran, newsplit, item, tran.OrigRefNbr);
newsplit.UnitCost = PXCurrencyAttribute.BaseRound(receiptEntry, newsplit.UnitCost);
receiptEntry.splits.Update(newsplit);
return newsplit;
}
}
}

Linq to object freezing

I have some LINQ to Object Query Code:
List<ePayDocumentOperation> additionalDocuments = new List<ePayDocumentOperation>();
try
{
additionalDocuments = (from op in possibleClientDocuments
let senderAccountID = GetSenderAccountID(con, op)
where Filter.Wallets.Contains(senderAccountID)
select op).ToList();
}
catch (Exception ex)
{
// some catcher
}
possibleClientDocuments object contains about 3 000 records. Not so much but the query freeze and I don't get result. I don't get any execeptions. I try to select code to try catch block but I don't see any exceptions and at the same time I don't get result too.
How to check the problem of query and why I don't getting result?
Thanks!
P.S.
private static long GetSenderAccountID(LavaPayEntities Con, ePayDocumentOperation operation)
{
long result = default(long);
// Payment
if (operation.OperationPatternID == Chart.Operations.PS_Payment)
{
var ipnRequest = Con.eIPNRequests.FirstOrDefault(ir => ir.OperationID == operation.ID);
if (ipnRequest == null) return result;
if (ipnRequest.ClientAccountID != null && ipnRequest.ClientAccountID != 0) return (long)ipnRequest.ClientAccountID;
var clientAccount = Con.eAccounts.FirstOrDefault(a => a.Email.Email == ipnRequest.ClientEmail);
if (clientAccount != null)
{
result = clientAccount.ID;
}
else
{
var client = ipnRequest.Client;
if (client != null)
{
clientAccount = client.Accounts.FirstOrDefault();
if (clientAccount != null)
{
result = clientAccount.ID;
}
}
}
}
// Other Operations
else
{
result = operation.SenderAccountID.HasValue ? operation.SenderAccountID.Value : 0;
}
return result;
}

linq conditional query

What would be the best practice for setting a status depending on several other "columns" retrieved in a linq query.
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = 0
});
I'd like to set the status to either 0, 1, 2.
(ApprovedDate == null and DeclinedDate == null) --> 0
(ApprovedDate != null and DeclinedDate == null) --> 1
(DeclinedDate != null) --> 3
So perhaps something like:
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = (q.CreatedDate == null && q.DeclinedDate == null) ? 0 : (q.ApprovedDate != null && q.DeclinedDate == null) ? 1 : 2
});
I might add even more status combinations, so should I try and do this in the linq select query, in my repository object.. Or later on in the controller where I would do a .ToList() and then foreach the list to set the correct status code?
Having even more than 3 statuscodes, the linq query gets "hard" to read.
What about moving status calculation to Item class? If status property depends on other properties value, then it's definitely calculated property:
var result = from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate
});
And
public class Item
{
// other properties
public int Status
{
get
{
if (ApprovedDate == null and DeclinedDate == null)
return 0;
if (ApprovedDate != null and DeclinedDate == null)
return 1;
if (DeclinedDate != null)
return 3;
// etc
}
}
}
Actually I think it's best option, because in this case status calculation logic will be close to required data. If (for some reason) you can't use this approach, then move setting statuses to local items collection:
var items = result.ToList().ForEach(i => i.Status = CalculateStatus(i));
Maybe wrapped all in a function An do a linq like this
var result = (from q in query sele q).AsEnumerable()
.Select( x => new Item()
{
ApprovedDate = x.ApprovedDate,
CreatedDate = x.CreatedDate,
DeclinedDate = x.DeclinedDate,
Status = MyStatusFunction(x.CreatedDate,q.DeclinedDate)
});
public int MyStatusFunction(DateTime ApprovedDate , Datetime DeclinedDate)
{
if (ApprovedDate == null and DeclinedDate == null) return 0;
else if(ApprovedDate != null and DeclinedDate == null) return 1;
else if (DeclinedDate != null) return 3;
}

EF Duplicated Value

Im getting angry with this error and cannot solve it.
Please, some Jedi master help me.
I'm trying to save trhee Entities: Region, Content and RegionalContent. Region is OK but Regional Content has to be associated with one Content and each Content may have Many RegionalContents(Translations). But I always get a DbUpdateException that has a UpdateException that has a SqlCeException that says something like:
*Impossible to insert a duplicated value with same index. Table name = XBLContents,Constraint name = PK_XBLContents_000000000000001C *
I'm debugging it for some days and could not find the error. Please, note that I'm still a little Padawan.
This is the code that saves the objects in they proper Tables:
Region region;
if (!db.Regions.Any(x => x.ID == Locale))
{
region = new Region { ID = Locale };
db.Regions.Add(region);
db.SaveChanges();
}
else
region = db.Regions.SingleOrDefault(x => x.ID == Locale);
for (int i = start; i < (start + 2); i++)
{
string guid = itens[i].Groups["guid"].Value;
Content c = new Content(guid);
if (!db.Contents.Any(x => x.GUID == guid))
{
c.Type = Type.ToString();
c.PopularInfo(Locale);
db.Contents.Add(c);
}
else
c = db.Contents.SingleOrDefault(x => x.GUID == c.GUID);
RegionalContent regionalcontent;
if (!db.RegionalInfos.Any(x => x.ContentId == guid && x.RegionId == Locale))
{
if (c.HTML == null)
c.PopularInfo(Locale);
regionalcontent = new RegionalContent(c, Locale);
regionalcontent.Region = region;
regionalcontent.Name = HttpUtility.HtmlDecode(itens[i].Groups["name"].Value);
db.RegionalInfos.Add(regionalcontent);
db.Contents.Add(c);
db.SaveChanges();
}
else
regionalcontent = db.RegionalInfos.SingleOrDefault(x => x.ContentId == guid && x.RegionId == Locale);
c.RegionalInfo.Clear();
regionalcontent.Region = region;
c.RegionalInfo.Add(regionalcontent);
Contents.Add(c);
}
You are calling SingleOrDefault when you know 1 already exists. Just use Single.
I would not call SaveChanges to the very end.
Are you sure the GUIDs are unique every time?

Categories