| |
| |
|
| |||||
| |||||||||||||||||||||||
| |||||||||||||||||||||||
| |||||
| |||
| |
| |
|
| |||||
| |||||||||||||||||||||||
| |||||||||||||||||||||||
| |||||
| |||
I recently had the pleasure of being featured on Azure DevOps Podcast. The host, Jeffery Palermo, interviewed me about a topic I’m passionate about “SQL Server For Developers”. These are features available within SQL Server that make some specific development tasks easy to implement. Some of these features include:
File Streams: “Pointers” that allow you to store a BLOB within the SQL Server File Groups and maintain security over it, not storing it in a DB and bloating the table size.
Temporal Tables: Tables that automatically maintain historical changes to every record with a time span of those changes.
FileTables: Tables that duplicate a structure of a specific folder, giving developers the ability to quickly query the files one they’re dropped in the folder.
I discuss these topics and more on the podcast. For the full podcast, please visit http://azuredevopspodcast.clear-measure.com/sam-nasr-on-sql-server-for-developers-episode-122#
Virtual User Group Meetings
Virtual Conferences
Virtual User Group Meetings
Virtual Conferences
The Global AI Bootcamp is a free one-day event organized across the world by local communities that are passionate about artificial intelligence on Microsoft Azure. It takes place on January 15-16th, 2021 with venues on every continent. The event is a perfect balance of quality content, awesome talks, and hands-on learning with like-minded peers in your community. Currently, there are 4 events happening in the US in different time zones.
Las Vegas, NV: https://globalai.community/global-ai-bootcamp-2020/united-states-las-vegas
Plano, TX: https://globalai.community/global-ai-bootcamp-2020/usa-plano-tx-1
Des Moines, IA: https://globalai.community/global-ai-bootcamp-2020/usa-des-moines
Raleigh, NC: https://globalai.community/global-ai-bootcamp-2020/united-states-raleigh
Find the one that best suits you or choose another event elsewhere in the world. To see a full list of all events, please visit https://globalai.community/global-ai-bootcamp-2020/
For more info on the bootcamp, please visit https://globalai.community/about.
Virtual User Group Meetings
Virtual Conferences
A situation came up where a Windows Forms application required 2 combo boxes filled with customer IDs. A caption of that form is shown below, where a user must select “From Customer ID” and “To Customer ID”. Since both combo boxes were populated from the same data column, the natural thing to do was to use the same code for filling both with the same data. However, this caused an undesirable effect. When the user selected the “From Customer ID”, the “To Customer ID” was automatically populated with the same value.
To resolve this problem, 2 separate DataTable objects must be used, dtFrom and dtTo. Although both objects are populated using the same method (dm.GetCustomerIDNames()), keeping the objects separate will prevent one control selection from effecting the other control. See sample code shown below.
private void FillCustomerIDs()
{
Cursor.Current = Cursors.WaitCursor; //Produces a "wait cursor" while data is loading
//Must have 2 separate instances for the data method.
//DataTables must also be kept separate to prevent issues with binding
//Data must NOT be cached, must be retrieved directly from the DB for each call.
//Otherwise all comboboxes will bind to the same datasource and to each other.
DataTable dtFrom = dm.GetCustomerIDNames();
FillFromCustomer(dtFrom);
DataTable dtTo = dm.GetCustomerIDNames();
FillToCustomer(dtTo);
}
private void FillFromCustomer(DataTable dtFrom)
{
AutoCompleteStringCollection acsFrom = new AutoCompleteStringCollection();
foreach (var rec in dtFrom.AsEnumerable())
acsFrom.Add(rec.ItemArray[0].ToString());
cbFromCustomerID.AutoCompleteCustomSource = acsFrom;
cbFromCustomerID.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cbFromCustomerID.AutoCompleteSource = AutoCompleteSource.CustomSource;
cbFromCustomerID.DataSource = dtFrom;
cbFromCustomerID.DisplayMember = "ID";
cbFromCustomerID.ValueMember = "Name";
cbFromCustomerID.SelectedIndex = -1; //Forces a blank item in the comboBox
}
private void FillToCustomer(DataTable dtTo)
{
AutoCompleteStringCollection acsTo = new AutoCompleteStringCollection();
foreach (var rec in dtTo.AsEnumerable())
acsTo.Add(rec.ItemArray[0].ToString());
cbToCustomerID.AutoCompleteCustomSource = acsTo;
cbToCustomerID.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cbToCustomerID.AutoCompleteSource = AutoCompleteSource.CustomSource;
cbToCustomerID.DataSource = dtTo;
cbToCustomerID.DisplayMember = "ID";
cbToCustomerID.ValueMember = "Name";
cbToCustomerID.SelectedIndex = -1;
}
Virtual User Group Meetings
Sep 29: .Net Virtual User Group - “VS/Code Hidden Gems”
https://www.meetup.com/dotnet-virtual-user-group/events/
Oct 15: Akron AITP User Group – “Enterprise Architecture in the Cloud”
https://www.meetup.com/AkronAITP/events/
Oct 21: Hudson Software Craftsmanship - “Crafting Better Software”
https://www.meetup.com/Hudson-Software-Craftsmanship-Meetup/
Oct 22: Cleveland C#/VB.Net User Group - “Multi-Model Databases in Azure SQL”
https://www.meetup.com/Cleveland-C-VB-Net-User-Group/
Virtual Conferences
Sep 26: Northern Virginia Code Camp
https://novacodecamp.org/index.html
Oct 3: SQL Saturday-Memphis
https://www.sqlsaturday.com/1003/
Oct 24: SQL Saturday-Oregon
https://www.sqlsaturday.com/1000/
Listed below is an example of using Sorting and Filtering on ADO objects. This examples utilizes an Oracle DB, but ADO objects can be used with various databases. In this situation, the entire data table is retrieved in memory. When needed, that data table can be sorted using a specific column or filtered using the SELECT method with the specified predicate.
public void ProcesssData()
{
//Retrieve entire data table into memory
gPOLines = RGNdm.GetLineNumsByPO(PO);
//SORT by desired field
gPOLines.DefaultView.Sort = " PART_ID ASC ";
//SELECT (filter) for specific criteria
DataRow[] filteredRows = gPOLines.Select($" PART_ID = '{cbPartID1.Text}' ");
if (filteredRows.Count() >= 1)
{
decimal userOrderQty = (decimal)filteredRows[0].Field<double>("USER_ORDER_QTY");
}
}
public DataTable GetLineNumsByPO(string PO)
{
string sql = $" SELECT LINE_NO, PART_ID, USER_ORDER_QTY FROM PO_TABLE ";
OracleDataAdapter da = new OracleDataAdapter(sql, User.connString);
DataTable tbl = new DataTable();
da.Fill(tbl);
return tbl;
}
Handling exceptions is a critical task in any application. When an exception is logged, it needs to have sufficient detail so it can be researched at a later time. In addition, as a developer, you want to extract and log as much detail about it as possible. Some of those details include the file name and line number where the exception occurred. Listed below is a method that handles exceptions, extracts the message, file name, and line number for logging.
private void ProcessEx(Exception ex, string details = "")
{
int lineNumber = 0;
const string lineNumberMarker = ":line ";
string traceText = ex.StackTrace + ""; //Adding empty space will ensure final value is never null
int lineIndex = traceText.LastIndexOf(lineNumberMarker);
if (lineIndex >= 0)
{
string lineNumberText = traceText.Substring(lineIndex + lineNumberMarker.Length);
int.TryParse(lineNumberText, out lineNumber);
}
string fileName = string.Empty;
const string fileNameMarker = " in ";
int fileNameIndex = traceText.LastIndexOf(fileNameMarker);
if (fileNameIndex >= 0)
{
int fileNameStart = fileNameIndex + fileNameMarker.Length;
int fileNameLength = lineIndex - fileNameIndex - fileNameMarker.Length;
fileName = traceText.Substring(fileNameStart, fileNameLength);
}
//Log error message + file/line info
string msg = $"Error: {ex.Message}. \r\n{details}";
string fileInfo = $"\r\nFile {fileName}, Line {lineNumber} ";
Logging lg = new Logging();
lg.WriteLog(msg + fileInfo);
}
While working with Oracle SQL Developer, you may encounter the following message when the password is near expiration.
A database warning was encountered performing the requested operation:
ORA-28002: the password will expire within 5 days
28002. 00000 - "the password will expire within %s days"
*Cause: The user's account is about to expire and the password
needs to be changed
*Action: change the password or contact the DBA
Vendor code 28002
To change the password:
That’s it. Seems simple but if you’re not familiar with SQL Developer, this can be a frustrating.
Overview: I have a Windows Form application, built in .Net 4.7.2 using Visual Studio 2019. The form has 3 PartID combo boxes, allowing the user to select 3 distinct part IDs from a finite list of parts in the drop down.
Problem: When the combo box was being filled with values in the drop down list, the following exception was thrown.
Exception:
System.AccessViolationException
HResult=0x80004003
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=<Cannot evaluate the exception source>
StackTrace:
<Cannot evaluate the exception stack trace>
Code:
private void cbPO_LostFocus(object sender, EventArgs e)
{
FillPartID1(poLines);
}
private void cbPO_SelectedIndexChanged(object sender, EventArgs e)
{
FillPartID1(poLines);
}
private void FillPartID1(DataTable poLines)
{
AutoCompleteStringCollection acs1 = new AutoCompleteStringCollection();
foreach (var rec in poLines.AsEnumerable())
acs1.Add(rec.ItemArray[1].ToString());
cbPartID1.AutoCompleteCustomSource = acs1;
cbPartID1.AutoCompleteMode = AutoCompleteMode.SuggestAppend; //Exception thrown here during 2nd execution
cbPartID1.AutoCompleteSource = AutoCompleteSource.CustomSource;
cbPartID1.DataSource = poLines;
cbPartID1.DisplayMember = "PART_ID";
cbPartID1.ValueMember = "PART_ID";
cbPartID1.SelectedIndex = -1;
}
Cause: The FillPartID1() was being called twice due to event handlers cbPO_SelectedIndexChanged() and cbPO_LostFocus().
Solution: Refactor the code so FillPartID1() is called only once.
Overview: I have a Windows Form application, built in .Net 4.7.2 using Visual Studio 2019. The form has 3 PartID combo boxes, allowing the user to select 3 distinct part IDs from a finite list of parts in the drop down.
Problem: Once the user selects a PartID in any combo box, the other 2 combo boxes are automatically set to that value.
private void Start()
{
DataTable POLines = GetLineNumsByPO(PO);
FillPartID1(POLines);
FillPartID2(POLines);
FillPartID3(POLines);
}
private void FillPartID1(DataTable poLines)
{
AutoCompleteStringCollection acs1 = new AutoCompleteStringCollection();
foreach (var rec in poLines.AsEnumerable())
acs1.Add(rec.ItemArray[1].ToString());
cbPartID1.AutoCompleteCustomSource = acs1;
cbPartID1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cbPartID1.AutoCompleteSource = AutoCompleteSource.CustomSource;
cbPartID1.DataSource = poLines;
cbPartID1.DisplayMember = "PART_ID";
cbPartID1.ValueMember = "PART_ID";
cbPartID1.SelectedIndex = -1;
}
private void FillPartID2(DataTable poLines)
{
AutoCompleteStringCollection acs2 = new AutoCompleteStringCollection();
foreach (var rec in poLines.AsEnumerable())
acs2.Add(rec.ItemArray[1].ToString());
cbPartID2.AutoCompleteCustomSource = acs2;
cbPartID2.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cbPartID2.AutoCompleteSource = AutoCompleteSource.CustomSource;
cbPartID2.DataSource = poLines;
cbPartID2.DisplayMember = "PART_ID";
cbPartID2.ValueMember = "PART_ID";
cbPartID2.SelectedIndex = -1;
}
private void FillPartID3(DataTable poLines)
{
AutoCompleteStringCollection acs3 = new AutoCompleteStringCollection();
foreach (var rec in poLines.AsEnumerable())
acs3.Add(rec.ItemArray[1].ToString());
cbPartID3.AutoCompleteCustomSource = acs3;
cbPartID3.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cbPartID3.AutoCompleteSource = AutoCompleteSource.CustomSource;
cbPartID3.DataSource = poLines;
cbPartID3.DisplayMember = "PART_ID";
cbPartID3.ValueMember = "PART_ID";
cbPartID3.SelectedIndex = -1;
}
Solution: The Start() method was modified to have 3 separate datatables, each one passed to a partID comboBox. This resolved one combo box selection updating the other combo boxes
private void Start()
{
//3 separate datatables were required to avoid having one PartID selection update another PartID control.
DataTable POLines1 = GetLineNumsByPO(PO);
DataTable POLines2 = GetLineNumsByPO(PO);
DataTable POLines3 = GetLineNumsByPO(PO);
FillPartID1(POLines1);
FillPartID2(POLines2);
FillPartID3(POLines3);
}