Please navigate to the bottom of the page for Table of Contents

Monday, November 18, 2013

Overall interview evaluation parameters for a software engineer/architect

When giving technical interviews, either as a software engineer or software architect, you are not only evaluated on technical prowess but also on a bunch of other factors that make a good or not so good hire. Every company and every team has a different set of parameters on which they evaluate candidates, but here is a list that is quite common across most organizations. Note that each of these categories may have a different weight assigned to them to provide emphasis to specific skills that are more important to that team. In addition, the team and company culture have a strong influence on the hiring process and that can be learnt to some degree by reading about the company and their development philosophy.

I have also given a typical question to highlight what I mean by that category. Again, this may vary, but it should give you a good idea on how to present yourself during an interview.

Quality
Question
Energetic
N/A – Based on observation
Ready to learn
N/A – Based on observation
Takes new challenges
What would you do if you happen to finish your assigned task earlier? (Hint: read new tech, fix existing bugs, help teammate, slack off, analyze data, etc.)


Potentially independent
Tell me about a situation that required you to build your knowledge base before solving a complex problem

Results oriented
When do you consider a problem solved? How much time would you spend tweaking a solution to gain more performance? When is the performance good enough and you move to the next problem?

Growth potential
Describe a time when you had to complete a project in which you were provided with very little information or guidance

Adaptability
Describe a time when you presented a proposal or provided a service that was given an unfavorable response by stakeholders
Sincerity
Tell me about a time when you did not meet a planned milestone
Mentoring
Tell me about a time when you mentored others in your area of technical expertise
Provide an example of a time when you had to motivate others to complete a challenging deliverable.


Team Fit
Describe your most recent experience working with a virtual team.
What were the challenges you faced and how did you work through them?
Describe a situation when you led a project with a geographically or functionally diverse team
Describe how you have collaborated with others outside of your immediate team to resolve a critical issue
Tell me about a time when you experienced a major organizational change (for example, a reorganization, a new supervisor or a new procedure) that created stress. 

Company Fit
Why would you join our team and not another competitor?
What are 3 words that describe you?
What experience do you have that qualifies you for the job?
What do you consider your strengths?
What are your short term and long term goals



Technical
Given an array of numbers, write a function to remove duplicates and sort the output
In an org, given two employees, find their common manager
SQL: Given an employee table with salaries, find the employees that make more than their manager

Design: Consider an API management system. There is an API portal (say NB) that has managed services that point to a variety of backend systems (let’s call them SB). The Developer apps call the NB system which checks for quotas, throttling, authentication, access, etc and if everything is good, massages the request to call the appropriate SB system. The SB response is then returned to the client. Some requests may be stateless (e.g. GetJobs()) and some may be more stateful (e.g. async call to translate speech to text). Design me a transactionID/conversationIDsystem that allows me to keep track of different requests for purposes of monitoring, measuring latency, failures, etc.

Data driven
Describe a long-term project or assignment of which you took ownership. How did you make sure each milestone was achieved in a timely manner?
You wanted to make a change on the website, but your product manager was not in favor. How would you handle this?
Tell me about a situation when you had multiple alternatives from which to choose. What steps did you take to determine the most appropriate alternative?


This should give you a good idea on what many interviewers gauge a candidate. There are of course many more factors and qualities that guide the hiring process. 

How to answer software design interview questions

In the previous post on How to answer algorithmic programming interview questions, we discussed a templated approach on how to solve algorithmic questions. In this post, we will explore similar steps that will help you think in the correct direction while solving design questions.

To set some context, design questions differ from their algorithmic counterparts in how you approach them. Design questions, as the name implies, focus more on how to architect a larger system than just writing an algorithm to solve a specific aspect of the problem. Some examples of design questions may include:
1. Design a transaction ID/conversation ID for an API system
OR
2. Design a cloud based employee performance management system that can support over 100,000 employers.

Here the approach you would take would focus more on component/system design with performance, throughput, stability, scale and redundancy in mind. Let's review some of these buckets that you should keep in mind when designing large scale software systems.

Design principles and philosophy: Before you even start your broader system design, talk about your design philosophy. That is, what are your guiding principles for a good design. For example, simple, robust, scalable, resilient to failures, etc.

Product requirements: To design a good system, you need to understand what the requirements are; UI, interactions, Middle tier, backend, etc. What are the different entry/exit points, metrics that matter, etc.

System requirements: Once you have articulated the different product requirements, it is important to think and list down system requirements such as availability, scale, perf, geo-redundancy, etc. This and the product requirements will help you design a system that meets both functional and system requirements.

Which features would be Pri-0 versus Pri-1 and why: This is an optional part and you might give this more attention if the interview is more focused on product design than a system design.

Architecture: This is the meat of the interview. Here you would define various components of the system, how they interact and the data flows. You would also talk about data redundancy, database backups, load balancing, etc.

Measurement and logging: All good designs should talk about how you would measure performance, key metrics and do your logging and analysis.

Performance characteristics: You should also spend some time discussing the performance characteristics of your proposed architecture. Pay special attention to key operations and high traffic sections. 

Geo-redundancy: Designing systems that serve traffic all across the globe and allow failures and fall-backs are strong characteristics of a good design. 

Define how the solution would scale: Scale is something that every company, product design looks for. How would you handle increased traffic across the globe while keeping your performance relatively constant should be thought through from the very start.

Security: Most non-trivial products today have security from start. User info, financial information need to be properly secured and steps defined on how to mitigate breaches.

Talk about stability/health checks: A highly performant system that is unstable does not help. Focus on aspects that help build a stable system. Local redundancy, failovers, uptime are important factors.

Deployments/Zero downtime: Think and define how zero downtime and rolling deployments will happen. 

Proposed enhancements/improvements: Since this is a first pass on design, talk about alternate design and different technologies.For example, RabbitMQ versus QPID, PostgresSQL versus Cassandra. etc.

Of course, a good system design has a strong architectural component. Focus on building a strong foundation and rest everything should be a guiding factor towards that goal.

How to answer algorithmic programming interview questions

Most technical programming interviews have at least one or more programming whiteboard exercise. These generally include questions like
1. Write an algorithm to reverse a linked list.
OR
2. Write a function to sort a binary tree.
OR
3. Write an algorithm to remove duplicates and sort an arbitrary long input string comprised of single digit numbers.

Almost all of such questions require you to explain your thinking on whiteboard and then design and explain your algorithm or function on the whiteboard in real time. So how do you answer algorithmic questions and make sure that your thinking and direction covers various different aspects that an interviewer is looking for.

Here is one such list that you can keep at the back of your mind when attempting to solve such questions. Some of them may not be valid for some questions, but you should try to model your answer around these tenets.

Understand the question: Make sure you repeat the question. The goal is to understand exactly what the interviewer is looking for in your answer. 

Asks clarifying questions to understand what the interviewer is looking for: This is an extension of the first point. You need to ask as many clarification questions as necessary to make sure you are solving the right problem. Most questions will have ambiguity built into them and it is your job to make sure you get them clarified.


Paraphrase the problem: Once you have clearly understood the question, paraphrase it. This helps make sure to the interviewer that you really get the question and the subtleties associated with it. 


Call out assumptions: Before and as you are designing the algorithm, make sure to clearly spell out any assumptions that you are making. For example, array index starts at 0, the input is assumed to be made only of numbers, etc. Unsaid assumptions have a bad habit of coming back and biting you later in the exercise.


Articulate at least one or two acceptable input and output: If possible, it is always good to show one or two possible input cases and the desired output from your solution. This helps both you and interviewer to make sure that you are on track to understanding the question.


Define broad solution: Once you have reached this stage, define a broad outline of the solution. No code, just the big ticket steps. For example, Sort the input, find the most common elements, use hashmaps, clean output to remove duplicates, etc.


Define possible other solutions (using hashtags, linked lists, trees, etc): Once you have a broad solution, make sure that you discuss or highlight some alternative approaches. This shows that you can think from a higher perspective and evaluate alternate approaches.


Draw a “manual run” on the whiteboard for input/output: To make sure that your high level solution really works, manually run your input through that to make sure you get the desired output.


Define the high level design: Now is the time to start hashing out your high level design components. Basic functions, data structures, core logic. A lot of your time would be spent here understanding and building your design.


Define the basic algorithm: Here you would be defining the core logic and algorithm for your problem. Make sure you keep talking about your approach.


Discuss time taken by algorithm (1, log(n), etc): Once you have the detailed design in place, discuss the performance characteristics of your solution. Think about minor tweaks or improvements you can do here to improve the performance. 


Talk about error handling: This is very important. You should at least talk about different error conditions and how your algorithm would handle it. You can keep the whiteboard clean by not writing every possible case, but make sure you talk about them.


Handle edge cases: Any good algorithm handles edge cases without going through the entire process very fast. Make sure you spend a few minutes thinking and articulating about them.

Testing: Once you think you have the final design ready, force yourself to turn-off your brain and run a couple of inputs through the algorithm. Follow exact steps there and do not use your brain. Basically, be the computer and test. Test. Test again. You will surely uncover a few things here that you might have missed (e.g. <= instead of ==).


Regularly check in to see if the interviewer is on the same page as you: This is a very important step and needs to be done through out the interview. You need to keep the interviewer engaged and focused. Speak clearly, slowly, think aloud and make sure interviewer is following you.


Hopefully this will help you answer algorithmic programming interview questions in a much better cleaner way using a templated approach.

In the next post we will look at such a templated approach to solving design interview questions.

Monday, October 14, 2013

Linked List create, insert, sort, min, max, find and print complete program

I have been asked a few times to provide a complete working example of different operations on Linked List.

This post is an attempt to do just that.

#include <stdio.h>
#include <stdlib.h> // for malloc

// define your linked list struct
// make sure to typedef to give better name
typedef struct ll
{
int data;
struct ll *next;
} LinkedListNode;


// remember to declare functions that will be used in main but defined after it
LinkedListNode* AddElementsToBeginningOfList(int inputSize, int input[], LinkedListNode *head);
LinkedListNode* AddElementsToEndOfList(int inputSize, int input[], LinkedListNode *head);

LinkedListNode* InsertElementAtBeginning(int input, LinkedListNode *head);
LinkedListNode* InsertElementAtEnd(int input, LinkedListNode *head);

void Sort(LinkedListNode *head);
LinkedListNode* Find(int value, LinkedListNode *head);
void MaxMinInList(int *max, int *min, LinkedListNode *head);

void PrintLinkedList(char* name, LinkedListNode *node);

int main(int argc, char* argv[])
{
// define head node
LinkedListNode* head = NULL;
LinkedListNode* inOrderList = NULL;
int max = 0, min = 0;
LinkedListNode* ele = NULL;

// de fine array
int input[] = { 43, 22, 11, 4, 22, 56, 47, 21, 78, 99, 97 };

// find the array length in C
int inputSize = sizeof(input) / sizeof(input[0]);

// add elements to the beginning of the list
head = AddElementsToBeginningOfList(inputSize, input, head);

// Print list
PrintLinkedList("AddElementsToBeginningOfList", head);

// add elements to the end of the list
inOrderList = AddElementsToEndOfList(inputSize, input, inOrderList);

// Print list
PrintLinkedList("AddElementsToEndOfList", inOrderList);

// insert a single element at beginning
inOrderList = InsertElementAtBeginning(100, inOrderList);

// Print list
PrintLinkedList("InsertElementAtBeginning", inOrderList);

// insert a single element at end
inOrderList = InsertElementAtEnd(200, inOrderList);

// Print list
PrintLinkedList("InsertElementAtEnd", inOrderList);

// Search for an element
ele = Find(56, inOrderList);

if (ele != NULL) {
printf("\r\nElement found: %d", ele->data);
}

// Find max and min in the list
MaxMinInList(&max, &min, inOrderList);

printf("\r\nMax: %d, Min: %d", max, min);

// sort the list
Sort(inOrderList);

// Print list
PrintLinkedList("Sorted list", inOrderList);

return 0;
}

LinkedListNode* AddElementsToBeginningOfList(int inputSize, int input[], LinkedListNode *head)
{
// define loop variable
int i = 0;

// add elements to the linked list
for (i = 0; i < inputSize; i++)
{
// create a temp node
LinkedListNode *temp = (LinkedListNode *)malloc(sizeof(LinkedListNode));
temp->data = input[i];

// add to the beginning of the list
// make this new node point to the current beginning of the list
temp->next = head;
// NOW set the head of the list to this new node
head = temp;
}

return head;
}

LinkedListNode* AddElementsToEndOfList(int inputSize, int input[], LinkedListNode *head)
{
// define loop variable
int i = 0;

// define the end of the list
// here is where new elements will be inserted
LinkedListNode* end = NULL;

// add elements to the linked list
for (i = 0; i < inputSize; i++)
{
// create a temp node
LinkedListNode *temp = (LinkedListNode *)malloc(sizeof(LinkedListNode));
temp->data = input[i];
// make sure to null the pointer to the last element
temp->next = NULL;

// if this is the first element, make head point to it
if (i == 0) {
head = temp;
}
else {
// add to the end of the list
end->next = temp;
}

// NOW set the end of the list to this new node
end = temp;
}

return head;

}

LinkedListNode* InsertElementAtBeginning(int input, LinkedListNode *head)
{
// create a temp node
LinkedListNode *temp = (LinkedListNode *)malloc(sizeof(LinkedListNode));
temp->data = input;

// add to the beginning of the list
// make this new node point to the current beginning of the list
temp->next = head;
// NOW set the head of the list to this new node
head = temp;

return head;
}

LinkedListNode* InsertElementAtEnd(int input, LinkedListNode *head)
{
LinkedListNode* curr = head;

// create a temp node
LinkedListNode *temp = (LinkedListNode *)malloc(sizeof(LinkedListNode));
temp->data = input;
temp->next = NULL;


while (curr->next != NULL)
curr = curr->next;

// add it as the last node
curr->next = temp;

return head;
}

void Sort(LinkedListNode *head)
{
LinkedListNode *list = NULL;
LinkedListNode *pass = NULL;

// traverse the entire list
for (list = head; list->next != NULL; list = list->next)
{
// compare to the list ahead
for (pass = list->next; pass != NULL; pass = pass->next)
{
// compare and swap
if (list->data > pass->data)
{
// swap
int temp = list->data;
list->data = pass->data;
pass->data = temp;
}
}
}
}

// finds the first node with the specified value
LinkedListNode* Find(int value, LinkedListNode *head)
{
// start at the root
LinkedListNode *currentNode = head;

// loop through the entire list
while (currentNode != NULL)
{
// if we have a match
if (currentNode->data == value)
return currentNode;
else // move to the next element
currentNode = currentNode->next;
}
}

// finds the maximum and minimum in the list
void MaxMinInList(int *max, int *min, LinkedListNode *head)
{
// start at the root
LinkedListNode *curr = head;

if (curr == NULL)
return 0; // list is empty

// initialize the max and min values to the first node
*max = *min = curr->data;

// loop through the list
for (curr = curr->next; curr != NULL; curr = curr->next)
{
if (curr->data > *max)
*max = curr->data;
else if (curr->data < *min)
*min = curr->data;
}
}

// print the list
void PrintLinkedList(char* name, LinkedListNode *node)
{
printf("\r\nName: %s", name);
printf("\r\n HEAD");

// loop while we have data
while (node != NULL)
{
printf(" -> %d", node->data);
// move to next node
node = node->next;
}
}

Friday, April 19, 2013

LINQ design interview question

LINQ is one of my favorite interview topics for many reasons. It allows me to check a candidate's ability to define logic, use SQL skills, showcase lambda expressions, distill the problem into easy steps and of course, see some code in action. Also, since LINQ code is generally quite compact, it is perfect for white boarding.

I was recently helping a friend solve a problem and felt that it could be modified to an excellent interview question. It has a bit of algorithm design, heavy use of simple to medium complex LINQ and is not more that 10-15 lines of actual code. Perfect.

Question: Given a class of students  (ID, Name, Grades) with results for at least 2 semesters, find the students whose Grades have significantly improved in the second semester as compared to the first. Bonus: List out top 25% of the students.

As the first step, let us define the rough algorithm for solving this problem:

  1. Load Student data for first semester
  2. Load student data for second semester
  3. Merge Grades for each student from second semester into the first semester result set
  4. For each student, calculate the grade difference for students whose grades have improved (you do not want to waste computational cycles for students whose grades have fallen)
  5. For each student from 4 above, calculate the grade improvement percent
  6. Sort the result by descending order of grade improvement percent
  7. Select rows that constitute top 25% of students

Now, let’s define our POCO class that will represent the student data structure. The data structure has two parts: basic student properties (Id, Name, Grade) and some computational properties that we will use to solve our algorithm.

    public class StudentRecord
{
// basic properties
public int Id { get; set; }
public string Name { get; set; }
public double Grade { get; set; }

// computational properties
public double GradeSecondSemester { get; set; }
public double GradeDifference { get; set; }
public double GradeDifferencePercent { get; set; }
}

In addition to a Student record, we also need to a collection to hold these records. Let’s call it ClassReport:

    public class ClassReport
{
// some basic properties of a class
public int Id { get; set; }
public string Name { get; set; }
public int SemesterId { get; set; }

// list of student records for that semester
public List<StudentRecord> Students;

public ClassReport()
{
Students =
new List<StudentRecord>();
}
}

For steps 1 and 2 of the algorithm, I am not going to focus on this blog. We will assume that we have a way to load this data (stubbed in our code sample below).


Step 1 and 2: Load Student data:


We will mock up our student data in this example. In real life, this data probably will come from a database or a web service call, etc. As you can see below, we generate some dummy student data for two different semesters. Note that the student Id’s need to match across (not really a hard requirement, but good to have for our demo purposes).

        static void Main(string[] args)
{
// Step 1 and 2: Load Student data
ClassReport cr1 = MockSem1();
ClassReport cr2 = MockSem2();

}

private static ClassReport MockSem1()
{
ClassReport cr = new ClassReport()
{
Id = 1,
Name =
"First Grade",
SemesterId = 1
};

// add dummy data - in real life, this comes from database
cr.Students.Add(
new StudentRecord()
{
Id = 1,
Name =
"Ray",
Grade = 90
});
cr.Students.Add(
new StudentRecord()
{
Id = 2,
Name =
"Jack",
Grade = 80
});
cr.Students.Add(
new StudentRecord()
{
Id = 3,
Name =
"John",
Grade = 60
});
cr.Students.Add(
new StudentRecord()
{
Id = 4,
Name =
"Lisa",
Grade = 67
});
cr.Students.Add(
new StudentRecord()
{
Id = 5,
Name =
"Jill",
Grade = 94
});
return cr;
}

private static ClassReport MockSem2()
{
ClassReport cr = new ClassReport()
{
Id = 1,
Name =
"First Grade",
SemesterId = 2
};

// add dummy data - in real life, this comes from database
cr.Students.Add(
new StudentRecord()
{
Id = 1,
Name =
"Ray",
Grade = 85
});
cr.Students.Add(
new StudentRecord()
{
Id = 2,
Name =
"Jack",
Grade = 95
});
cr.Students.Add(
new StudentRecord()
{
Id = 3,
Name =
"John",
Grade = 82
});
cr.Students.Add(
new StudentRecord()
{
Id = 4,
Name =
"Lisa",
Grade = 95
});
cr.Students.Add(
new StudentRecord()
{
Id = 5,
Name =
"Jill",
Grade = 96
});
return cr;
}

Step 3: Merge Grades for each student from second semester into the first semester result set


Here is where our LINQ fun starts. In one of my previous post, I had explained how LINQ join works. Now would be a good time to review bunch of LINQ related interview questions and answers from the past.

            // step 3: Merge Grades for each student from second 
// semester into the first semester result set

// in-line merge; no need for a separate assignment
// for each student in first semester report
(from firstSem in cr1.Students
// join to second sem class report
join secondSem in cr2.Students
// on student id as the key
on firstSem.Id equals secondSem.Id
// assign second sem grade to first (via a select; clever)
select firstSem.GradeSecondSemester = secondSem.Grade)
// execute
.ToList();
As you can see from the LINQ statement, we join first semester class report with second semester and then do an in-line update of the first semester data structure’s GradeSecondSemester property.

Step 4: Calculate grade difference for students whose grades have improved


Now we need to process those students whose grades improved in the 2nd semester and update our data structure to reflect the difference:

// step 4: For each student, calculate the grade difference 
// for students whose grades have improved (you do not want
// to waste computational cycles for students whose grades have fallen)
cr1.Students
// find all students who did better in 2nd semester
.FindAll(s => ((s.GradeSecondSemester - s.Grade) > 0))
// and update their grade difference property
.ForEach(x => x.GradeDifference = (x.GradeSecondSemester - x.Grade));

We use FindAll to find those students who have better grades and then loop for those records using ForEach to update the GradeDifference property.


Step 5: For each student from 4 above, calculate the grade improvement percent

// Step 5: For each student from 4 above, 
// calculate the grade improvement percent

// 5.a. calculate total difference sum
double gradeDiffSum = cr1.Students.Sum(x => x.GradeDifference);

// 5.b. calculate percent for each row
cr1.Students
// find all students who did better in 2nd semester
.FindAll(s => ((s.GradeSecondSemester - s.Grade) > 0))
// update grade percent
.ForEach(x => x.GradeDifferencePercent =
((x.GradeDifference * 100) / gradeDiffSum));

Step 6: Sort the result by descending order of grade improvement percent


By this point, I am hoping you get the gist of how to approach such LINQ queries. Sorting a list is a relatively simple operation as shown below:

    // Step 6: Sort the result by descending order of grade improvement percent
var sortedList =
(
from entry in cr1.Students
orderby entry.GradeDifferencePercent descending
select
entry)
.ToList();
Step 7: Select rows that constitute top 25% of students

We use TakeWhile here to keep selecting students till we reach our number.

    //Step 7: Select rows that constitute top 25% of students
// 7.a. - sum of grade diff percent
double gradeDiffPercentSum = 0;

// 7.b - take as many rows as needed till we reach the 25% sum
var bestStudents =
sortedList
.TakeWhile(x =>
((gradeDiffPercentSum += x.GradeDifferencePercent) <= 25.0))
.ToList();

// 7.c account for the case where the first element is more than 25
if (bestStudents.Count() == 0)
bestStudents = sortedList.Take(1).ToList();
That’s it. You now have a list of top 25% students whose grades have improved in the second semester.

I am pasting the complete program here in case you like to see the whole thing in one shot.


The complete solution:

using System.Linq;

namespace
StudentReport
{
class
Program
{
static void Main(string
[] args)
{
// Step 1 and 2: Load Student data
ClassReport
cr1 = MockSem1();
ClassReport
cr2 = MockSem2();

// step 3: Merge Grades for each student from second
// semester into the first semester result set

// in-line merge; no need for a separate assignment
// for each student in first sem report
(from firstSem in
cr1.Students
// join to second sem class report
join secondSem in
cr2.Students
// on student id as the key
on firstSem.Id equals
secondSem.Id
// assign second sem grade to first (via a select; clever)
select
firstSem.GradeSecondSemester = secondSem.Grade)
// execute
.ToList();

// step 4: For each student, calculate the grade difference
// for students whose grades have improved (you do not want
// to waste computational cycles for students whose grades have fallen)
cr1.Students
// find all students who did better in 2nd semester
.FindAll(s => ((s.GradeSecondSemester - s.Grade) > 0))
// and update their grade difference property
.ForEach(x => x.GradeDifference = (x.GradeSecondSemester - x.Grade));

// Step 5: For each student from 4 above,
// calculate the grade improvement percent

// 5.a. calculate total difference sum
double
gradeDiffSum = cr1.Students.Sum(x => x.GradeDifference);

// 5.b. calculate percent for each row
cr1.Students
// find all students who did better in 2nd semester
.FindAll(s => ((s.GradeSecondSemester - s.Grade) > 0))
// update grade percent
.ForEach(x => x.GradeDifferencePercent =
((x.GradeDifference * 100) / gradeDiffSum));


// Step 6: Sort the result by descending order of grade improvement percent
var
sortedList =
(
from entry in
cr1.Students
orderby entry.GradeDifferencePercent
descending
select
entry)
.ToList();


//Step 7: Select rows that constitute top 25% of students
// 7.a. - sum of grade diff percent
double
gradeDiffPercentSum = 0;

// 7.b - take as many rows as needed till we reach the 25% sum
var
bestStudents =
sortedList
.TakeWhile(x =>
((gradeDiffPercentSum += x.GradeDifferencePercent) <= 25.0))
.ToList();

// 7.c account for the case where the first element is more than 25
if
(bestStudents.Count() == 0)
bestStudents = sortedList.Take(1).ToList();

// do something with this result set
}

private static ClassReport
MockSem1()
{
ClassReport cr = new ClassReport
()
{
Id = 1,
Name =
"First Grade"
,
SemesterId = 1
};

// add dummy data - in real life, this comes from database
cr.Students.Add(
new StudentRecord
()
{
Id = 1,
Name =
"Ray"
,
Grade = 90
});
cr.Students.Add(
new StudentRecord
()
{
Id = 2,
Name =
"Jack"
,
Grade = 80
});
cr.Students.Add(
new StudentRecord
()
{
Id = 3,
Name =
"John"
,
Grade = 60
});
cr.Students.Add(
new StudentRecord
()
{
Id = 4,
Name =
"Lisa"
,
Grade = 67
});
cr.Students.Add(
new StudentRecord
()
{
Id = 5,
Name =
"Jill"
,
Grade = 94
});
return
cr;
}

private static ClassReport
MockSem2()
{
ClassReport cr = new ClassReport
()
{
Id = 1,
Name =
"First Grade"
,
SemesterId = 2
};

// add dummy data - in real life, this comes from database
cr.Students.Add(
new StudentRecord
()
{
Id = 1,
Name =
"Ray"
,
Grade = 85
});
cr.Students.Add(
new StudentRecord
()
{
Id = 2,
Name =
"Jack"
,
Grade = 85
});
cr.Students.Add(
new StudentRecord
()
{
Id = 3,
Name =
"John"
,
Grade = 70
});
cr.Students.Add(
new StudentRecord
()
{
Id = 4,
Name =
"Lisa"
,
Grade = 82
});
cr.Students.Add(
new StudentRecord
()
{
Id = 5,
Name =
"Jill"
,
Grade = 96
});
return
cr;
}
}
}

Friday, March 22, 2013

Entity Framework Interview Question - Explain ENUM usage in EF5

Entity Framework 5 introduced support for Enum’s amongst other new features. This was a long awaited feature by the community. In this post, we will explore how enums work with entity framework, how can you code them, how to use them, how they are represented in the database, etc.

Question: Write code to demonstrate ENUM usage in Entity Framework.

For this exercise, please make sure that your project references at least version 5 of Entity Framework. You can use NuGet to add that to your project.

Model

In Entity Framework, an enumeration can have the following underlying types: Byte, Int16, Int32, Int64 , or SByte. I chose 3 different enum types to highlight the differences in database representation for different enum types.
    public enum DefaultEnum
    {
        Red = 1,
        Blue = 2,
        Green = 3
    }

    public enum ByteEnum : byte
    {
        RedByte,
        BlueByte,
        GreenByte
    }

    public enum ShortEnum : short
    {
        RedShort,
        BlueShort,
        GreenShort
    }

Now, let’s define our Account class that uses these enums.
    public class EnumDemoAccount
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public string Name { get; set; }

        // our enum fields
        public DefaultEnum DefaultEnum { get; set; }
        public ByteEnum ByteEnum { get; set; }
        public ShortEnum ShortEnum { get; set; }
    }

As you can note, I am generating the Identity column as auto-incrementing (bonus).

Context


Our Context definition is pretty standard for this example. All I am telling the system is to create a database with the name I have given (instead of it choosing one based on namespace + class.
    public class AccountContext : DbContext
    {
        public DbSet<EnumDemoAccount> EnumDemoAccounts { get; set; }

        public AccountContext() : base("EFDemoDb")
        {}
    }

I also like to like to create a context initializer for test projects so that I always have a clean database for each run:
    public class DatabaseContextInitializer : 
        DropCreateDatabaseAlways<AccountContext>
    {
        protected override void Seed(AccountContext dbContext)
        {
            base.Seed(dbContext);
        }
    }

Test code


The test code is simple as well. We first set our initialization strategy, create a context, add a few records and see how things look.
        static void Main(string[] args)
        {
            // initialization 
            Database.SetInitializer<AccountContext>
                (new DatabaseContextInitializer());

            // create a new context
            AccountContext ac = new AccountContext();

            // add a couple of test entries
            ac.EnumDemoAccounts.Add(
                new EnumDemoAccount()
                {
                    Name = "First",
                    DefaultEnum = DefaultEnum.Blue,
                    ByteEnum = ByteEnum.GreenByte,
                    ShortEnum = ShortEnum.RedShort
                });

            ac.EnumDemoAccounts.Add(
                new EnumDemoAccount()
                {
                    Name = "Second",
                    DefaultEnum = DefaultEnum.Green,
                    ByteEnum = ByteEnum.RedByte,
                    ShortEnum = ShortEnum.BlueShort
                });

            // save to db
            ac.SaveChanges();

            // display
            foreach (var account in ac.EnumDemoAccounts)
            {
                Console.WriteLine(
                    "Name: {0}\n\tDefaultEnum:{1}\n\tByteEnum:{2} \n\tShortEnum: {3}",
                    account.Name,
                    account.DefaultEnum,
                    account.ByteEnum,
                    account.ShortEnum);
            }

            Console.ReadKey();
        }

Output


Now let’s review the output we got:

EnumDemo-output

As you can see, the enum values survived the round trip to the database. Wonderful! And they are all strongly typed so we can use them in our regular programming.

Database


Understanding what happened on the database side is equally important. Remember, we created 3 different enum types in our model – int, short and byte. Does the data type of the enum make any difference in the storage scheme? You bet! Let’s see how Entity Framework internally represents these types to the database:

EnumDemo-db-table

Note that the default enum data type (int) remained as int. Byte was translated as tinyint and Short as smallint. So, if you are using enums, think hard about your use cases and choose the appropriate datatype for your enums.

And finally, to round things up, let’s see what data values are stored in our database:

EnumDemo-db-query-output

No surprises here. Auto generated identity column, proper values for our enums. Things look good. Hoping that this post has given you a good starting point about enums in Entity Framework 5.