I am working with the Dynamics AX 2012 R2 query service and need to filter (set a range) on the modifiedDateTime field of the CustTable. I am creating a QueryDataRangeMetadata object and setting its properties. I can filter properly on integer values but not DateTimes.
I was able to figure out that the comparison operator is actually embedded with the value. I have tested this with integer fields and it does work for but I have not been able to figure out how to format a DateTime value so that it is properly evaluated. The code below doesn't work. The range is simply ignored and all records from the CustTable are returned.
public static void RangeTest()
{
var client = new QueryServiceClient();
var dataSource = new QueryDataSourceMetadata
{
Table = "CustTable",
Name = "CustTable",
HasRelations = false,
Enabled = true,
DynamicFieldList = true // get all fields
};
var range = new QueryDataRangeMetadata
{
TableName = "CustTable",
FieldName = "modifiedDateTime",
Value = ">2013-02-05T21:17:33Z", // <-- ISSUE: notice the operator with the value!
Enabled = true
};
dataSource.Ranges = new QueryRangeMetadata[] { range };
var sort = new QueryDataOrderByMetadata
{
DataSource = "CustTable",
FieldName = "modifiedDateTime",
SortOrder = SortOrder.Ascending
};
var query = new QueryMetadata
{
QueryType = QueryType.Join,
DataSources = new[] { dataSource },
OrderByFields = new QueryOrderByMetadata[] { sort }
};
Paging paging = null;
var dataSet = client.ExecuteQuery(query, ref paging);
Console.WriteLine(dataSet.Tables[0].Rows.Count);
}
I have also tried these formatting variations with no success:
Value = ">2013-02-05 21:17:33"
Value = ">2013-02-05T9:17:33"
Value = ">'2013-02-05T9:17:33'"
Value = ">2013-02-05T21:17:33Z"
Anyone know what the format of the DateTime is supposed to be in this case?
After iterating over a bunch of DateTime formatting variations I just copied and pasted a value from the UI and guess what? It worked. This is the snippet:
var range = new QueryDataRangeMetadata
{
TableName = "CustTable",
FieldName = "modifiedDateTime",
Value = ">2/5/2013 9:17:33 PM",
Enabled = true
};
So the format seems to be: comparison_operatorMM/DD/YYYY hh:mm:ss AM
I am in the US and the format is month-first. I imagine that other locales would have to format differently, e.g. day-first.
Related
I'm taking a datatable and serializing it as geojson. I'm using linq for this:
var envelope = new
{
type = "FeatureCollection",
features = dataTable.AsEnumerable().Select(record => new {
type = "Feature",
properties = new
{
Name = Convert.ToString(record["Name"]),
Date = Convert.ToString(record["Date"]),
Icon = Convert.ToString(record["imageUrl"]),
//ReportMonth = Convert.ToString(record["Month"]),
ReportMonth = (!string.IsNullOrEmpty(record["Month"])) ? Convert.ToString(record["ReportMonth"]) : string.Empty
},
geometry = new
{
type = "Point",
coordinates = new[] {
Convert.ToDecimal(record["Lon"]),
Convert.ToDecimal(record["Lat"])
}
}
}).ToArray()
};
This works when the datatable has all the columns. When the column doesn't exist in the datatable (ie. column Month) then the iteration fails.
Is there a way to check if the column exists? I tried using a ternary operator to check the value, but it obviously won't work since I'm still checking if the value exists.
You can use:
ReportMonth = record.Table.Columns.Contains("Month")
? Convert.ToString(record["Month"])
: string.Empty;
Convert.ToString(object) returns string.Empty if the object is null so we don't need to check it.
Here is a speed performance optimization:
bool hasName = dataTable.Columns.Contains("Name");
bool hasDate = dataTable.Columns.Contains("Date");
bool hasimageUrl = dataTable.Columns.Contains("imageUrl");
bool hasMonth = dataTable.Columns.Contains("Month");
bool hasLon = dataTable.Columns.Contains("Lon");
bool hasLat = dataTable.Columns.Contains("Lat");
var envelope = new
{
// use: ReportMonth = hasMonth ? ... : ... ;
}
You could try record.Table.Columns.Contains(...).
I am trying to convert a list of 1000s dynamic (that I get from CsvHelper reading csv file) into static type but its taking forever.
Here's code:
dynamic object
MyObj {
id =1, prop1=1,prop2=2,prop3=3...
}
result
PObj1 { oid = 1 , name = "Property 1", value = "1" }
PObj2 { oid = 1 , name = "Property 2", value = "2" }
PObj3 { oid = 1 , name = "Property 3", value = "3" }
Code to convert
var imp = rows.SelectMany(x => map.Keys.ToList().Select(k => new PObj
{
OID = (((IDictionary<string, object>)x)["oid"] ?? "").ToString(),
Name = k,
Value = ToDate((((IDictionary<string, object>)x)[map[k]] ?? "").ToString())
}).ToList()).ToList();
map contains list of properties about 40-50
map<string,string>{
{"Property 1","prop1"},
{"Property 1","prop2"},
{"Property 1","prop3"}
...
}
ToDate function
private DateTime? ToDate(string strDate)
{
strDate = strDate.Split(' ')[0];
strDate = strDate.Replace('-', '/').Replace('.', '/');
DateTime? dt = null;
try
{
dt = DateTime.ParseExact(strDate, dateFormats, CultureInfo.InvariantCulture, DateTimeStyles.None);
} catch { }
return dt;
}
map can contain any number of peroperties hence expandoObject will have dynamic number of properties.
Is there any way I can improve the performance?
The reason I need to do this conversion is because I need to than send this as table to a stored procedure therefore converting expandoObject straight into table creates issue if number properties in object changes as this mean number of column will also change in table.
I am open to other solutions as well if works in above situation.
seems like it was my pc (running windows on mac). Same code now works fine
rows.ToList().ForEach(x => imps.AddRange(map.Keys.Select(k => new ImportMilestone
{
JVSiteID = (((IDictionary<string, object>)x)[siteid] ?? "").ToString(),
Milestone = k,
MilestoneValue = ToDate((((IDictionary<string, object>)x)[map[k]] ?? "").ToString())
}).ToList()));
i am writing code for search page and i have to pass some filters to the action and depending on those input I have to generate hyper links, hence i am using Url.Action function to generate links.
below is my code
#Url.Action("Index","Search",new SkillKindleWeb.ViewModels.Search.SearchRawInput()
{
CategoryIds = Model.Request.CategoryIds,
SubCategoryIds = Model.Request.SubCategoryIds,
StartDate = Model.Request.StartDate,
EndDate = Model.Request.EndDate,
StartPrice = Model.Request.StartPrice,
LocationGroupIds = Model.Request.LocationGroupIds,
LocationIds = Model.Request.LocationIds,
EndPrice = Model.Request.EndPrice,
City = Model.Request.City,
PageNo = 1,
SearchQuery = Model.Request.SearchQuery,
Segment1 = Model.Request.Segment1,
Segment2 = Model.Request.Segment2,
TargetAge = Model.Request.TargetAge
})
and it is generating url like this
http://someDomain.com/ncr/classes?CategoryIds=System.Collections.Generic.List%601%5BSystem.Int32%5D&StartDate=03%2F30%2F2013%2000%3A00%3A00&StartPrice=0&EndPrice=140000&PageNo=2
My expected Url was
http://SomeDomain.com/ncr/classes?CategoryIds=9&StartDate=3/30/2013&StartPrice=0&EndPrice=140000
What about converting it to string representation yourself like that:
#Url.Action("Index","Search",new SkillKindleWeb.ViewModels.Search.SearchRawInput()
{
CategoryIds = string.Join(",", Model.Request.CategoryIds),
SubCategoryIds = string.Join(",", Model.Request.SubCategoryIds),
StartDate = Model.Request.StartDate.ToShortDateString(),
EndDate = Model.Request.EndDate.ToShortDateString(),
StartPrice = Model.Request.StartPrice,
LocationGroupIds = Model.Request.LocationGroupIds,
LocationIds = Model.Request.LocationIds,
EndPrice = Model.Request.EndPrice,
City = Model.Request.City,
PageNo = 1,
SearchQuery = Model.Request.SearchQuery,
Segment1 = Model.Request.Segment1,
Segment2 = Model.Request.Segment2,
TargetAge = Model.Request.TargetAge
})
That is what a viewmodel should be for. That you convert and format all the values you need in the way the view expects it.
Notice that I added a ToShortDateString() to your dates as well, since it seems you are not interested in the time part.
I'm writing a bit of code, and I'd like to play with doing it using the anonymous features of C#.
I'm writing a summary based on a DataTable returned from the SQL Server.
There are many ways I could write it already knowing Classical C# (???), but I'm interested in having a little fun.
So, here are the type of anonymous classes I want to have:
// Employee
var emp = new {
Badge = "000000",
Name = "No Name",
Parts = new List<Part>(),
Days = new List<DateTime>(),
};
// Part
var part = new {
SerialNumber = "N/A",
Date = DateTime.MinValue,
Badge = "000000",
};
Now, as I iterate over my DataTable entries, I want to sort my Parts by SerialNumber.
The first thing I have to do is break the data down into days.
private void TestMethod(DateTime minDate, DateTime maxDate, DataTable table) {
int days = 1;
var nextDay = minDate.AddHours(24);
foreach (DataRow row in table.Rows) {
var dateTime = (DateTime)row["Date_Time"];
var emp = new {
Badge = row["Badge"].ToString(),
Parts = new List<Part>(),
Days = new List<DateTime>(),
};
var part = new {
SerialNumber = row["Serial_Number"].ToString(),
Date = dateTime,
Badge = row["Badge"].ToString(),
};
if (nextDay < dateTime) {
days++;
nextDay = nextDay.AddHours(24);
}
}
Now, it is getting a little interesting.
I need a way to store Part information for the different days and the different employees found for the period.
How would I create and use an anonymous collection of my anonymous class items?
var parts = new List<typeof(part)>();
var emps = new List<typeof(emp)>();
Using typeof (above) does not work!
What does?
You need to use type inference:
new[] { part }.ToList()
(you probably want to clear the list afterwards)
You can also make a helper method:
public static List<T> ListOf<T>(T sample) {
return new List<T>();
}
var parts = ListOf(part);
I'm running the query below :-
var Values = from data in DtSet.Tables["tblCosts"].AsEnumerable()
group data by new
{
InvNo = data.Field<double>("InvoiceNo"),
AccRef = data.Field<double>("SiteRefNum"),
}
into g
select new
{
Code = "1",
InvType = "I",
Account = g.Key.AccRef,
InvNo = g.Key.InvNo,
ChargeTotal = g.Sum(d => d.field<double>("Charge")
};
Due to the way the data is imported into the datatable (from Excel) sometimes the datatype of AccRef is double and sometimes it's string. Is there a way to overcome this at runtime, as I'd prefer to not have the user modify the source data in Excel before importing.
You can use Convert.ToDouble with objects, so it should work for double and for string:
var Values = from data in DtSet.Tables["tblCosts"].AsEnumerable()
group data by new
{
InvNo = data.Field<double>("InvoiceNo"),
AccRef = Convert.ToDouble(data["SiteRefNum"]),
}
into g
select new
{
Code = "1",
InvType = "I",
Account = g.Key.AccRef,
InvNo = g.Key.InvNo,
ChargeTotal = g.Sum(d => d.Field<double>("Charge"))
};
Of course that works only if SiteRefNum is actually convertable to a double.