ServiceStack Bug serializing GUIDs, numbers, etc. when value is default and SerializeFn is specified - servicestack

When you try and serialize a Guid that is empty (not null, but empty) the result will be omitted if you set ExcludeDefaultValues = true.
But, if you then set ExcludeDefaultValues = false it will generate the string ""
JsConfig.IncludeNullValues = false;
JsConfig.ExcludeDefaultValues = false;
var tt = new { Name="Fred", Value=Guid.Empty, Value2=Guid.NewGuid() };
var test = JSON.stringify(tt);
Console.WriteLine(test);
Gives
{"Name":"Fred","Value":"00000000000000000000000000000000","Value2":"598a6e08af224db9a08c2d0e2f6cff11"}
But we want the Guid's formatted as a Microsoft format Guid at the client end, so we add a serializer:
JsConfig.IncludeNullValues = false;
JsConfig.ExcludeDefaultValues = false;
JsConfig<Guid>.SerializeFn = guid => guid.ToString();
var tt = new { Name="Fred", Value=Guid.Empty, Value2=Guid.NewGuid() };
var test = JSON.stringify(tt);
Console.WriteLine(test);
Gives
{"Name":"Fred","Value2":"07a2d8c0-48ad-4e72-b6f3-4fec81c36a1d"}
So the presence of a SerializeFn seems to make it ignore the config settings so it's impossible to generate the empty Guid.
The same bug applies to numbers, so if (like us) you reformat all Double to three decimal places they are omitted if zero, which is wrong.
Has anyone found a workaround for this?

Stepping through the source, it appears you need to explicitly call out what Types you want to include the default value for if there is a SerializeFn for that Type. Source reference. Note the JsConfig<Guid>.IncludeDefaultValue = true; line below.
Example Source
JsConfig.Reset();
JsConfig.IncludeNullValues = false;
JsConfig.ExcludeDefaultValues = false;
JsConfig<Guid>.SerializeFn = guid => guid.ToString();
JsConfig<Guid>.IncludeDefaultValue = true;
var tt = new { Name = "Fred", Value = Guid.Empty, Value2 = Guid.NewGuid() };
var test = tt.ToJson();
Console.WriteLine(test);
Output
{"Name":"Fred","Value":"00000000-0000-0000-0000-000000000000","Value2":"b86c4a18-db07-42f8-b618-6263148219ad"}
Your problem statement: Gistlyn: Note how it does not return the default GUID in the console.
The answer proposed above: Gistlyn: Notice how it does return the default GUID in the console.

Related

How to create purchase order via code?

I had wrote some code for creating po via code, but it faild with error message: "CS Error: Cannot generate the next number for the sequence."
How can I fix this? Anything I missed? Thx for helping!
protected void createPO() {
POOrder order = new POOrder();
POOrderEntry graph = PXGraph.CreateInstance < POOrderEntry > ();
order.OrderType = "Normal";
order.OrderDesc = "some text";
order.EmployeeID = 215;
order.Hold = false;
var branch = (Branch)PXSelect<Branch, Where<Branch.branchCD, Equal<Required<Branch.branchCD>>>>.Select(Base, "WEST");
graph.FieldDefaulting.AddHandler<POOrder.branchID>((s, e) =>
{
e.NewValue = branch.BranchID;
e.Cancel = true;
});
order.VendorID = 79;
order = graph.CurrentDocument.Insert(order);
graph.CurrentDocument.Update(order);
graph.Actions.PressSave();
throw new PXRedirectRequiredException(graph, null);
}
Try to simply it first to see if something like this works...
protected void createPO()
{
var graph = PXGraph.CreateInstance<POOrderEntry>();
var order = graph.Document.Insert(new POOrder());
order.OrderType = POOrderType.RegularOrder; // This is the default so not necessary
order.OrderDesc = "some text";
order.EmployeeID = 215;
order.Hold = false;
order.VendorID = 79;
graph.Document.Update(order);
graph.Actions.PressSave();
throw new PXRedirectRequiredException(graph, null);
}
Using the Document view in place of the CurrentDocument view which is based on the current record of Document. Document is the primary view and the primary view should be used.
Also for the purchase order type the attribute related to the list values should be used for the stored value of the database (vs what you had was the displayed list value). For example order.OrderType = POOrderType.RegularOrder. Also this is the default value for a PO so it is not necessary to set this value unless you want a different constant found in the POOrderType class.

How can I turn background error checking off for the Excel app object in EPPlus?

Using the unwieldy and ponderous but full-featured Excel Interop, background error checking can be toggled off like so:
Excel.Application excelApp = new Excel.Application();
excelApp.ErrorCheckingOptions.BackgroundChecking = false;
...as shown here
I am getting the green triangles indicating a bad number like so:
...which I want to turn off. These are just string vals that should not be flagged as bad or suspicious.
So how can I turn background error checking off for the Excel app object, or otherwise programmatically prevent these green triangles, using EPPlus?
UPDATE
Changing the code from this:
using (var custNumCell = priceComplianceWorksheet.Cells[rowToPopulate, DETAIL_CUSTNUM_COL])
{
custNumCell.Style.Font.Size = DATA_FONT_SIZE;
custNumCell.Value = _custNumber;
custNumCell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
}
...to this:
using (var custNumCell = priceComplianceWorksheet.Cells[rowToPopulate, DETAIL_CUSTNUM_COL])
{
custNumCell.Style.Font.Size = DATA_FONT_SIZE;
custNumCell.ConvertValueToAppropriateTypeAndAssign(_custNumber);
custNumCell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
}
// Adapted from https://stackoverflow.com/questions/26483496/is-it-possible-to-ignore-excel-warnings-when-generating-spreadsheets-using-epplu
public static void ConvertValueToAppropriateTypeAndAssign(this ExcelRangeBase range, object value)
{
string strVal = value.ToString();
if (!String.IsNullOrEmpty(strVal))
{
decimal decVal;
double dVal;
int iVal;
if (decimal.TryParse(strVal, out decVal))
{
range.Value = decVal;
}
else if (double.TryParse(strVal, out dVal))
{
range.Value = dVal;
}
else if (Int32.TryParse(strVal, out iVal))
{
range.Value = iVal;
}
else
{
range.Value = strVal;
}
}
else
{
range.Value = null;
}
}
...semi-fixed it; it is now:
But notice that the leading "0" got stripped out. I need that to remain, so this is still only half-solved.
UPDATE 2
I tried the suggestion from the comment below that pointed here, and added this code:
//Create the import nodes (note the plural vs singular
var ignoredErrors =
priceComplianceWorksheet.CreateNode(XmlNodeType.Element, "ignoredErrors",
xdoc.DocumentElement.NamespaceURI);
var ignoredError
priceComplianceWorksheet.CreateNode(XmlNodeType.Element, "ignoredError",
xdoc.DocumentElement.NamespaceURI);
ignoredErrors.AppendChild(ignoredError);
//Attributes for the INNER node
var sqrefAtt = priceComplianceWorksheet.CreateAttribute("sqref");
sqrefAtt.Value = range;
var flagAtt =
priceComplianceWorksheet.CreateAttribute("numberStoredAsText");
flagAtt.Value = "1";
ignoredError.Attributes.Append(sqrefAtt);
ignoredError.Attributes.Append(flagAtt);
//Now put the OUTER node into the worksheet xml
priceComplianceWorksheet.LastChild.AppendChild(ignoredErrors);
...but "CreateAttribute" and "LastChild" are not recognized...?!?
In response to update 2, you just need to reference the XmlDocument and use that to generate the XML:
var xdoc = priceComplianceWorksheet.WorksheetXml;
//Create the import nodes (note the plural vs singular
var ignoredErrors = xdoc.CreateNode(XmlNodeType.Element, "ignoredErrors",xdoc.DocumentElement.NamespaceURI);
var ignoredError = xdoc.CreateNode(XmlNodeType.Element, "ignoredError",xdoc.DocumentElement.NamespaceURI);
ignoredErrors.AppendChild(ignoredError);
//Attributes for the INNER node
var sqrefAtt = xdoc.CreateAttribute("sqref");
sqrefAtt.Value = "C2:C10"; // Or whatever range is needed....
var flagAtt = xdoc.CreateAttribute("numberStoredAsText");
flagAtt.Value = "1";
ignoredError.Attributes.Append(sqrefAtt);
ignoredError.Attributes.Append(flagAtt);
//Now put the OUTER node into the worksheet xml
xdoc.LastChild.AppendChild(ignoredErrors);

How to Add an optionset filter criteria in MS CRM Query Expression?

I have an entity LeaveType with two attributes, 1. Type, 2. Available Days, where Type is an optionset and Available days is a text field. I want to fetch all such LeaveType records where the Type = 'Annual' selected in the optionset. I am not able to find how to add a filter the query expression for the option set value. Below is my in progress method:
public Entity Getleavetype(Guid LeaveDetailsId, IOrganizationService _orgService, CodeActivityContext Acontext)
{
QueryExpression GetLeavedetails = new QueryExpression();
GetLeavedetails.EntityName = "sgfdhr_leavetype";
GetLeavedetails.ColumnSet = new ColumnSet("new_type");
GetLeavedetails.ColumnSet = new ColumnSet("new_availabledays");
GetLeavedetails.Criteria.AddCondition("new_type", ConditionOperator.Equal, "Annual" ); //Is this correct????
GetLeavedetails.Criteria.AddCondition("new_employeeleavecalculation", ConditionOperator.Equal, LeaveDetailsId); //ignore this
//((OptionSetValue)LeaveDetailsId["new_leavetype"]).Value
EntityCollection LeaveDetails = _orgService.RetrieveMultiple(GetLeavedetails);
return LeaveDetails[0];
}
In your condition you need to set the integer value of the optionset, not the label.
Assuming that Annual value is for example 2, the code will be:
GetLeavedetails.Criteria.AddCondition("new_type", ConditionOperator.Equal, 2);
You should use RetrieveAttributeRequest to find an int value of OptionSet text.
In my code it looks like:
private static int findParkedOptionValue(IOrganizationService service)
{
RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
{
EntityLogicalName = Model.Invite.ENTITY_NAME,
LogicalName = Model.Invite.COLUMN_STATUS,
RetrieveAsIfPublished = false
};
// Execute the request
RetrieveAttributeResponse attributeResponse =
(RetrieveAttributeResponse)service.Execute(attributeRequest);
var attributeMetadata = (EnumAttributeMetadata)attributeResponse.AttributeMetadata;
// Get the current options list for the retrieved attribute.
var optionList = (from o in attributeMetadata.OptionSet.Options
select new { Value = o.Value, Text = o.Label.UserLocalizedLabel.Label }).ToList();
int value = (int)optionList.Where(o => o.Text == "Парковка")
.Select(o => o.Value)
.FirstOrDefault();
return value;
}
In https://community.dynamics.com/enterprise/b/crmmemories/archive/2017/04/20/retrieve-option-set-metadata-in-c you found a perfect example.

Return a set of objects from a class

I have a method that adds a new item to an EF table, then queries back the table to return a subset of the table. It needs to return to the caller a set of "rows", each of which is a set of columns. I'm not sure how to do this. I have some code, but I think it's wrong. I don't want to return ONE row, I want to return zero or more rows. I'm not sure what DataType to use... [qryCurrentTSApproval is an EF object, referring to a small view in SS. tblTimesheetEventlog is also an EF object, referring to the underlying table]
Ideas?
private qryCurrentTSApproval LogApprovalEvents(int TSID, int EventType)
{
using (CPASEntities ctx = new CPASEntities())
{
tblTimesheetEventLog el = new tblTimesheetEventLog();
el.TSID = TSID;
el.TSEventType = EventType;
el.TSEUserName = (string)Session["strShortUserName"];
el.TSEventDateTime = DateTime.Now;
ctx.tblTimesheetEventLogs.AddObject(el);
ctx.AcceptAllChanges();
var e = (from x in ctx.qryCurrentTSApprovals
where x.TSID == TSID
select x);
return (qryCurrentTSApproval)e;
}
}
Change your method return type to a collection of qryCurrentTSApproval
private List<qryCurrentTSApproval> LogApprovalEvents(int TSID, int EventType)
{
using (CPASEntities ctx = new CPASEntities())
{
// some other existing code here
var itemList = (from x in ctx.qryCurrentTSApprovals
where x.TSID == TSID
select x).ToList();
return itemList;
}
}

Can't convert a variable to Guid

I know my question is really easy but i can't get its solution since last three hours.
I have created a gridview which has three columns PeriodID,PeriodName and Checkbox.
now when i go through for each loop for this grid,i want to get periodid value of each row in this gridview..
for this i have writen code like this.
foreach (GridViewRow row in gvdchk.Rows)
{
int i = row.RowIndex;
var cb = (HtmlInputCheckBox)row.FindControl("chkPaid");
var Periodid = gvdchk.Rows[i].FindControl("PeriodID");
//string su = Periodid.ToString();
// Guid PeriodID = Guid.Parse(su);
// Guid guid = new Guid(Periodid);
//var PeriodID = row.FindControl("PeriodID");
//Guid PeriodId = (Guid) PeriodID;
//Guid PeriodId = (Guid)(gvdchk.Rows[row.RowIndex].FindControl("datemonth2"));
// int order = m.bussinesCollection.BussinesPeriod.GetOrder(PeriodID);
if (cb != null && cb.Checked)
{
}
}
When I debug my code,I found that var Periodid gets the correct value of the period id but I want it as Guid.
Can anyone help in this..?
The following code that you have in your post
Guid guid = new Guid(Periodid);
will work fine to convert the string to GUID.
You can also check what is the datatype for the Periodid that you get in the line no. 5 of your code.

Resources