Using ConvertUsing to map and to project with SqlFunctions - automapper

I am upgrading a .NET Framework project from Automapper 4.1.1 to Automapper 10.0.0 and I am encountering a problem that I am unable to resolve.
I see that ConvertUsing and ProjectUsing have been unified, but somehow I have to be able to tell if a Mapping or a Project is being done.
I have a configuration so that it is applied generically when changing a DateTime to an int so that it transforms by calculating the number of minutes.
I need to use SqlFunctions to do the conversion but only when we are projecting, when we are mapping it must be done through a simple transformation.
How can you do this if you can now only have one ConvertUsing?
CreateMap<DateTime?, int>().ConvertUsing(x => x.HasValue ? System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", x.Value).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", x.Value).Value : 0); CreateMap<DateTime, int>().ConvertUsing(x => System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", x).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", x).Value); CreateMap<DateTime?, int>().ConvertUsing(x => Convert.ToInt32(x.HasValue ? x.Value.TimeOfDay.TotalMinutes : 0)); CreateMap<DateTime, int>().ConvertUsing(x => Convert.ToInt32(x.TimeOfDay.TotalMinutes));
I tried using ProjectUsing or looking for a parameter inside ConvertUsing that tells me if I'm converting an IQueryable but it doesn't seem to exist

You can create custom DB function (using e.g. CodeFirstFunctions or EntityFramework.Functions package that allows creating custom functions) and provide body of this function (example):
[Function(FunctionType.ComposableScalarValuedFunction, nameof(SecondsDiff), Schema = "dbo")]
[return: Parameter(DbType = "int")]
public static int? SecondsDiff([Parameter(DbType = "date")] DateTime? date1, [Parameter(DbType = "date")] DateTime? date2)
{
if (date1 == null || date2 == null)
{
return null;
}
DateTime a = date1.Value;
DateTime b = date2.Value;
if (a >= b)
{
return (int?)(b - a).TotalSeconds;
}
return -(int?)(a - b).TotalSeconds;
}
It will work in both modes.

Related

jooq: Add interval to timestamp postgres

I'm trying to bump a timestamptz value further in to the future by a number of interval seconds. Is there a way to massage these types so the jooq will allow me to do so in one statement, or do I just need to get the TriggerRecord and do the calculation in Java code?
Code and attempt follows:
public final TableField<TriggerRecord, Instant> PAUSED_UNTIL = createField(DSL.name("paused_until"), SQLDataType.TIMESTAMPWITHTIMEZONE(6), this, "", new OffsetDateTimeInstantConverter());
public class OffsetDateTimeInstantConverter implements Converter<OffsetDateTime, Instant> {
private static Instant min;
public OffsetDateTimeInstantConverter() {
}
public Instant from(OffsetDateTime databaseObject) {
return databaseObject == null ? null : databaseObject.toInstant();
}
public OffsetDateTime to(Instant userObject) {
if (userObject == null) {
return null;
} else {
return userObject.isBefore(min) ? OffsetDateTime.MIN : userObject.atOffset(ZoneOffset.UTC);
}
}
public Class<OffsetDateTime> fromType() {
return OffsetDateTime.class;
}
public Class<Instant> toType() {
return Instant.class;
}
static {
min = OffsetDateTime.MIN.toInstant();
}
In one case it errors out
final Long ps = 360;
query = using(configuration)
.update(TRIGGER)
.set(TRIGGER.ACTIVE, active)
.set(TRIGGER.PAUSED_UNTIL,
TRIGGER.PAUSED_UNTIL.add(ps))
.returning()
.fetch();
ERROR: operator does not exist: timestamp with time zone + timestamp with time zone
And in another attempt errors as
final var query = using(configuration)
.update(TRIGGER)
.set(TRIGGER.ACTIVE, active)
.set(TRIGGER.PAUSED_UNTIL,
TRIGGER.PAUSED_UNTIL
.add(val(DayToSecond.valueOf(Duration.ofSeconds(ps)))))
org.jooq.exception.DataTypeException: Cannot convert from +0 00:06:00.000000000 (class org.jooq.types.DayToSecond) to class java.time.OffsetDateTime
update trigger set "paused_until" = ("alert"."trigger"."paused_until" + cast(? as timestamp(6) with time zone))
This looks like bug #12036, which has been fixed in jOOQ 3.17.0, 3.16.4, and 3.15.8. The workaround is to use plain SQL templating for this particular expression.
DSL.field("{0} + {1}",
TRIGGER.PAUSED_UNTIL.getDataType(),
TRIGGER.PAUSED_UNTIL, ps
);

Change String length or decimal precision of field attribute dynamically

I'm trying to use setup data from one table to allow me to format fields on the fly / dynamically. I know I can change field names and visibility based on the PXUIFieldAttribute class, but changing the precision or string length is a bit trickier, obviously. From the research I've done, I've come up with the following example code that seems like it should work - but I get the error:
"Unable to cast object of type 'PX.Data.PXUIFieldAttribute' to type 'PX.Data.PXDBDecimalAttribute'.
I don't see why this is occurring...
protected virtual void xTACOpenSourceDetail_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
var osd = (PXCache)sender;
foreach (PXDBDecimalAttribute attribute in this.Caches<xTACOpenSourceDetail>().GetAttributes("Number1"))
{
PXDBDecimalAttribute someAttribute = attribute as PXDBDecimalAttribute;
if (someAttribute != null)
{
someAttribute.DBProperties._precision = 4;
}
}
}
I just tried the below code in sales order screen and it seems working!
var props = typeof(SOOrder).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(PXDecimalAttribute)));
foreach (System.Reflection.PropertyInfo item in props)
{
PXDecimalAttribute.SetPrecision(this.Base.Caches[typeof(SOOrder)], item.Name, 1);
}
You might need to change this to match your DAC.

Unable to apply sorting in MVC 5

I'm trying to apply sorting in my MVC 5 application. I used the code here
to apply sorting. Unfortunately, its not working and it won't sort. Am I missing something? All datatypes I used are strings btw.
Here is the code:
//Controller
public ActionResult Index(string sort)
{
ViewBag.ExtSortParm = String.IsNullOrEmpty(sort) ? "ext_desc" : "";
ViewBag.DtsSortParm = sort == "DTS" ? "dts_desc" : "DTS";
var sales = from s in db.Sales1 select s;
switch (sort)
{
case "ext_desc":
sales = sales.OrderByDescending(s => s.ExtSerial);
break;
case "DTS":
sales = sales.OrderBy(s => s.DTS);
break;
case "dts_desc":
sales = sales.OrderByDescending(s => s.DTS);
break;
default:
sales = sales.OrderBy(s => s.ExtSerial);
break;
}
return View(db.Sales1.ToList());
}
And my View applied:
#Html.ActionLink("ExtSerial", "Index", new { sortOrder = ViewBag.ExtSortParm })
#Html.ActionLink("DTS", "Index", new { sortOrder = ViewBag.DtsSortParm })
Was there something I missed? DTS is a date by the way just in string type. Here is an example value: 5/11/2015 5:29:56 AM
db is my database & Sales1 is my SalesEntity
You are sorting your collection but he following line returns the original unsorted collection
return View(db.Sales1.ToList());
You need to return the sorted collection to the view using
return View(sales.ToList());

How to format a numeric result as string using a custom converter

In my form I have four fields that should be calculated and the result displayed in the last field called PaymentAmount.
I was recommended to use a custom converter for PaymentAmount because I then only needed to have partial refresh on PaymentAmount for onchange event on the other four fields.
This works very good but my problem is that the result is in wrong format.
My code looks like this:
getAsObject()
try {
var transCode = getComponent("TransactionCode").getValue();
var nominal = getComponent("Nominal").getValue();
var price = getComponent("Price").getValue();
var qFactor = getComponent("QuoteFactor").getValue()||1;
var fee1= getComponent("Fee1").getValue()||0;
var fee2= getComponent("Fee2").getValue()||0;
var fee3= getComponent("Fee3").getValue()||0;
var feeTotal = fee1+fee2+fee3;
var paymentAmount = nominal * (price * qFactor);
if(transCode == "Buy") {
paymentAmount+=feeTotal;
} else if(transCode == "Sell") {
paymentAmount -= feeTotal;
} else return 0;
return paymentAmount;
} catch(e){
dBar.error(e);
}
getAsString()
return value.toString();
I have tried to format the result using all available methods and objects in java like:
String.format("%.2f", value); but failed.
If I enter my values according to my locale 10000*1,44+1,2 = 14401,2 but the result displayed in PaymentAmount is 14401.2.
I'd like it to be displayed according to my locale 14001,2.
If I for instance use the following in getAsString() I get this error:
try {
var val = java.lang.String.format("%.2f",value);
} catch(e) { dBar.error(e); }
com.ibm.jscript.InterpretException: Script interpreter error, line=2, col=28: Java method 'format(string, number)' on java class 'java.lang.String' not found
I can't get the correct data type for value in getAsString().
For you who has seen/commented on my earlier questions I'm stuck with these "localisation issues" once again...
Please advice
/M
You can use this Java code to get the number in your locale:
import java.text.NumberFormat;
import java.util.Locale;
...
...
Locale swedishLocale = new Locale("sv", "SE"); // Locale for Sweden
NumberFormat nf = NumberFormat.getInstance(swedishLocale);
return nf.format(14401.2);
This would return 14 401,2. Do not try convert this into SSJS otherwise you would get an error of Ambiguity when calling format(long) and format(double). I have bitten by this before.
Update
You can create a class with a static method which formats the date in your locale.
package pkg;
import java.text.NumberFormat;
import java.util.Locale;
public class Utilities {
public static String formatString() {
Locale swedishLocale = new Locale("sv", "SE");
NumberFormat nf = NumberFormat.getInstance(swedishLocale);
return nf.format(14401.2);
}
}
Then you can call this method in SSJS by:
var formattedNumber = pkg.Utilities.formatString();
You can add parameters to method as per your requirements.

Anonymous type and getting values out side of method scope

I am building an asp.net site in .net framework 4.0, and I am stuck at the method that supposed to call a .cs class and get the query result back here is my method call and method
1: method call form aspx.cs page:
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
2: Method in helper class:
public IQueryable<VariablesForIQueryble> GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some connection_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where (gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)
select new VariablesForIQueryble(m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//select new {m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap};
return query ;
}
I tried the above code with IEnumerable too without any luck. This is the code for class VariablesForIQueryble:
3:Class it self for taking anonymouse type and cast it to proper types:
public class VariablesForIQueryble
{
private int _emailCap;
public int EmailCap
{
get { return _emailCap; }
set { _emailCap = value; }
}`....................................
4: and a constructor:
public VariablesForIQueryble(int memberID, string memberFirst, string memberLast, string memberEmail, int? validEmail, int? emailCap)
{
this.EmailCap = (int) emailCap;
.........................
}
I can't seem to get the query result back, first it told me anonymous type problem, I made a class after reading this: link text; and now it tells me constructors with parameters not supported. Now I am an intermediate developer, is there an easy solution to this or do I have to take my query back to the .aspx.cs page.
If you want to project to a specific type .NET type like this you will need to force the query to actually happen using either .AsEnumerable() or .ToList() and then use .Select() against linq to objects.
You could leave your original anonymous type in to specify what you want back from the database, then call .ToList() on it and then .Select(...) to reproject.
You can also clean up your code somewhat by using an Entity Association between Groups and Members using a FK association in the database. Then the query becomes a much simpler:
var result = ctx.Members11.Include("Group").Where(m => m.Group.groupID == incomingGroupID && m.EmailCap == incomingEmailCap);
You still have the issue of having to do a select to specify which columns to return and then calling .ToList() to force execution before reprojecting to your new type.
Another alternative is to create a view in your database and import that as an Entity into the Entity Designer.
Used reflection to solve the problem:
A: Query, not using custom made "VariablesForIQueryble" class any more:
//Method in helper class
public IEnumerable GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where ((gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)) //select m;
select new { m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap };
//select new VariablesForIQueryble (m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//List<object> lst = new List<object>();
//foreach (var i in query)
//{
// lst.Add(i.MemberEmail);
//}
//return lst;
//return query.Select(x => new{x.MemberEmail,x.MemberID,x.ValidEmail,x.MemberFirst,x.MemberLast}).ToList();
return query;
}
B:Code to catch objects and conversion of those objects using reflection
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
if (query != null)
{
foreach (var objRow in query)
{
System.Type type = objRow.GetType();
int memberId = (int)type.GetProperty("MemberID").GetValue(objRow, null);
string memberEmail = (string)type.GetProperty("MemberEmail").GetValue(objRow, null);
}
else
{
something else....
}

Resources