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(...).
Related
I have two blocks; in each block, I am retrieving the data from a table using command and reader and transforming the data, and then updating the same. What I am looking to extract the common function by passing the table name, type, and transform function to that function from these two blocks
Below is the code for the same,
// Block #1
using var queryProjectSteamSystemsCommand = dbContext.Database.GetDbConnection().CreateCommand();
queryProjectSteamSystemsCommand.CommandText = #"SELECT ""Id""::varchar, ""InitialObject""::varchar from ""DesignHubProjectSteamSystems""";
using var steamSystemProjectsReader = queryProjectSteamSystemsCommand.ExecuteReader();
if (steamSystemProjectsReader.HasRows)
{
while (steamSystemProjectsReader.Read())
{
var id = steamSystemProjectsReader.IsDBNull(0) ? Guid.Empty : Guid.Parse(steamSystemProjectsReader.GetString(0));
var steamSystemJson = steamSystemProjectsReader.IsDBNull(1) ? "null" : steamSystemProjectsReader.GetString(1);
var projSteamSystemInitialObj = JsonConvert.DeserializeObject<OldSteamSystem>(steamSystemJson);
string json = JsonConvert.SerializeObject(TransformProjectSteamSystem(projSteamSystemInitialObj)).Replace("'", "''", StringComparison.Ordinal);
migrationBuilder.Sql($"UPDATE \"DesignHubProjectSteamSystems\" SET \"InitialObject\" = '{json}'::jsonb WHERE \"Id\" = '{id}'");
}
}
steamSystemProjectsReader.Close();
// Block #2
using var queryProjectFuelSystemsCommand = dbContext.Database.GetDbConnection().CreateCommand();
queryProjectFuelSystemsCommand.CommandText = #"SELECT ""Id""::varchar, ""InitialObject""::varchar from ""DesignHubProjectFuelSystems""";
using var fuelSystemProjectsReader = queryProjectFuelSystemsCommand.ExecuteReader();
if (fuelSystemProjectsReader.HasRows)
{
while (fuelSystemProjectsReader.Read())
{
var id = fuelSystemProjectsReader.IsDBNull(0) ? Guid.Empty : Guid.Parse(fuelSystemProjectsReader.GetString(0));
var fuelSystemJson = fuelSystemProjectsReader.IsDBNull(1) ? "null" : fuelSystemProjectsReader.GetString(1);
var projFuelSystemInitialObj = JsonConvert.DeserializeObject<OldFuelSystem>(fuelSystemJson);
string json = JsonConvert.SerializeObject(TransformProjectFuelSystem(projFuelSystemInitialObj)).Replace("'", "''", StringComparison.Ordinal);
migrationBuilder.Sql($"UPDATE \"DesignHubProjectFuelSystems\" SET \"InitialObject\" = '{json}'::jsonb WHERE \"Id\" = '{id}'");
}
}
fuelSystemProjectsReader.Close();
I cannot combine OldSteamSystem and OldFuelSystem two classes, which are different. I can't make any familiar interface and abstract class out of these two.
So, could anyone please let me know how to make a common function out of it?
Many thanks in advance!!!
Use generics, and ask for the table name and a method to transform the object.
void DoTheThing<TSystem>(string tableName, Func<TSystem, TSystem> transform)
{
using var command = dbContext.Database.GetDbConnection().CreateCommand();
command.CommandText = #$"SELECT ""Id""::varchar, ""InitialObject""::varchar from ""{tableName}""";
// I know I complain about SQL injection, you get to fix this one...
using var reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
var id = reader.IsDBNull(0) ? Guid.Empty : Guid.Parse(reader.GetString(0));
var initialJson = reader.IsDBNull(1) ? "null" : reader.GetString(1);
var initialObj = JsonConvert.DeserializeObject<TSystem>(initialJson);
// this uses the "transform" parameter, IE a method your caller provides
string transformedJson = JsonConvert.SerializeObject(transform(initialObj)).Replace("'", "''", StringComparison.Ordinal);
migrationBuilder.Sql($"UPDATE \"{tableName}\" SET \"InitialObject\" = '{transformedJson}'::jsonb WHERE \"Id\" = '{id}'");
// again, SQL injection
}
}
reader.Close();
}
If the transform method takes one type and returns a different type, then change the method signature to
void DoTheThing<TSystem, TTransformed>(string tableName, Func<TSystem, TTransformed> transform)
I am trying to use the "Put_Ledger" function inside the Financial_management API in Workday, but I keep on getting an error when I try to add the object[] to the object (as it states in the API to do).
Workday has been no help in solving this issue. Here is a sample of the code. The objects are creates, and then added to parent objects:
Ledger_Only_DataType ldOnly = new Ledger_Only_DataType
{
Actuals_Ledger_ID = "1234567",
Can_View_Budget_Date = true
};
//Commitment_Ledger_data
Commitment_Ledger_Data__Public_Type cl = new Commitment_Ledger_Data__Public_Type
{
Commitment_Ledger_Reference = ledgerObject,
Enable_Commitment_Ledger = true,
Spend_Transaction_Data = st,
Payroll_Transaction_Data = pt
};
// This is where the error occurs:
ldOnly.Commitment_Ledger_Data = cl;
Error message:
"Cannot implicitly convert type 'CallWorkdayAPI.Financial_Management.Commitment_Ledger_Data__Public_Type' to 'CallWorkdayAPI.Financial_Management.Commitment_Ledger_Data__Public_Type[]"
Use lists and convert them to an array. It's easier:
List<Commitment_Ledger_Data__Public_Type> cls = new List<Commitment_Ledger_Data__Public_Type>();
Commitment_Ledger_Data__Public_Type cl1 = new
Commitment_Ledger_Data__Public_Type
{
Commitment_Ledger_Reference = ledgerObject,
Enable_Commitment_Ledger = true,
Spend_Transaction_Data = st,
Payroll_Transaction_Data = pt
};
cls.Add(cl1);
ldOnly.Commitment_Ledger_Data = cls.ToArray();
You can simplify and do it inside the initializer as well
Not familiar with Workday, but I am assuming
ldOnly.Commitment_Ledger_Data
Is an array of: Commitment_Ledger_Data__Public_Type
So you need to set it equal to an array of that type, whereas currently you are setting it equal to a single object of that type.
Ledger_Only_DataType ldOnly = new Ledger_Only_DataType
{
Actuals_Ledger_ID = "1234567",
Can_View_Budget_Date = true
};
//Commitment_Ledger_data
Commitment_Ledger_Data__Public_Type cl = new
Commitment_Ledger_Data__Public_Type
{
Commitment_Ledger_Reference = ledgerObject,
Enable_Commitment_Ledger = true,
Spend_Transaction_Data = st,
Payroll_Transaction_Data = pt
};
Commitment_Ledger_Data__Public_Type[] cls = new Commitment_Ledger_Data__Public_Type[1];
cls[0] = cl;
ldOnly.Commitment_Ledger_Data = cls;
The error message is telling you what the problem is - you're trying to assign a single instance of a Commitment_Ledger_Data__Public_Type type to an object that represents an array of that type (Commitment_Ledger_Data).
You should be able to do the assignment using an array (with the single item you created as it's only member) instead:
ldlOnly.Commitment_Ledger_Data = new[] {cl};
Or you could shorten the whole thing to use initializer syntax:
var ldOnly = new Ledger_Only_DataType
{
Actuals_Ledger_ID = "1234567",
Can_View_Budget_Date = true,
Commitment_Ledger_Data = new[]
{
new Commitment_Ledger_Data__Public_Type
{
Commitment_Ledger_Reference = ledgerObject,
Enable_Commitment_Ledger = true,
Spend_Transaction_Data = st,
Payroll_Transaction_Data = pt
}
}
};
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()));
When i run my application i get an error that not all the fields of the table tblContact are filled. How do i edit in my code that if a record is null I show in my application null.
Here is my code:
public void SelID(int Zoek)
{
string SQL = "select * from tblContact";
SQL += " where cNr=" + Zoek.ToString();
DataTable DT = Database.ExecSelect(SQL);
if (DT.Rows.Count == 0)
{
pr_cNr = -1;
}
else
{
pr_cNr = (int)(DT.Rows[0]["cNr"]);
pr_cVNaam = (string)(DT.Rows[0]["cVnaam"]);
pr_cTNaam = (string)(DT.Rows[0]["cTNaam"]);
pr_cANaam = (string)(DT.Rows[0]["cAnaam"]);
pr_cAdres = (string)(DT.Rows[0]["cAdres"]);
pr_cPost = (string)(DT.Rows[0]["cPost"]);
pr_cPlaats = (string)(DT.Rows[0]["cPlaats"]);
}
}
try this way:
object val = row["ColumnName"];
if (val == DBNull.Value)
// your code
else
//your code
using inline checking for all columns makes your code messy and error-prone.
It seems, you have to check for null:
...
else
{
// In case of integer we have to specify how null should be treated:
pr_cNr = DT.Rows[0].IsNUll("cNr")
? -1 // or whatever default value
: Convert.ToInt32(DT.Rows[0]["cNr"]);
// In case of string Convert.ToString(DBNull.Value) returns empty string ""
// Please, notice that original (string) (DBNull.Value) throws exception
pr_cVNaam = Convert.ToString(DT.Rows[0]["cVnaam"]);
pr_cTNaam = Convert.ToString(DT.Rows[0]["cTNaam"]);
pr_cANaam = Convert.ToString(DT.Rows[0]["cAnaam"]);
pr_cAdres = Convert.ToString(DT.Rows[0]["cAdres"]);
pr_cPost = Convert.ToString(DT.Rows[0]["cPost"]);
pr_cPlaats = Convert.ToString(DT.Rows[0]["cPlaats"]);
}
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.