Friday, July 21, 2017

The magic of LINQ

LINQ is one of my favorite features of the Microsoft .NET Framework.

 It was introduced with VS.NET 2008 / .NET 3.5, and is absolutely incredible.

I often wish we had something like it in Java, where I'm still cobbling together foreach loops to process Object Lists, SAX Parsers to process XML, and HQL/JP-QL to select data. I needed to take a list of Objects and write out XML Export in .NET.

 Here's my code:
            XDocument xmlDocument = new XDocument(new XElement("Agendas",
                from agenda in orgAgendas
                select new XElement("Agenda",
                    new XAttribute("Id", agenda.AgendaId),
                    new XElement("AgendaName", agenda.AgendaName),
                    new XElement("MeetingDateTime", agenda.MeetingDateTime),
                    new XElement("MeetingLocation", agenda.MeetingLocation),
                    new XElement("OriginalFileUrl", agenda.AgendaOrigUrl),
                    new XElement("ConvertedFileUrl", agenda.AgendaPdfUrl),
                    new XElement("Type", agenda.Type),
                    new XElement("TypeDesc", agenda.TypeDesc),
                    new XElement("CreateDate", agenda.CreateDate),
                    new XElement("Published", agenda.Publish),
                    new XElement("Sections",
                    from section in agenda.AgendaSections
                    select new XElement("Section", 
                        new XAttribute("Id", section.SectionId),
                        new XElement("Text", section.SectionText),
                        new XElement("Type", section.Type),
                        new XElement("Owner", userUtils.GetUserNameById(section.OwnerId.GetValueOrDefault())),
                        new XElement("Items",
                        from item in section.AgendaSectionItems
                            select new XElement("Item", 
                            new XAttribute("Id", item.ItemId),
                            new XElement("Text", item.ItemText),
                            new XElement("Owner", userUtils.GetUserNameById(item.OwnerId.GetValueOrDefault())),
                            new XElement("Assignee", userUtils.GetUserNameById(item.AssigneeId.GetValueOrDefault())),
                            new XElement("Completed", item.Completed),
                            new XElement("DueDate", item.DueDate),
                            new XElement("Approved", item.Approved),
                            new XElement("Attachments",
                            from attachment in item.Attachments
                                select new XElement("Attachment",
                                new XAttribute("Id", attachment.AttachmentId),
                                new XElement("Description", attachment.Description),
                                new XElement("OriginalFileUrl", attachment.OriginalUrl),
                                new XElement("ConvertedFileUrl", attachment.ConvertedUrl),
                                new XElement("Confidential", attachment.Confidential)
                                )   
                            )
                            )
                        )
                    )
                    )
                )
            ));
So elegant ... :-)

Saturday, July 15, 2017

Entity Framework Woes

I started getting a new and horrifying Entity Framework error I had never seen before. Conflicting changes detected. This may happen when trying to insert multiple entities with the same key. It looked like it was due to me saving a parent and having it cascade to save children. Soooo, I took it upon myself to save parent, save child, reconstitute relationship. This was horrifying. My code went from Exhibit A:
        /// 
        /// Helper Method.  Updates Sections and Items
        /// 
        /// Agenda ID
        /// List of AgendaSections
        /// The Current User creating Sections and Items
        private void UpdateSectionsAndItems(int agendaId, IList<AgendaSection> agendaSections, IUser currentUser)
        {
            List<AgendaSectionItem> agendaSectionItems = new List<AgendaSectionItem>();

            int sectionNumber = 0;
            foreach (AgendaSection section in agendaSections)
            {
                section.Number = sectionNumber;
                section.AgendaId = agendaId;
                section.OwnerId = currentUser.UserId;
                if (section.AgendaSectionItems != null)
                {
                    int itemNumber = 0;
                    foreach (AgendaSectionItem item in section.AgendaSectionItems)
                    {
                        item.Number = itemNumber;
                        itemNumber++;
                        item.OwnerId = currentUser.UserId;
                        agendaSectionItems.AddRange(section.AgendaSectionItems);
                    }
                }
                sectionNumber++;
            }
            db.AgendaSections.AddRange(agendaSections);
            db.AgendaSectionItems.AddRange(agendaSectionItems);
            db.SaveChanges();
        }
To the following horrifying monstrosity:
        /// 
        /// Helper Method.  Updates Sections and Items
        /// 
        /// Agenda ID
        /// List of AgendaSections
        /// The current user
        private void UpdateSectionsAndItems(int agendaId, IList<AgendaSection> agendaSections, IUser currentUser)
        {            
            // This is a horrible workaround to deal with this mysterious error: 
            //   Conflicting changes detected. This may happen when trying to insert multiple entities with the same key.
            // That I get when I simply try to save parent (AgendaSection) and let it cascade down to the child (AgendaSectionItems).
            IDictionary<int, List<AgendaSectionItem>> tempItemDictionary = new Dictionary<int, List<AgendaSectionItem>>();
            int sectionNumber = 0;
            foreach (AgendaSection section in agendaSections)
            {
                section.Number = sectionNumber;
                section.AgendaId = agendaId;
                if (section.AgendaSectionItems != null)
                {
                    int itemNumber = 0;
                    foreach (AgendaSectionItem item in section.AgendaSectionItems)
                    {
                        item.Number = itemNumber;
                        itemNumber++;
                        item.OwnerId = currentUser.UserId;
                        if (tempItemDictionary.ContainsKey(section.Number.GetValueOrDefault()))
                        {
                            List<AgendaSectionItem> agendaSectionItems = tempItemDictionary[section.Number.GetValueOrDefault()];
                            agendaSectionItems.Add(item);
                        }
                        else
                        { 
                            List<AgendaSectionItem> agendaSectionItems = new List<AgendaSectionItem>();
                            agendaSectionItems.Add(item);
                            tempItemDictionary.Add(section.Number.GetValueOrDefault(), agendaSectionItems);
                        }
                    }
                }
                // More workaround
                section.AgendaSectionItems = null;
                sectionNumber++;
            }
            db.AgendaSections.AddRange(agendaSections);
            db.SaveChanges();

            // More workaround ... FML
            foreach (int i in tempItemDictionary.Keys)
            {
                IList<AgendaSectionItem> items = tempItemDictionary[i];
                AgendaSection agendaSection = agendaSections.First(s => s.Number == i);
                if (agendaSection != null)
                {
                    foreach (AgendaSectionItem item in items)
                    {
                        item.SectionId = agendaSection.SectionId;
                        if (agendaSection.AgendaSectionItems == null)
                        {
                            agendaSection.AgendaSectionItems = new List<AgendaSectionItem>();
                        }
                        agendaSection.AgendaSectionItems.Add(item);
                        db.AgendaSectionItems.Add(item);
                        db.SaveChanges();
                    }
                }
            }            
        }
When I finally got down to it, what I had done was accidentally mapped my primary key on a table as a foreign key in a completely incorrect mapping. So, I went and rolled back the monstrosity and will sleep better tonight.

Why can't I find a 3rd party product to send emails that works?

*sigh* it should not be that difficult.

So far, I have tried:


  • SendGrid:  They seemed to work.  However, I inadvertently spun up a new project and committed my SendGrid API Key in my source code to GitHub.  Apparently they like being Big Brother and scan GitHub for their API Keys and shut down accounts when they find it.  Great.  Shut down my account, that's fine I'm moving on ...
  • MailGun: This did ... not ... work.  I walked it in the debugger, no errors, went through all their documentation.  Just did not work. I don't have the time, patience, or bandwidth to make it work.
  • SparkPost: My current attempt.  Really hoping 3rd time is a charm. 

Wednesday, July 12, 2017

Introducing: new EDMX and a personal challenge

I am taking a break to build a Recruiter Review Website.

The Data Model is much simpler:


I am a firm believer that a good Senior Developer should be able to go into a 1 hour Interview and build a fully functional website.

I am challenging myself to build this new site in 1 day.  A 1 hour Website should be functional, but will not look polished and have nice widgets and UI, but I really do believe a good Senior Developer should be able to build a functional CRUD site in an hour.

Monday, July 10, 2017

Introducing: my EDMX Diagram

I am building my own Software Project.

Here is my Data Model (Entity Framework EDMX Diagram).



You know ... that horrifying moment when you ask yourself "is my Data Model too complex?"

Seriously though, I went with the simplest Data Model possible given the scope and functionality of my site, and keeping with 3rd Normal Form, and this is what I arrived at.

Sunday, July 9, 2017

Azure WebJobs

Document Conversion Job (WebJob)


1.      Release Build from Visual Studio


2.  Navigate to DocumentConversionJob\bin\Release and zip everything into Release.zip

To Deploy open https://portal.azure.com

Azure Web App -> Web Jobs ->  Delete existing DocumentConversionJob

Add Web Job.

Screen capture of “Add Web Job” Interface:





















3  3.  For checking on the health of the WebJob, there will be a URL:


Wednesday, July 5, 2017

C# 4.0 - dynamic ... my new best friend

Okay, I can see the opportunity for all sorts of abuse if used incorrectly.

However, I found the perfect use for C# dynamic keyword ... dealing with JSON.

Often times, I need to send back a JSON Result with a subset of my object graph, or a flattened object graph.  I only want to send back the fields that the front end needs (trying to be judicious with bandwidth).


  dynamic result = new Object();
  result.AgendaOrigFileName = agenda.AgendaOrigFileName;
  result.AgendaId = agenda.AgendaId;
  return Json(result);

Tuesday, July 4, 2017

Microservices in Azure

I've started deploying Microservice VMs in the Microsoft Azure Cloud.

My VMs convert Microsoft Office Documents to PDF.  I believe this is a perfect use of Microservice / appliance Virtual Machines.

Here's how I set up the VMs.

Document Conversion VM Create/Config
  1. Deploying files via copy/paste (never able to get Web Deploy working).  This was done by editing the RDP Connection->Local Resources->(check) Clipboard
  2. VM -> Network Interfaces -> inbound Security Rules.  Add http / port 80
  3. VM -> Network Interfaces -> Outbound Security Rules.  Add http / port 80
  4. Provide a Domain Name for the VM.  Go to VM -> Properties, click on the link with IP/DNS.  Will see the following interface, enter a DNS Name.



Monday, July 3, 2017

ASP.NET MVC ViewModel not updating in View

I ran into a new and very painful ASP.NET MVC issue.

I was updating a ViewModel property in my Controller, but was not seeing the value updated in my View.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Manage(ManageAgenda manageAgenda)
{
    // Save an Agenda and get back a sequence-generated Unique ID
    manageAgenda.AgendaId = agenda.AgendaId;
    return View(manageAgenda);
}
On my View:
@using (Html.BeginForm("Manage", "Agenda", FormMethod.Post, new { @id = "ManageAgendaForm"})) { @Html.AntiForgeryToken() @Html.HiddenFor(model => model.AgendaId)
Now, this became REALLY painful because my ID kept coming back as 0, so upon each subsequent save, a brand new record was being created ...
The fix for this turned out to be adding the following line of code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Manage(ManageAgenda manageAgenda)
{
    // Save an Agenda and get back a sequence-generated Unique ID
    manageAgenda.AgendaId = agenda.AgendaId;
    // Send back the Agenda ID.
    // Clear out the ModelState so that the ViewModel will be updated in the View.
    // See: https://stackoverflow.com/a/9645580/463196
    ModelState.Clear();
    return View(manageAgenda);
}
What scares me is I *do not understand* why this works. Other comments just described it as a bug in ASP.NET MVC. As a Software Engineer, I do not like fixes I cannot explain to others or myself.