How to read back a nulllable DateTime using SQL Data Reader? - sql-server

I've declared a nullable DateTime? NextUpdateproperty in my model and database.
I can update the DateTime value fine on my DB as it allows null for this field.
But when I try to get the value of NextUpdate field from the database using SQL Data Reader it bombs out because the value of NextUpdate is null.
I did try to init the NextUpdate value if it is null using the following assignment but the error is still thrown telling me that field is null:
NextUpdate = dataReader.GetDateTime(dataReader.GetOrdinal("NextUpdate")) != null ? dataReader.GetDateTime(dataReader.GetOrdinal("NextUpdate")) : DateTime.MinValue,
Error:
Data is Null. This method or property cannot be called on Null values - at System.Data.SqlClient.SqlBuffer.get_DateTime()
Question:
Is there a short method of reading back and initializing a nullable **DateTime?** value?
Code:
using (IDataReader dataReader = db.ExecuteReader(dbCommand))
{
if (dataReader.Read())
{
esc = new Escalation
{
NextUpdate = dataReader.GetDateTime(dataReader.GetOrdinal("NextUpdate")),
RootCause = dataReader["RootCause"] != null ? dataReader["EM"].ToString() : ""
};
}
}
Property in Model:
public DateTime? NextUpdate { get; set; }

Compare the value against DBNull.Value instead of null:
NextUpdate = dataReader["NextUpdate"].Equals(DBNull.Value) ? (DateTime?)null : (DateTime?)dataReader["NextUpdate"]
(assuming that the NextUpdate member is a DateTime?)

Try following expression:
RootCause = (dataReader["RootCause"] is DBNull) ? (DateTime?)null : (DateTime?)dataReader["RootCause"] ;

Related

how to have Custom DB field as some value instead null while creating custom field in acumatica

I have a custom field to be created which is DB field. Initial value of the "Forecast to Complete" field is null even though default value is 0.0.
How do i get the DB field 0.00 initially?
[PXDBDecimal(2)]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName="Forecast To Complete")]
Maybe there's a mechanism assigning a null value after the field has been initialized.
If the record is created from UI you can try setting AllowNull property to False value:
<px:PXNumberEdit ID="edForecastToComplete" runat="server" AllowNull="False"
DataField="ForecastToComplete" Decimals="4" ValueType="Decimal" />
Worst case scenario you can force the assignation to be non null using C# property setter and backing field:
public decimal? _forecastToComplete;
[PXDBDecimal]
[PXUIField(DisplayName = "Forecast to Complete")]
public virtual decimal? ForecastToComplete
{
get { return _forecastToComplete != null ? _forecastToComplete : 0M; }
set { _forecastToComplete = value != null ? value : 0M }
}
public abstract class forecastToComplete : IBqlField { }
PXDefaultAttribute is allowing you to set the default value for new records.
The value is assigned when the record is inserted for the first time.
You can write FieldSelecting event handler and check the value of the field and set to the default one if the value is null.

Setting a string to be empty if value is null

I am not sure how to search for the issue I am trying to solve here. In the program I am writing (in VB.Net) I am trying to assign values pulled from a database to different variables in a structure.
Now my issue is that sometimes, some of the values pulled from the database are NULL, for example not every phone number has an extension. This is what I have for my code at the moment:
Structure CustomerContact
Public _name As String
Public _email As String
Public _fax As String
Public _phone1 As String
Public _phone2 As String
Public _phone3 As String
Public _ext1 As String
Public _ext2 As String
Public _ext3 As String
Public _type1 As String
Public _type2 As String
Public _type3 As String
End Structure
Dim contactData As DataTable = CustomerDBFunctions.GetCustomerContacts(Customer)
For Each row As DataRow In contactData.Rows
If contacts.Count < 1 Then
contacts.Add(New CustomerContact With {
._name = row.Item("FullName").ToString() & " (" & row.Item("ContactType").ToString() & ")",
._email = row.Item("Email").ToString(),
._fax = row.Item("Fax").ToString(),
._phone1 = row.Item("Phone").ToString(),
._ext1 = row.Item("Extension").ToString(),
._type1 = row.Item("PhoneType").ToString()})
End If
Next
Right now I am getting an error when the value in the database is NULL because it can't assign a NULL value to a string. I'd like to in the instances where a NULL value is present instead set the value of the variable to "" instead. I am just unsure how to code this.
Technically, the problem isn't that the column is null. String is a reference type, so it can but null (though, if it was null, you wouldn't be able to call ToString on it anyway). What's actually going on is that ADO.NET always returns DBNull.Value for all columns where the row contains a null value.
You could check it, like this:
If row.Item("Phone") <> DBNull.Value Then
customerContact._phone1 = row.Item("Phone")?.ToString()
End If
Note, I used ?. instead of . when calling ToString just in case the column actually is null rather than DbNull.Value. The reason for this is that I don't know what kind of code you're using to fill that DataTable. If it's ADO.NET that's filling it, it'll never be null, but if it's custom code that populates it via some other means, it might get actual nulls in it.
Since you are using a DataTable to return the value, it has a convenient IsNull method that you can use, which cleans up the code a little bit:
If Not row.IsNull("Phone") Then
customerContact._phone1 = row.Item("Phone")?.ToString()
End If
Obviously, if you're doing this a lot, it would be good to wrap it up into a reusable function. Also, if you want to shorten it up into one line, you could do so like this:
._phone1 = If(row.IsNull("Phone"), row.Item("Phone")?.ToString(), Nothing)
String concatenation can be used to convert Nothing to "" (other alternatives in my answer here)
._fax = row!Fax & "",

Identify String Value is Date or DateTime field in Apex Salesforce

Working on Salesforce. I have Transaction-1 and Transaction-2 object which has master detailed relationship with the Case Object. Dynamically query is getting created, it's executing on the database and Results coming like below.
This is sample result for Case:
({Id=500c000000B1LYCAA3, CaseNumber__c=RPQ-00001231,CreatedById=005c000UUUU, LastModifiedDate=2018-01-02 12:56:03, LastModifiedById=005c000UUUU, OwnerId=005c000UUUU, Category__c=Payment, SubCategory__c=Chase Paymentech, Origin=Email, CreatedDate=2018-01-01 15:22:14, Status=Pending, ContactId=003c000})
All the Date and DateTime fields I need to format based on the logged in users Locale. Mostly dd-MM-yyyy.
List<Sobject> s = Database.query(queryStr);
s has all above result. How to identify all Date and DateTime fields ? Do I need to iterate over it and used RegEx to identify Date or Date/Time fields ?
I tried something like below by taking a reference from link : https://releasenotes.docs.salesforce.com/en-us/summer16/release-notes/rn_apex_sobject_getmap.htm
Map<String, Object> finalMap = new Map<String, Object>();
List<Case> mylist = new List<Case>();
Map<String, Object> fieldsToValue = myCase.getPopulatedFieldsAsMap();
for(String fieldName : fieldsToValue.keySet()){
if(fieldsToValue.get(fieldName) instanceOf Date){
Date dateValue = Date.valueOf(fieldsToValue.get(fieldName));
finalMap.put(fieldName, DateTime.newInstance(dateValue.year(), dateValue.month(), dateValue.day()).format());
}else if(fieldsToValue.get(fieldName) instanceOf DateTime){
Date dateValue = Date.valueOf(fieldsToValue.get(fieldName));
finalMap.put(fieldName, DateTime.newInstance(dateValue.year(), dateValue.month(), dateValue.day()).format());
}else{
finalMap.put(fieldName, fieldsToValue.get(fieldName));
}
}
How to convert Map finalMap back to SObject?
How are you creating that dynamic query? If you're using "describe" calls to learn names of all fields on Case then you're not very far off.
Also remember that just because the query was dynamic it doesn't mean you have to store results in List<sObject>. It's perfectly fine to cast it to Cases and then they become so much easier to work with (and if you'll use the results on a VF page, it should handle date formatting for you!). List<Case> records = Database.query('SELECT Subject FROM Case LIMIT 10'); should be OK.
But, if you really have to, let's try to do it manually. I'll read all fields on Contact (I don't have any date fields on Case in my dev edition) and check how I should format them.
Map<String, Schema.SObjectField> fieldMap = Schema.SObjectType.Contact.fields.getMap();
Set<String> dateFields = new Set<String>();
Set<String> dateTimeFields = new Set<String>();
List<String> allFields = new List<String>(fieldMap.keyset());
allFields.sort();
System.debug(allFields);
for(String key : fieldMap.keyset()){
Schema.DescribeFieldResult dfr = fieldMap.get(key).getDescribe();
Schema.DisplayType t = dfr.getType();
if(t == DisplayType.Date){
dateFields.add(key);
} else if(t == DisplayType.DateTime){
dateTimeFields.add(key);
}
}
System.debug('Date: ' + dateFields);
System.debug('DateTime: ' + dateTimeFields);
String query = String.join(new List<String>{
'SELECT ' + String.join(allFields, ','),
'FROM Contact',
'LIMIT 10'
}, '\n');
List<Contact> contacts = (List<Contact>) Database.query(query);
for(Contact c : contacts){
System.debug(c);
for(String field : dateFields){
String value = c.get(field) == null ? '(empty)' : ((Date) c.get(field)).format();
System.debug(field + ': ' + value);
}
for(String field : dateTimeFields){
String value = c.get(field) == null ? '(empty)' : ((DateTime) c.get(field)).format();
System.debug(field + ': ' + value);
}
}

How to search for empty strings in a text field with Entity Framework?

I'd like to know how can I search for empty strings when I'm using a text type field with Entity Framework.
I've looked the SQL query that Entity is generating and It's using LIKE to compare because It's searching in a text type field, so when I use .Equals(""), == "", == string.Empty, .Contains(""), .Contains(string.Empty), and everything else, It's returning all results because it sql query is like '' and the == command throws exception because It uses the = command that is not valid with text type field.
When I try to use .Equals(null), .Contains(null), == null, It returns nothing, because It is generating FIELD ISNULL command.
I already tried the .Lenght == 0 but It throws an exception...
This works for me:
public class POCO
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
static void Main(string[] args)
{
var pocos = new List<POCO>
{
new POCO { Id = 1, Name = "John", Description = "basic" },
new POCO { Id = 2, Name = "Jane", Description = "" },
new POCO { Id = 3, Name = "Joey", Description = string.Empty }
};
pocos.Where(x => x.Description == string.Empty)
.ToList()
.ForEach(x => Console.WriteLine($"{x.Id} {x.Name} {x.Description}"));
}
However the issue MAY BE that your T4 generated object is not fully realized with data you can use, if you are using Entity Framework. EG the translation from the database is not populating objects to interrogate correctly. I would just do an operation like this to see:
using (var context = new YOURCONTEXTNAME())
{
var persons = context.YOURDATABASEOBJECT.ToList();
persons.ForEach(x => Console.WriteLine($"{x.COLUMNINQUESTION}"));
}
If you are successfully having data in it, it should be retrieved. I would not use text if possible. Use a varchar(max) nvarchar(max) xml, whatever text will be deprecated eventually and is bad form so to speak to continue using at this point.
EDIT
Okay I see, the answer is you cannot interogate the object until it is fully realized when it is text. I did a test on my local database and created a context and tested it and sure enough you cannot do a '== string.empty', '== ""', or 'String.IsNullOrEmpty()' on a text. However you can do it once the object is materialized in a realized object. EG:
// Won't work as context does not understand type
//var persons = context.tePersons.Where(x => x.Description == string.Empty).ToList();
//Works fine as transformation got the object translated to a string in .NET
var start = context.tePersons.ToList();
var persons = start.Where(x => x.Description == String.Empty).ToList();
This poses a problem obviously as you need to get ALL your data potentially before performing a predicate. Not the best means by any measure. You could do a sql object for this instead then to do a function, proc, or view to change this.

SqlDateTimeOverflow exception with nullable DateTime fields with Dapper update

I have a db table with several DateTime fields with null values. These are mapped to nullable DateTimes in my class.
If I try to perform an update with Dapper, within my data layer:
using (IDbConnection cnn = new SqlConnection(DB.getConString()))
{
cnn.Open();
return cnn.Execute((this.OptionID == 0 ? _insertSQL : _updateSQL), this);
}
I get a SqlDateTimeOverflow exception (because the DateTime field is '01/01/0001 00:00:00' rather than null.
Is the only way around this to specify each parameter individually and switch the value to null like this:
using (IDbConnection cnn = new SqlConnection(DB.getConString()))
{
cnn.Open();
return cnn.Execute("UPDATE MyTable SET MyDateField = #MyDateField", new {MyDateField = (MyDateField.HasValue? MyDateField : Null), etc etc... );
I have about 50 fields in the table so this would be quite a bit of code, plus there is an INSERT method to update similarly too. Is there an easier syntax I am missing?
The issue here is that 01/01/0001 00:00:00 is not a "null value"; if you had used DateTime? I suspect it would have worked fine. However, I also have to recognize that DateTime.MinValue has often (mainly due to .NET 1.1 lacking nullable structs) been used to represent a null value. My preferred suggestion here would be to simply use DateTime?. The min-value map is a bit complicated as we might also consider whether that should be automatically mapped instead to the sql min-value (January 1, 1753).
Re the update statement - maybe add an extension method to map between min-value and null?
public static DateTime? NullIfZero(this DateTime when) {
return when == DateTime.MinValue ? (DateTime?)null : when;
}
and use:
new { MyDateField = MyDateField.NullIfZero() }
but again, if MyDateField was DateTime?, you could just use:
new { MyDateField }

Resources