c# Method not working as expected Address book project - c#

I'm new to c# and working on my first project - a console app. I am having some trouble understanding why my code will not return false when an entry in the address book already exists. The following methods are all part of the AddressBook class CheckEntry(), AddEntry(), RemoveEntry().
Ok, so the boolean method CheckEntry() is used by two other methods - AddEntry() and RemoveEntry() - both are looking to verify if the user entry exists before performing their respective duties. In AddEntry() it is supposed to see if the contact already exists before adding another contact and shouldn't create the contact if it does (but it's adding duplicates). RemoveEntry() is supposed to check if it exists and uses the updated value of the stored variable in CheckEntry() to remove the current contact (but does nothing).
I know I'm probably either missing something simple or have way over thought the whole process. My assumption is that checkEntry() is not working correctly since both of the functions tied to it are faulting. Anyone have any ideas?? Let me know if I need to explain anything further.
NOTE: I know that using a list would be more efficient/easier to work with. It was my goal to use an array for learning purposes. Switching from learning Javascript to C# has been a bit of a challenge and I want to make sure I'm learning each thing before moving onto the next.
Here is all of my code. Each class is separated by "//--------" thank you in advance for your help.
namespace AddressBook {
class Contact {
public string Name;
public string Address;
public Contact(string name, string address) {
Name = name;
Address = address;
}
}
}
//------------------------------------------------------------------------
using System;
namespace AddressBook {
class AddressBook {
public readonly Contact[] contacts;
public AddressBook() {
contacts = new Contact[2]; ;
}
public void AddEntry(string name, string address) {
Contact AddContact = new Contact(name, address);
if (CheckEntry(name)) {
for (int i = 0; i < contacts.Length; i++) {
if (contacts[i] == null) {
contacts[i] = AddContact;
Console.WriteLine("Address Book updated. {0} has been added!", name);
break;
}
}
}
}
private string existingContact = "";
private bool CheckEntry(string name) {
foreach(Contact contact in contacts) {
if (contact == null) {
break;
}
else if (contact != null && contact.ToString() != name) {
continue;
}
else if (contact.ToString() == name) {
existingContact = contact.ToString();
return false;
}
}
return true;
}
public void RemoveEntry(string name) {
if( !(CheckEntry(name)) ) {
existingContact = null;
Console.WriteLine("{0} removed from contacts", name);
}
}
public string View() {
string contactList = "";
foreach(Contact contact in contacts) {
if(contact == null) {
break;
}
contactList += String.Format("Name: {0} -- Address: {1}" + Environment.NewLine, contact.Name, contact.Address);
}
return contactList;
}
}
}
//------------------------------------------------------------------------
using System;
namespace AddressBook {
class Program {
static void Main(string[] args) {
AddressBook addressBook = new AddressBook();
PromptUser();
void Menu() {
Console.WriteLine("TYPE:");
Console.WriteLine("'Add' to add a contact: ");
Console.WriteLine("'Remove' to select and remove a contact: ");
Console.WriteLine("'Quit' to exit: ");
}
void UpdateAddressBook(string userInput) {
string name = "";
string address = "";
switch ( userInput.ToLower() ) {
case "add":
Console.Write("Enter a name: ");
name = Console.ReadLine();
Console.Write("Enter an address: ");
address = Console.ReadLine();
addressBook.AddEntry(name, address);
break;
case "remove":
Console.Write("Enter a name to remove: ");
name = Console.ReadLine();
addressBook.RemoveEntry(name);
break;
case "view":
Console.WriteLine(addressBook.View());
break;
}
}
void PromptUser() {
Menu();
string userInput = "";
while (userInput != "quit") {
Console.WriteLine("What would you like to do?");
userInput = Console.ReadLine();
UpdateAddressBook(userInput);
}
}
}
}
}
Here is what I came up with - tested changes
Now I can't add duplicate names and I can remove entries.
public void AddEntry(string name, string address) {
Contact AddContact = new Contact(); //changed
AddContact.Name = name; //changed
AddContact.Address = address; //changed
if (CheckEntry(name)) {
for(int i = 0; i < contacts.Length; i++) {
if (contacts[i] == null) {
contacts[i] = AddContact;
Console.WriteLine("Address Book updated. {0} has been added!", name);
break;
}
}
}
}
//changed - removed variable and all instances of...
private bool CheckEntry(string name) {
foreach(Contact contact in contacts) {
if (contact == null) {
break;
}
else if (contact != null && contact.Name != name) {
continue;
}
else if (contact.Name == name) {
return false;
}
}
return true;
}
//changed - instead of passing checkentry() as a check I just took care of it here
public void RemoveEntry(string name) {
for(int i = 0; i < contacts.Length; i++) {
if(contacts[i].Name == name) {
contacts[i] = null;
break;
}
}
Console.WriteLine("{0} removed from contacts", name);
}

In CheckEntry you are testing an object of type Contact with your param of type string. This cannot work as you are testing 2 different types.
It could work the way you wrote it if you override the ToString method in your Contact class (so that it gives the attribute contact.name).
You can modify your code like this (add using System.Linq):
private bool CheckEntry(string name)
{
return contacts.Any(a => a.Name == name);
}

First of, I am assuming your strategy is to find the first empty slot in the array and insert a new Contact in it for AddEntry. To remove an entry, you want to simply mark that array location as empty. As you realize, this means that the array does not grow dynamically with the request i.e. you can have an ArrayFull situation that you need to handle. Also, you are doing linear search a.k.a. scan of the array - I assume you don't want to focus on that aspect in this sample.
Below are my comments for your existing code:
Shouldn't AddEntry update the Address if Address is different even though the Name matches?
Also returning a bool to indicate if the address was added/updated (true) versus nothing was done (false)
You should also handle the 'ArrayFull` condition
I do not understand why you need the variable existingContact
Don't use functions named CheckXXX if you plan to return bool. ContainxXXX is better understood method name to use with a bool return value.
You should not break from your foreach loop in CheckEntry. What if 2 entries were added, then the first one removed and then request to add the second one comes again? You would just break out since first entry is null
You seem to have 3 different ifs to check in your foreach where only one suffices.
Below is my relevant code with comments. I tried to keep them similar to what you have. You can use LINQ in multiple places instead of looping.
class Contact {
public string Name { get; private set; } // use a property with a private setter, instead of a public member
public string Address { get; private set; } // use a property with a private setter, instead of a public member
public Contact(string name, string address) {
Name = name;
Address = address;
}
}
//------------------------------------------------------------------------
class AddressBook {
public readonly Contact[] contacts;
public AddressBook() {
contacts = new Contact[2]; // I am assuming you kept the size 2 for testing
}
public bool AddEntry(string name, string address) {
if (!ContainsEntry(name)) {
Contact AddContact = new Contact(name, address);
for (int i = 0; i < contacts.Length; i++) {
if (contacts[i] == null) {
contacts[i] = AddContact;
Console.WriteLine("Address Book updated. {0} has been added!", name);
return true;
}
}
Console.WriteLine($"Cannot add name ({name}) to Address Book since it is full!");
// TODO: Throw some exception or specific return values to indicate the same to the caller
} else {
Console.WriteLine($"Name ({name}) already exists in Address Book!");
// TODO: Update the address?
}
return false;
}
private int GetEntryIndex(string name) {
for (int i = 0; i < contacts.Length; i++) {
if (contacts[i] != null && contacts[i].Name == name)
return i;
}
return -1;
}
private bool ContainsEntry(string name) {
return GetEntryIndex(name) != -1;
}
public void RemoveEntry(string name) {
var index = GetEntryIndex(name);
if (index != -1) {
contacts[index] = null;
Console.WriteLine("{0} removed from contacts", name);
}
}
public string View() {
string contactList = "";
foreach (Contact contact in contacts) {
if (contact == null) {
continue; // Don't break, but simply continue to look further
}
contactList += String.Format("Name: {0} -- Address: {1}" + Environment.NewLine, contact.Name, contact.Address);
}
return contactList;
}
}
EDIT: Answering some of the questions the OP has
Q: What should I do with the return value of AddEntry
A: Right now, you are writing code for practice, but once you start writing industry standard code, you will soon realize that you wouldn't know who call's your function. Never assume (unless the method is private) that only you will call this function. Currently, you don't need to do anything with this return value, but time might come when you want to know if your call did indeed modify some values or just returned without changes. It is for that time. It is a pretty standard practice. Some folks even return an enum {NoChange, Added, Updated, Deleted} instead of a bool
Q: How to reduce the CheckEntry function.
A:
Below I have written your function with a slightly different formatting
private bool CheckEntry(string name) {
foreach (Contact contact in contacts) {
if (contact == null) { // First if
continue;
} else {
if (contact != null && contact.Name != name) { // Second if
continue;
} else {
if (contact.Name == name) { // Third if
return false;
}
}
}
}
return true;
}
For the first if statement, the else part is redudant. The second if statement will hit only when contact is not null due to the continue statement, cancelling the need for the else keyword.
Since contact is not null when we hit the second if statement, the check contact != null is pretty redundant in the second if. You can reduce the if statement's as below
if (contact.Name != name) { // Second if
continue;
} else {
if (contact.Name == name) { // Third if
return false;
}
}
Similarly you will notice that the the third if will hit only when contact.Name is the same as name (otherwise it would have continued). So there is no need to check again. This will reduce our checks as below
if (contact == null)
continue;
if (contact.Name != name)
continue;
else
return false;
This can be further reduced by combining the conditions in the two if statements as below
if (contact == null || contact.Name != name)
continue;
else
return false;
Which is the same as (negating the condition)
if (contact != null && contact.Name == name)
return false;
So your function will look like
private bool CheckEntry(string name) {
foreach (Contact contact in contacts)
if (contact != null && contact.Name == name)
return false;
return true;
}
Hope you follow the deductions here
Q: I may have misunderstood from the course but when you write the properties in a class with getters and setters, which I did change with my updated version, I was under the impression that it wasn't necessary, even redundant, to create a Constructor - that the class (not sure if this is correct terminology) has a default constructor built in for cases where you would want to add the property.
A: Correct. Just different ways of doing it.
Q: I like what you did with GetEntryIndex() and ContainsEntry() - I'm curious, is GetEntryIndex() not the same as writing your own Array.IndexOf(), though? Someone, either me or a method, has to scan the array. Both versions are linear so either way, this is O(n), correct? (just getting into some theory so please correct me if I'm wrong). So, just for my understanding, this is the same as: return Array.IndexOf(contacts, name); this returns -1 if it doesn't exist or whatever the index is(I'm assuming)
A: It is not the same, but similar in essence. IndexOf has a set of rules to figure out if given two objects are equal or not. You haven't defined these rules on your custom object, hence it will always return -1. There are simple LINQ statements to do these, but I would let you explore that on your own.

First, thank you for taking the time to make such a thorough response. Second, I should probably note that I am teaching myself/taking a course on Treehouse and I'm on my second week/second section of the course - just to give you an idea of where I'm coming from. That being said, I want to walk through what you have given me and where I was heading with this project so I can learn.
I agree the user should be able to update and it was a feature that I had considered but had not quite gotten there.
if returning a bool when added/updated would you then remove the update string and place it with the caller in main()? This way it only prints that it's been updated if return true else print that the contact was unchanged. It kind of feels like it might be advantageous to make a class that is suitable just checking values - which would also make my code more reusable?? If I was going to use it in Main() maybe something like this would work..
if(!addEntry()) {Console.WriteLine("Contact {0} was not updated", name);} //do something
else{Console.WriteLine("Address Book updated. {0} has been added!", name);}
Furthermore, if the user is just updating the contact it could print that it's been updated. So the Console output could be a ternary operation - if new name print contact added otherwise contact updated??
I agree and had run into the situation where nothing was done because the array was full and that was something I had planned on working on.
I absolutely agree about the existing contact variable. I was so stuck when it came to what to do that was the best I could come up with. I should have walked away for a bit and thought about it more. I don't know if you saw my updated portion, but I was able to eliminate it.
Contains for a bool method seems logical, I will be sure to follow this rule.
I had not considered this in such terms before you said that. It makes perfect sense though. If the first entry is null and the second contains the name that the user is trying to enter then you would end up with a duplicate since I was breaking from the loop at the first instance of null instead of making sure the name didn't exist in the entire array, first.
I'm not sure, without your methods, how I would have been able to eliminate my three conditionals. I see what you did to make the one work, but am I possibly missing something? Or, was that more of a reference saying, ok here's a better way - use these methods to check and then eliminate the if/else chain, thus making your code more concise.
for your code:
I may have misunderstood from the course but when you write the properties in a class with getters and setters, which I did change with my updated version, I was under the impression that it wasn't necessary, even redundant, to create a Constructor - that the class (not sure if this is correct terminology) has a default constructor built in for cases where you would want to add the property.
Correct, I did set the size to 2 for testing purposes.
I like what you did with GetEntryIndex() and ContainsEntry() - I'm curious, is GetEntryIndex() not the same as writing your own Array.IndexOf(), though? Someone, either me or a method, has to scan the array. Both versions are linear so either way, this is O(n), correct? (just getting into some theory so please correct me if I'm wrong). So, just for my understanding, this is the same as:
return Array.IndexOf(contacts, name);
this returns -1 if it doesn't exist or whatever the index is(I'm assuming)
That's a good idea with the continue in View(). That will make sure to print out any contact with an index higher than an index with a null value. By some magic, this was working in this way with the break(it would print out index 1 even if index 0 was empty), but I do realize why it shouldn't and how using continue is better.

Related

How To Display All Desired Elements From A List<Object> To The Console (C#)

So I'm making a menu the user can use to view movies, add movies, and delete movies. Right now I have the menu completed to the point where if the user input is the number 1 it should display the movie title, year, director, and summary of that movie in another screen. I have a foreach loop that I'm using the display that particular movie's title, year, director, and summary but when I run my program it only shows the title and year and that's it. How can I show all 4 of those at once in the console? Code is below.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace MovieLibrary
{
// Should contain data: an address and list of movie objects.
// MUST include a dynamic 'MENU' of MOVIES
public class Library
{
// Fields
private string _directory = "../../output/";
private string _file = "Movies.txt";
private List<Movie> _movies;
public Library()
{
_movies = new List<Movie>();
Load();
bool menuRunning = true;
while (menuRunning)
{
Console.WriteLine("Pick a number. Any number.");
string userOption = Console.ReadLine();
while (string.IsNullOrWhiteSpace(userOption))
{
Console.WriteLine("Please do not leave this blank.");
Console.WriteLine("Pick a number. Any number.");
userOption = Console.ReadLine();
}
if (userOption == "1")
{
string montyTitle;
int montyYear;
string montyDirector;
string montySummary;
foreach (Movie movie in _movies)
{
if (movie.Title == "Monty Python and the Holy Grail")
{
montyTitle = movie.Title;
Console.WriteLine("Title: {0}", montyTitle);
}
if (movie.Year == 1975)
{
montyYear = movie.Year;
Console.WriteLine("Year: {0}", montyYear);
}
if (movie.Director == "Terry Gilliam & Terry Jones")
{
montyDirector = movie.Director;
Console.WriteLine("Director: {0}", montyDirector);
}
if (movie.Summary == "Monty Python and the Holy Grail is about a ragtag group, the knights of the round table, assembled by the Great King Arthur to embark on a quest given by God to find the Holy Grail.")
{
montySummary = movie.Summary;
Console.WriteLine("Summary: {0}\r\n", montySummary);
}
}
}
else if (userOption == "2")
{
// RE
}
else if (userOption == "3")
{
// Alien
}
else if (userOption == "4")
{
// DP
}
else if (userOption == "5")
{
// The Avengers
}
else if (userOption == "6")
{
// Zombieland
}
else if (userOption == "7")
{
// BTLC
}
else if (userOption == "8")
{
// The Thing
}
else if (userOption == "9")
{
// CITW
}
}
}
//Loads the text file
private void Load()
{
using (StreamReader sr = new StreamReader(_directory + _file))
{
string text;
while ((text = sr.ReadLine()) != null)
{
string[] contents = text.Split(':');
Movie newLibrary = new Movie(contents[0], int.Parse(contents[1]), contents[2], contents[3]);
_movies.Add(newLibrary);
}
}
}
// Allows the user to view the list of movies
private void View()
{
Console.Clear();
foreach (Movie movie in _movies)
{
Console.WriteLine($"{movie.Title,-10}{"\n" + movie.Year,-10}{"\n" + movie.Director,-10}{"\n" + movie.Summary + "\r\n",-10}");
}
}
}
// Allows the user to add any new movies they would like to the list of movies in the text file
/*public static void Add()
{
}
// Allows the user to remove any movies they would like from the text file
public static void Remove()
{
}*/
}
I do not really understand your question.
It might be that the if (movie.Director == "Terry Gilliam & Terry Jones") is not going inside the if.
Maybe I can propose something different(?)
You do not need the foreach loop to iterate the list.
You can get items from your _movies in 2 different ways:
Convert to array: var _myMoviesArray = _movies.ToArray(); and access them by an index (Console.WriteLine(_myMoviesArray[userOption].Title));
Or use Console.WriteLine(_movies.IndexAt(userOption).Title).
For printing in the console, instead of:
Console.WriteLine("Title: {0}", montyTitle);
you can do:
Console.WriteLine($"Title: {movie.Title}");
And I think that you do not need to check the if(movie.Title == "Monty Python and the Holy Grail"). If you access by the userOption index, you can get directly to the Movie in your array.
I'm not sure which part is bothering you exactly, but I was bored and decided to do a simple PoC of segregated code for you. I've defined a simple repository to manipulate your entity data and created a specific implementation of it targeting movies. I've implemented in-memory storage of your movies which could have any underlying implementation, such as text file or you could keep it in memory and store in the library itself once you finish modifying in-memory state. You can expand it, modify it, or discard it entirely if you don't like it.
Repository interface definition
namespace MovieLibrary
{
using System;
using System.Collections.Generic;
internal interface IRepository<T, K>
{
IEnumerable<T> Get();
IEnumerable<T> Get(Func<T, bool> condition);
T Get(K guid);
void Add(T entity);
void Remove(T guid);
}
}
Movie repository implementation
namespace MovieLibrary
{
using System;
using System.Collections.Generic;
using System.Linq;
internal class MovieRepository
: IRepository<Movie, Guid>
{
// In-memory storage, can be replaced with anything else.
private readonly ISet<Movie> movies;
public MovieRepository(IEnumerable<Movie> initialState = null)
{
this.movies = initialState?.ToHashSet() ?? new HashSet<Movie>();
}
public void Add(Movie movie) => movies.Add(movie);
public IEnumerable<Movie> Get() => movies;
public IEnumerable<Movie> Get(Func<Movie, bool> condition) => movies.Where(condition);
public Movie Get(Guid guid) => movies.SingleOrDefault(movie => movie.Id == guid);
public void Remove(Movie movie) => movies.Remove(movie);
}
}
Movie definition
namespace MovieLibrary
{
using System;
public class Movie
{
public Movie(string title, int year, string director, string summary)
{
this.Id = Guid.NewGuid();
this.Title = title;
this.Year = year;
this.Director = director;
this.Summary = summary;
}
public Guid Id { get; }
public string Title { get; }
public int Year { get; }
public string Director { get; }
public string Summary { get; }
public override bool Equals(object other) => other is Movie movie && movie.Id == this.Id;
public override int GetHashCode() => HashCode.Combine(Id);
public override string ToString() => string.Format(
"-- Details --{0}{0}Title: {1}{0}Year: {2}{0}Director: {3}{0}Summary: {4}",
Environment.NewLine,
this.Title,
this.Year,
this.Director,
this.Summary);
}
}
Library itself
namespace MovieLibrary
{
using System;
using System.Collections.Generic;
using System.Linq;
public class Library
{
private readonly IRepository<Movie, Guid> repository;
private readonly IDictionary<int, Movie> menuOptions;
public Library()
{
this.repository = this.InitializeRepository();
this.menuOptions = this.repository.Get()
.Select((movie, index) =>
new { Ordinal = index + 1, Movie = movie })
.ToDictionary(
item => item.Ordinal,
item => item.Movie);
}
public void Run()
{
Console.WriteLine("Welcome to the movie library! Pick a movie you'd like to watch...");
foreach (KeyValuePair<int, Movie> option in menuOptions)
{
Console.WriteLine($"{option.Key}: {option.Value.Title}");
}
int selectedOrdinal = 0;
Movie selectedMovie = null;
do
{
Console.Write("Select option: ");
string input = Console.ReadLine();
if (!int.TryParse(input, out selectedOrdinal))
{
Console.WriteLine("Please pick the existing option from the list...");
continue;
}
this.menuOptions.TryGetValue(selectedOrdinal, out selectedMovie);
}
while (selectedMovie == null);
Console.WriteLine("Thank you for picking our movie library! Your movie will start in a second...");
Console.WriteLine(selectedMovie);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
private IRepository<Movie, Guid> InitializeRepository()
{
// Mocking the initial data of library. You could load it from file instead.
IEnumerable<Movie> movies = Enumerable
.Range(1, 10)
.Select(ordinal => new Movie(
$"Title {ordinal}",
DateTime.UtcNow.Year + ordinal,
$"Some Director {ordinal}",
$"Some dummy summary text {ordinal}"));
return new MovieRepository(movies);
}
}
}
Can I get you to do one thing?
Turn the computer off (maybe after printing the problem statement) and sit down with a pencil and paper
Think about the problem in English (or your native language)
You have a file of movie info, one movie per line
You need to read the file, take input from the user that is a numerical index of the movie in the file lines, and show info about that movie in the console
Reason I'm asking you to turn the computer off is when sitting with visual studio open and a problem statement there is a great temptation to start writing code before thinking about what needs to be written. It's like being asked to build a bridge and you immediately march off to the builders yard and place an order for 10000 bricks - the first step to building a bridge is to find out all the requirements, draw it, make some calculations, think about what it has to support now and in the future. Building a bridge is a big task, this programming one you have is a relatively small task but it's big to you because you're just starting out. Once you learn the value of these pre-programming scoping exercises I promise you you'll use them throughout your entire career.
The problems will get bigger but the approach will remain the same; putting your algorithm on paper will help immensely, and should be viewed like writing an essay plan, or forming a sentence in your mind in English because you're an English native, rearranging it to how the Spanish person would say it, then translating it to Spanish words and speaking it. Everything you do when engineering software will be an exercise in translation, from headline overview, to detailed overview, to fine grained processes, eventually to computer code
You may always think in English and have to translate your English thoughts to c# actions; you need to write the process down in English first. Eventually you'll think in C# for some things, but it'll always mix with English, especially when dealing with normal users
Instead of pen and paper, you could also do this in c# comments then translate underneath them (and end up with nicely commented code, bonus) but as a beginner you'll get a lot more out of the visualization exercise of pen and paper. You can easily create flow diagrams, side notes, box outs, and rub things out - treeware approach will always help
So your algorithm might look like this
read all the lines out of the file
work through them one by one
turn the line into a movie object -- callout note to self, need a movie object, what does it look like?
add it to a collection of Movie objects
that's the reading part done -- should store that collection of movies somewhere. Should make that reading part a self contained thing
I now know how many movies I read out of the file because I have a collection with a count
ask the user for an index
print the movie at that index. No, wait. Stuff in c# is zero based so if the user wants movie 1, that will be in index 0 of the list/array/whatever -- remember to do a -1!!
ask the user for another index - this is repetitive, so I probably need a loop. Another note to self, need a way to get out of the loop
Then you might add
printing a movie needs breaking down
should have a separate bit of code that prints a movie
oh, there was that lecture about ToString and how it can be used to make a string representation of a custom object. Note to self- make a ToString in movie, then can just print the movie object and it will be ToStringed and nicely formatted automatically
The next thing I'll ask you to do, and it's a big ask, is to temporarily (or perhaps permanently) set aside all the code you wrote already. Some bits are usable, some are a mess of jumbled thoughts with no clear algorithm
Your movie class is probably ok
Your View method contains bits that would be useful for ToString
The method that reads the file is OK too
Start simple, with a static void main that first reads the movies (calls a ReadLibrary method, that returns a List<> of Movies) and enters a loop that shows the user a menu
Ask for input, just the movie index to be printed - none of that saving, adding new movies etc. Gotta keep it simple to start
Print the movie out, loop round again
With what you have already in the reading, the movie class, turning that view into an override ToString, you should be able to complete the current task in about 10 or fewer lines of code. If you go significantly over this (and you have currently) then your thinking has gone wrong
For example, you're asking for input and then you're saying "if they entered 1 then else if they entered 2 then..."
Take their input, turn it into a number and show the movie at that number (less one) in the collection. This makes it truly dynamic. Consider what will happen if they enter 999999 for a laugh and your movie collection only has 10 movies. Put a check in to stop it breaking. You might go back to your paper and where it says "ask the user for an index" you might add "and ensure that it isn't a crazy value"
Have an AskString method that takes a string question, prints the question and asks for input and returns it
Have an AskInt method that uses the AskString method and parses its return value to an int. extend the method to take another two parameters as well as the question- lower bound and upper bound. If the user enters a number outside the bounds, repeat the AskString until they enter a sensible value
I cannot, in good conscience, do your homework for you but I present this answer as educating you as to how to solve your own problem. All the bits you need are already present in what you've written somewhere but they lack structure and forethought
I've no qualms repeatedly editing this answer to address further queries you have- drop a comment as to what is needed
Based on my test, if your other movies have duplicate titles, years, etc., " if (movie.Title == "Monty Python and the Holy Grail") " cannot find a movie accurately.
Your loop " while (menuRunning){} " code can change to the following code:
while (menuRunning)
{
Console.WriteLine("Pick a number. Any number.");
try
{
int userOption = int.Parse(Console.ReadLine());
Console.Clear();
var _myMoviesArray = _movies.ToArray();
Console.WriteLine("Title:{0}\nYear:{1}\nDirector:{2}\nSummary:{3}\n", _myMoviesArray[userOption].Title, _myMoviesArray[userOption].Year, _myMoviesArray[userOption].Director, _myMoviesArray[userOption].Summary);
}
catch (Exception e) { Console.WriteLine("please enter the number in the correct format."); }
}
My test document
Code running result

Result Delegate never called to Load table in DynamoDB in Unity3D (C#)

Unity Version : 2017.3.1f1
AWS Service : DynamoDB
Testing Environment : Mac OSX on the editor
Scenario
Trying to load a table from Dynamo DB but having weird issues.
It loads extremely well when I use the primary HASH key that is valid but when I supply a wrong HASH key (key that is not present in the database), the delegate is never called.
Source
private void performLoadOperation()
{
ExperienceListing _experienceListing = null;
string _ISEN = "21-89297083-ebe3-40b2-a269-3cfe80922fed";
DH.log ("About to load ");
Context.LoadAsync<ExperienceListing>
(_ISEN, (_result)=>
{
Debug.Log ("result returned - " + _result.Result.Title);
if(_result.Exception == null)
{
_experienceListing = _result.Result as ExperienceListing;
Debug.Log ("Title - " + _result.Result.Title);
}
else
{
Debug.Log ("Exception - " + _result.Exception);
}
}
);
}
[DynamoDBTable("ExperienceListing")]
public class ExperienceListing
{
[DynamoDBHashKey] // Hash key.
public string ISEN{ get; set; }
[DynamoDBProperty]
public string Title{ get; set; }
[DynamoDBProperty]
public string Blurb{get;set;}
[DynamoDBProperty]
public int Views{ get; set; }
[DynamoDBProperty]
public int Rating{ get; set; }
}
Test Case
For the above performLoadOperation(), the delegate is being called properly. Now let's say that I change _ISEN variable to a string that is not present in the database, the delegate never gets called. I would like to note that, the internet is available.
Request
Am I supposed to give the correct hash key at all times and should understand that the delegate would never be called for invalid/wrong hash key's?
EDIT
The following worked for me but still it is wierd.
private void performLoadOperation()
{
ExperienceListing _experienceListing = null;
string _ISEN = "08c0ba69-af71-4017-afb8-5040f7033b33";
DH.log ("About to load ");
Context.LoadAsync<ExperienceListing>
(_ISEN, (_result)=>
{
if(_result.Exception == null)
{
_experienceListing = _result.Result as ExperienceListing;
Debug.Log ("No Exception");
if(_experienceListing == null)
{
Debug.Log ("Experience Listing is null");
}
else
{
Debug.Log ("Experience Listing not null - " + _experienceListing.Title);
}
}
else
{
Debug.Log ("Exception - " + _result.Exception.Message);
}
if(_result == null)
{
Debug.Log ("Return result is null");
}
}, null
);
}
When none of the "_result" is access for print logs, the logs were printed. As from advice from #MikeDinescu , tested with breakpoints which gave indication that delegate was called.
With the above solution, when the _result is still not null - it is required to manually check _experienceListing to know whether DyanomoDB has any of the tables from the hash key.
The delegate should be called regardless of whether the operation retrieves a result or not. In the case when there is no item matching the key, Result would be null and Exception would contain the error details.
However, the callback lambda contains a null reference exception right on the first line. Most likely what is happening is the lambda is invoked but throws an exception right away because it’s trying to dereference the Null result.
Are you sure it isn’t called? Have you tried putting a break point on the first line of the lambda?
The other thing to try would be to change the first Debug.WriteLine in the callback lambda to be a simple string, removing the part that attempts to dereference _result.Result, and then check if the debug message is printed even when the result is an exception.

DDD modifying more than one object per function (passing by reference)

I have a method inside a root entity (of type ProductOptionGroup) that sets properties on a reference to another object (target, also of type ProductOptionGroup).
I have to pass in by reference as the target variable is modified in the below method snippet:
void SetOptionDependency(Product sourceProduct, Product targetProduct, ref ProductOptionGroup target)
{
if (this.targetDependencyId == null)
{
this.targetDependencyId = target.Id;
}
if (this.targetDependencyId != target.Id)
{
// abort - reassignement of active dependency not allowed
}
if (this.map.Contains(sourceProduct.Id) == false)
{
// abort - the provided id is not associated with us
}
if (target.map.Contains(targetProduct.Id) == false)
{
// abort - the supplied id is not associated with the dependency target
}
// ** here the parameter passed in is modified **
target.associationCount[targetProduct.Id]++;
this.map.Add(sourceProduct.Id, targetProduct.Id);
}
I have put the code within the ProductOptionGroup aggregate as the business rules are most easily read there.
I guess the alternative could be to use a domain service to make the associations, but then some of the business logic and checking would come out of the entity and in to the service. I am not sure I like this idea.
The downside i see is that who ever is calling the method on the entity for would need to ensure that the entity is saved, along with the object modified by reference is also saved - the ref keyword on the method gives a big hint but is not explicit.
My question is does modifying variables passed in by reference to a entity method violate any DDD rules?
To give better context if needed, full code snippets below:
public class ProductOptionGroup
{
string Id; // our Id
string targetDependencyId; // we can link to one other ProductOptionsGroup by reference
MapList<string, string> map = new MapList<string, string>();
Dictionary<string, int> associationCount = new Dictionary<string, int>();
void AssociateProduct(Product product)
{
this.map.AddKey(product.Id);
}
void DisassociatedProduct(Product product)
{
if (this.map.Contains(product.Id) == false)
{
// abort - the provided id is not associated with us
}
// check children are not referring to this product
int count = 0;
if (this.associationCount.TryGetValue(product.Id, out count) && count > 0)
{
// abort - this product is being referenced
}
else
{
this.map.Remove(product.Id);
}
}
void SetOptionDependency(Product sourceProduct, Product targetProduct, ref ProductOptionGroup target)
{
if (this.targetDependencyId == null)
{
this.targetDependencyId = target.Id;
}
if (this.targetDependencyId != target.Id)
{
// abort - reassignement of active dependency not allowed
}
if (this.map.Contains(sourceProduct.Id) == false)
{
// abort - the provided id is not associated with us
}
if (target.map.Contains(targetProduct.Id) == false)
{
// abort - the supplied id is not associated with the dependency target
}
target.associationCount[targetProduct.Id]++;
this.map.Add(sourceProduct.Id, targetProduct.Id);
}
}
In short I have concluded modifying the state of one variable inside the function of another function is really bad news, I view this as a side effect.
As was mentioned in the comments, needing to modify multiple things at once pointed to deeper domain insight that was waiting to be explored to allow a more natural and clean model, and associated code.
https://softwareengineering.stackexchange.com/questions/40297/what-is-a-side-effect

How to get PrimarySmtpAddress for Exchance users and distribution lists

I am trying the email address of the from, to, and cc fields. Sometimes these are AD emails, SMTP, or Distribution emails.
I found someone who had a similiar problem here but they did not have anything about distribution lists.
I modified the code slightly to try to get this value.
if (type.ToLower() == "ex")
{
recip = Globals.ThisAddIn.Application.GetNamespace("MAPI").CreateRecipient(address);
if (recip.DisplayType == OlDisplayType.olDistList)
{
sAddress = recip.AddressEntry.GetExchangeDistributionList().PrimarySmtpAddress;
}
else
{
sAddress = recip.AddressEntry.GetExchangeUser().PrimarySmtpAddress;
}
}
else
{
sAddress = address.Replace("'", "");
}
The problem is that recip.DisplayType is null unless there is a small delay after getting a recipient and calling DisplayType on that object.
Is there a better way to do this?
I changed the code to the following but I have concerns that this will not work for all the DisplayTypes and I'm not even sure what most of the types are (The options are shown here http://msdn.microsoft.com/en-us/library/microsoft.office.interop.outlook.oldisplaytype%28v=office.14%29.aspx)
private static string GetSmtpAddress(AddressEntry addressEntry)
{
string address;
if (addressEntry.Type == "ex")
{
if (addressEntry.DisplayType == OlDisplayType.olDistList)
{
address = addressEntry.GetExchangeDistributionList().PrimarySmtpAddress;
}
else
{
address = addressEntry.GetExchangeUser().PrimarySmtpAddress;
}
}
else
{
address = addressEntry.Address;
}
return address;
}
You need to resolve the recipient first - after calling CreateRecipient, call Recipient.Resolve.

Encapsulation with multiple classes C#

So my question is over basic encapsulation. I know I am setting up my getters and setters right (I actually have a question later about this) but I have multiple classes. I have another class and I understand that I am making pieces of my code view-able to my outside class by making certain things public. So I think I set up my first code file right. (Some background, I have a class that is connecting to a database and then another that is encapsulating all the data. The first code section posted is the encapsulating part, I then post my three methods I was messing up on.)
I feel okay with the getting and setting, I feel a little unsure of my constructor. I feel like I put my variables in the parameter list so that I put values in them from my outside class? Right? or should I be putting public forms of my private variables in my other code file and then passing those into my constructor in that same file?
/ this my first code file
using System;
public class clsEncapsulate
{
private int mID;
private string mName;
private string mClassification;
private DateTime mConvocationDate;
private string mLocation;
public int ID
{
get
{
return mID;
}
set
{
mID = value;
}
}
public string Name
{
get
{
return mName;
}
set
{
mName = value;
}
}
public string Classification
{
get
{
return mName;
}
set
{
mName = value;
}
}
private DateTime ConvocationDate
{
get
{
return mConvocationDate;
}
set
{
mConvocationDate = value;
}
}
private string Location
{
get
{
return mLocation;
}
set
{
mLocation = value;
}
}
public clsEncapsulate(int id, string name, string classification, DateTime convocationDate, string location)
{
bool running = false;
while(running == false)
{
ID = mID;
Name = mName;
Classification = mClassification;
ConvocationDate = mConvocationDate;
Location = mLocation;
running = true;
}
}
}
In my second code file I am just going to put the methods that I am having trouble with.
private void refreshbox()
{
string formattedConvocation;
string formattedDateTime;
string formattedConvocationName;
lstConvocations.Items.Clear();
foreach (clsEncapsulate currentConvocation in mConvocationAL)
{
formattedConvocationName = currentConvocation.Name;
formattedConvocationName = truncateString(formattedConvocationName, 30);
formattedConvocation = formattedConvocationName.PadRight(33);
formattedConvocation += currentConvocation.Classification.PadRight(17);
formattedDateTime = currentConvocation.ConvocationDate.ToShortDateString().PadRight(10)
+ currentConvocation.ConvocationDate.ToShortTimeString().PadLeft(8);
formattedConvocation += formattedDateTime;
lstConvocations.Items.Add(formattedConvocation);
}
}
Alright, so in order for my second code file to manipulate the variables in the first code file, I need to expose them to this method. I didn't know if I should be putting my public variables in the constructor, or if I should be declaring them somewhere in my first code file. I was very unsure of how to expose these variables to this method. I've fiddle around with it but my book doesn't address this situation exactly and I was having trouble figuring it out.
If someone does answer this question please break down why you're going to put what you're going to put! I want to understand why, say, I put my public variables in one place, and not another. Or why I declare an object of my encapsulate class in one place and not another. I was trying to declare an encapsulate object in my method so it would give this method access to the variables, but it wasn't working! Please tell me what I was doing wrong or if you want me to post more of my code.
Below are the two other methods I was messing up on.
/ second method from my second code file I was messing up on:
private void displayProperties(int index)
{
if (index == -1)
{
return;
}
clsEncapsulate selectedValue = (clsEncapsulate)mConvocationAL[index];
txtConvocationName.Text = selectedValue.Name;
txtConvocationClassification.Text = selectedValue.Classification;
txtConvocationDate.Text = selectedValue.ConvocationDate.ToShortDateString();
txtConvocationTime.Text = selectedValue.ConvocationDate.ToShortTimeString();
txtConvocationLocation.Text = selectedValue.Location;
txtID.Text = selectedValue.ID.ToString();
}
/ last method I was messing up on:
private void readConvocations(string filterConstraint, string sortField, string sortOrder)
{
OleDbConnection connection = null;
OleDbDataReader reader = null;
try
{
connection = new OleDbConnection();
connection.ConnectionString = mConnectionString;
connection.Open();
string statement = "SELECT ID, Name, Classification, Location, Date FROM Convocations ";
if(filterConstraint != "")
{
statement += "WHERE Name LIKE " + toSQL(filterConstraint, true) + " ";
}
string statement2 = statement;
statement = string.Concat(new string[]
{
statement2, "ORDER BY ", sortField, " ", sortOrder
});
OleDbCommand oleDbCommand = new OleDbCommand(statement, connection);
reader = oleDbCommand.ExecuteReader();
mConvocationAL.Clear();
while(reader.Read())
{
clsEncapsulteconvocation = new clsEncapsulate();
convocation.ID = (int)reader["ID"];
convocation.Name = (string)reader["Name"];
convocation.Classification = (string)reader["Classification"];
convocation.Location = (string)reader["Location"];
convocation.ConvocationDate = (DateTime)reader["Date"];
mConvocationAL.Add(convocation);
}
}
finally
{
if (reader != null)
{
reader.Close();
}
if (connection != null)
{
connection.Close();
}
}
}
Tell me if you need me to elaborate more to help you understand my situation. I am new at learning vocabulary and want to understand this! Thank you for helping. :)
The code you provided is one public object and a bunch of private methods so its difficult to get the overall picture of how your code it working together but there are a few principles you can apply to make your code better structured, now and in the future.
Have a read about SOLID (http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)). The S and the D would apply well to your example.
Also you mentioned construtors and private properties. Try looking into Imutable types. That means once the object is created you cannot change it. For your clsEncapsulate class that would mean making your fields read only and remove the public setters.
Good luck.

Categories