Implementing a Collection Class Using .NET
Implementing a Collection Class Using .NET
Common Behaviors of Collections
Introduction
Although you can create a collection class from scratch, most collections classes share some fundamental behaviors and characteristics that you can apply when creating a collection class. To give a skeleton of these default requirements, the .NET Framework provides a few interfaces that you can implement.
Practical Learning: Introducing .NET Collection Classes
body { background-color: #2b5b8f; } .top-bar { top: 0px; height: 7em; width: 100%; position: fixed; background-color: #203864; } .logo-bar { margin: auto; width: 460px; } .navbar-inverse { background-color: #001132; border-top: 3px solid #cfdde0; border-bottom: 3px solid #cfdde0; } .navbar-fixed-top { top: 6.75em; } .jumbotron { padding-bottom: 4px; background-color: #153a62; } .col-md-4 h2 { color: #abcbd9; border-bottom: 1px solid #cfdde0; } .highligh { width: 100%; color: white; background-color: #203864; border: 1px solid white; } .lead { color: #cfdde0 } .col-md-4 p { color: #d5d4c2; } .copyright { color: #beeeab; } .push-down { margin-top: 8em; } .push-down h2 { color: #d5d4c2; } .push-down h3 { color: #abcbd9; } .push-down p { color: #cfdde0; } .push-down2 { margin-top: 4em; } .left-col { width: 100px } .ctl-deco { font-weight: 600; color: #cfdde0; } .large-col { width: 170px; font-weight: 600; color: #cfdde0; } .xlarge-col { width: 430px; font-weight: 600; color: #cfdde0; } address { color: #cfdde0; }
using System.Web.Mvc; namespace WaterDistributionCompany3.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } public ActionResult About() { ViewBag.Message = "We supply water to the whole region in a friendly and responsible manner."; return View(); } public ActionResult Contact() { ViewBag.Message = "Contact us for all your water concerns or issues."; return View(); } } }
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - Water for a Shining Life</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") <link rel="stylesheet" type="text/css" href="~/Content/WaterDistribution.css" /> </head> <body> <div class="top-bar"> <div class="logo-bar"><img src="~/Images/wsl1.png" alt="Water for a Shining Life" width="490" height="92" /></div> </div> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("Home", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("Emergency Services", "Index", "Home")</li> <li>@Html.ActionLink("Cummunity", "Index", "Home")</li> <li>@Html.ActionLink("Environment", "Index", "Home")</li> <li>@Html.ActionLink("Resources", "Index", "Home")</li> <li>@Html.ActionLink("Projects", "Index", "Home")</li> <li>@Html.ActionLink("Customer Service", "Index", "Home")</li> <li>@Html.ActionLink("Employment", "Index", "Home")</li> <li>@Html.ActionLink("Questions?", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> </ul> </div> </div> </div> <div class="container body-content"> @RenderBody() <hr /> <footer> <p class="copyright text-center">© @DateTime.Now.Year - Water for a Shining Life</p> </footer> </div> @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/bootstrap") @RenderSection("scripts", required: false) </body> </html>
@{ ViewBag.Title = "Welcome"; } <div class="jumbotron"> <h2>.</h2> <p class="lead">Our water utility company provides energy, greatness, and warmth for a everyday life, a shining life. We provide solutions to families, businesses, and the community.</p> <p class="lead">This is the employees portal section of the company. From here, employees can register a new water meter, manage a customer account, or create a water bill.</p> </div> <div class="row"> <div class="col-md-4"> <h2>Water Meters</h2> <p>Our company uses the most accurate, sophisticated, and environment-friendly water meters on the market. Our suppliers care.</p> <p>@Html.ActionLink("New Water Meter", "NewWaterMeter", "Home", new { @class = "btn btn-primary" })</p> </div> <div class="col-md-4"> <h2>Customers</h2> <p>We supply water to individuals, families, small businesses, as well as enterprises or government agencies.</p> <p>@Html.ActionLink("New Customer Account", "NewCustomer", "Home", new { @class = "btn btn-primary" })</p> </div> <div class="col-md-4"> <h2>Water Bills</h2> <p>Our water rates are very competitive nationwide. We use precise, effective, and strict algorithms when performing our water bills calculations.</p> <p>@Html.ActionLink("New Water Bill", "FindCustomer", "Home", new { @class = "btn btn-primary" })</p> </div> </div>
Introduction to the ICollection Interface
One of the primary pieces of information you should provide abo ut the values in a collection is the number of items a list is (currently) holding. When creating a collection class, to prepare it to provide this valuable information, you can (should) implement an interface named ICollection. The ICollection interface is defined in the System.Collections namespace. The ICollection interface derives from the IEnumerable initerface:
public interface ICollection : IEnumerable
The generic equivalent of the ICollection interface is defined in the System.Collections.Generic namespace. The ICollection<> interface inherits from the IEnumerable<> and the IEnumerable interfaces:
public interface ICollection<T> : IEnumerable<T>, IEnumerable
Therefore, to start a collection class, you can first implement one of these interfaces. Here is an example for the System.Collections.ICollection<> interface:
using System.Collections.generic; public class Itemization<T> : ICollection<T> { }
Thanks to the flexibility of arrays in the .NET Framework, you can create the items of the collection as an array and give it an initial size. Here is an example:
using System.Collections.Generic; public class Itemization<T> : ICollection<T> { private int size; private T[] items; public Itemization() { size = 0; items = new T[10]; } }
Implementing ICollection
To assist you with keeping track of the number of items in a collection, the ICollection<T> interface is equipped with an integral property named Count, which you must implement. To do this, you can create a private member variable that will actually keep a count of the number of items. The Count property can then be used to communicate this information to the clients of the class. Here is an example:
using System.Collections.Generic;
public class Itemization<T> : ICollection<T>
{
private int size;
public Itemization()
{
size = 0;
}
public virtual int Count
{
get { return size; }
}
}
The ICollection<> interface also allows its implementer to copy some of its items to an array. To provide this functionality, the interface is equipped with a method named CopyTo, which you must implement. The syntax of this method is:
void CopyTo(T[] array, int arrayIndex);
This method takes two arguments. The first argument is the array that will receive the items. The second argument is the index of the item from where the copying operation will begin. Here is an example:
public class Itemization<T> : ICollection<T>
{
private int size;
private T[] items;
public Itemization()
{
size = 0;
items = new T[10];
}
public virtual int Count
{
get { return size; }
}
public void CopyTo(T[] array, int index)
{
T[] values = new T[size];
for (int i = 0; i < size; i++)
values[i] = items[i];
array = values;
}
}
The System.Collections.ICollection interface extends the IEnumerable interface. The System.Collections.Generic.ICollection<T> interface extends the IEnumerable and the IEnumerable<> interfaces. In the previous lesson, we saw that this means that you should be able to use foreach in your ICollection<>-based collections but you must create the functionality yourself, which is done by implementing the GetEnumerator() method. Because the ICollection<> interface inherits from IEnumerable<> that itself inherits from IEnumerable, you must implement two versions of the GetEnumerator() methods. As we saw already, their syntaxes are:
IEnumerator<T> GetEnumerator(); IEnumerator GetEnumerator();
Even if you don't want to support this feature, you still must provide at least a skeleton for these methods. Here is an example:
public class Itemization<T> : ICollection<T>
{
private int size;
private T[] items;
public Itemization()
{
size = 0;
items = new T[10];
}
public virtual int Count
{
get { return size; }
}
public void CopyTo(T[] array, int index)
{
T[] values = new T[size];
for (int i = 0; i < size; i++)
values[i] = items[i];
array = values;
}
public IEnumerator<T> GetEnumerator()
{
int counter = 0;
while (counter < Count)
{
yield return items[counter];
counter++;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
int counter = 0;
while (counter < Count)
{
yield return items[counter];
counter++;
}
}
}
Introduction to the IList Interface
Overview
While the System.Collections.Generic.ICollection<> (and the System.Collections.ICollection) interface provides basic functionality, to assist you in laying out a foundation of a collection class as complete as possible, the .NET Framework provides an interface named IList<> (and its accompanying IList interface). The IList<> interface is defined in the System.Collections.Generic namespace and its non-generic equivalent of the same name is defined in the System.Collections namespace. To use the functionalities of the System.Collections.Generic.IList<> interface necessary to add, insert, get, and delete items from a collection, you must create a class that implements them.
Implementing IList
The System.Collections.IList interface starts as follows:
public interface IList : ICollection, IEnumerable
The System.Collections.Generic.IList<> interface starts as follows:
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
As mentioned above, to create a collection, you can implement the IList<> interface. Here is an example of starting it:
using System.Collections.Generic; public class Itemization<T> : IList<T> { }
This means that the IList<> interface extends the ICollection<>, the IEnumerable<>, and the IEnumerable interfaces. This also implies that you must implement the members of all these parent interfaces, including the Count, the IsReadOnly properties, the CopyTo(), and the GetEnumerator() methods as we saw above. From what we learned with the ICollection interface, here are examples of implementing these members for the System.Collections.IList<T> interface:
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
public Itemization()
{
size = 0;
items = new T[10];
}
public void CopyTo(T[] array, int arrayIndex)
{
T[] values = new T[size];
for (int i = 0; i < size; i++)
values[i] = items[i];
array = values;
}
public int Count
{
get { return size; }
}
public IEnumerator<T> GetEnumerator()
{
int counter = 0;
while (counter < Count)
{
yield return items[counter];
counter++;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
int counter = 0;
while (counter < Count)
{
yield return items[counter];
counter++;
}
}
}
As you may know already, while you are implementing an interface, if you are working in Microsoft Visual Studio, it would keep displaying some warnings to remind you about some member(s) you haven't implemented yet. Microsoft Visual Studion can generate skeleton code for your implementation. This would accomplish two goals: 1) it would eliminate the warnings (and errors), 2) it would provide a type of default implementation or a member.
To ask Microsoft Visual Studio to generate code for your interface implementation, start the class in the Code Editor and specify its interface(s). Then:
Practical Learning: Starting an IList Implementation
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace WaterDistributionCompany3.Models { [Serializable] public class Collection<T> : IList<T> { } }
using System; using System.Collections; using System.Collections.Generic; namespace WaterDistributionCompany3.Models { [Serializable] public class Collection<T> : IList<T> { // This field will hold the number of items as they are added or removed private int counter; // This array holds the items of the collection private T[] items; // The default constructor is used to initialize a constructor with some default values. public Collection() { counter = 0; // We will start the collection with a capacity for 10 items items = new T[10]; } /* IList<T>: This indexed property makes it possible to access EACH item as done in an array. */ public T this[int index] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } /* ICollection<T>: This read-only property holds the total number of items in the collection*/ public int Count { get { return counter; } } /* ICollection<T>: This property would prevent new item addition.*/ public bool IsReadOnly { get { throw new NotImplementedException(); } } /* ICollection<T>: This method is used to add a new item to the collection.*/ public void Add(T item) { throw new NotImplementedException(); } // ICollection<T>: This method deletes all items from the collection public void Clear() { throw new NotImplementedException(); } /* ICollection<T>: This method is used to find out whether * the collection contains the item passed as argument. */ public bool Contains(T item) { throw new NotImplementedException(); } /* ICollection<T>: This method is used to copy * the items of this collection to an array passed as argument. */ public void CopyTo(T[] array, int arrayIndex) { throw new NotImplementedException(); } /* IEnumerable<T>: Used for iteration */ public IEnumerator<T> GetEnumerator() { throw new NotImplementedException(); } /* IList<T>: This method checks whether the item * passed as argument exists in the collection. If so, it returns its index. */ public int IndexOf(T item) { throw new NotImplementedException(); } /* IList<T>: This method can be used to insert an item at a certain position inside the collection. */ public void Insert(int index, T item) { throw new NotImplementedException(); } /* ICollection<T>: This method first checks the existence of * the item passed as argument. If the item exists, the method deletes it. */ public bool Remove(T item) { throw new NotImplementedException(); } /* IList<T>: This method can be used to delete the item positioned at the index passed as argument. */ public void RemoveAt(int index) { throw new NotImplementedException(); } /* IEnumerable: Used for iteration */ IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } }
Populating a Collection
A Read-Only Collection
Most collections are made to receive new values. If you want, you can create a list that cannot receive new values. To support this, the IList interface is equipped with a Boolean property named IsReadOnly. If a list is read-only, it would prevent the clients from adding items to it.
Here is an example of implementing the IsReadOnly property for the System.Collections.IList<T> interface:
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
public Itemization()
{
size = 0;
items = new T[10];
}
public void CopyTo(T[] array, int arrayIndex)
{
T[] values = new T[size];
for (int i = 0; i < size; i++)
values[i] = items[i];
array = values;
}
public int Count
{
get { return size; }
}
public bool IsReadOnly
{
get { return false; }
}
public IEnumerator<T> GetEnumerator()
{
int counter = 0;
while (counter < Count)
{
yield return items[counter];
counter++;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
int counter = 0;
while (counter < Count)
{
yield return items[counter];
counter++;
}
}
}
Practical Learning: Setting the Read-Only Effect
using System.Collections;
using System.Collections.Generic;
namespace WaterDistributionCompany3.Models
{
[Serializable]
public class Collection<T> : IList<T>
{
. . . No Change
/* ICollection<T>: This property would prevent new item addition.*/
public bool IsReadOnly
{
get
{
return false;
}
}
. . . Change
}
}
Adding an Item
As it should be obvious, the primary operation to perform on a list is to populate it with at least one value or object. To support this operation, the IList<> interface is equipped with a method named Add. Its syntax is:
void Add(T item);
This method takes one argument as the value to add to the collection. If your collection is an array, you can first check that there is still enough room to add a new item. In reality, this is never an issue with the System.Collections.IList interface:
We saw that the Add() method could be defined as follows::
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
. . .
public int Add(T item)
{
this.items[this.size] = item;
this.size++;
return size;
}
}
By default, an array has a fixed size. If you try adding an item in a position higher than the maximum number of items, the compiler would throw an IndexOutOfRangeException exception. Fortunately, we saw that the Array class is equipped with a method named Resize. This allows you to increase the size of an array when you judge it necessary. As a consequence, you can have an array as big as possible.
Practical Learning: Adding an Item to the Collection
using System; using System.Collections; using System.Collections.Generic; namespace WaterDistributionCompany3.Models { [Serializable] public class Collection<T> : IList<T> { . . . No Change private void CheckAndIncreaseIfNecessary() { // If there is not enough room to add a new item (low capacity), create room for 5 additional items. if (counter >= items.Length) Array.Resize<T>(ref items, items.Length + 5); } /* ICollection<T>: This method is used to add a new item to the collection.*/ public void Add(T item) { // Check whether there is still room in the array. // If there is not enough room, then increase the capacity CheckAndIncreaseIfNecessary(); // Add the item at the end items[counter] = item; // Increase the current number of items counter++; } . . . No Change } }
using System; namespace WaterDistributionCompany3.Models { [Serializable] public class WaterMeter { public string MeterNumber { get; set; } public string Make { get; set; } public string Model { get; set; } public string MeterSize { get; set; } public override bool Equals(object obj) { WaterMeter meter = (WaterMeter)obj; if (meter.MeterNumber == MeterNumber) return true; return false; } public override int GetHashCode() { return base.GetHashCode(); } } }
using System.Web.Mvc;
namespace WaterDistributionCompany40.Controllers
{
public class WaterMetersController : Controller
{
// GET: WaterMeters
public ActionResult Index()
{
return View();
}
// GET: WaterMeters/Create
public ActionResult Create()
{
return View();
}
}
}
@{ ViewBag.Title = "Create Water Meter"; } <div class="push-down"> <h2 class="text-center">Create Water Meter</h2> </div> @{ string strErrorMessage = string.Empty; FileStream fsWaterMeters = null; string strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr"); // Create a list of water meters. Since we don't know whether a list of meter waters exists already, let's start with an empty one WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter> waterMeters = new WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>(); System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bfWaterMeters = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); if (IsPost) { // Make sure a meter number was provided. If not, don't do nothing if (string.IsNullOrEmpty(Request["meterNumber"])) { strErrorMessage = "You must enter a meter number. Otherwise, the water meter account cannot be created."; } else { WaterDistributionCompany40.Models.WaterMeter meter = new WaterDistributionCompany40.Models.WaterMeter() { MeterNumber = Request["MeterNumber"], Make = Request["Make"], Model = Request["Model"], MeterSize = Request["MeterSize"] }; // If a list of water meters was previously created and saved to a file... if (File.Exists(strFileWaterMeters)) { // ... open that file ... using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read)) { // ... get the list of water meters waterMeters = (WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters); } } // Whether the list of water meters is empty or not, add the new water meter to the list waterMeters.Add(meter); // Save the new list of water meters to a file using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Create, FileAccess.Write, FileShare.Write)) { bfWaterMeters.Serialize(fsWaterMeters, waterMeters); // Reset the form in case the user wants to create another record } } } } <hr /> <div align="center"> @using (Html.BeginForm()) { <table> <tr> <td class="small ctl-deco"><label for="mNbr">Meter #:</label></td> <td>@Html.TextBox("MeterNumber", "", new { @class = "form-control small" })</td> </tr> <tr> <td class="ctl-deco"><label for="make"></label>Make:</td> <td>@Html.TextBox("Make", "", new { @class = "form-control medium" })</td> </tr> <tr> <td class="ctl-deco">Model:</td> <td>@Html.TextBox("Model", "", new { @class = "form-control medium" })</td> </tr> <tr> <td class="ctl-deco">Meter Size:</td> <td>@Html.TextBox("MeterSize", "", new { @class = "form-control medium" })</td> </tr> <tr> <td><hr /></td> <td><hr /></td> </tr> <tr> <td></td> <td> <input type="reset" name="btnReset" value="Reset" class="btn btn-primary medium" /> <input type="submit" name="btnSubmit" value="Submit" class="btn btn-primary medium" /> </td> </tr> </table> } <table> <tr> <td style="width: 120px"> </td> <td> @Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" }) @Html.ActionLink("New Customer", "NewCustomer", "BillsProcessing", new { @class = "navbar-brand" }) </td> </tr> </table> <p class="text-center">@strErrorMessage</p> </div>
Meter # | Make | Model | Meter Size |
293-740 | Breston | S-93749 | 3/4 Inches |
820-418 | Vashty Worldwide | DD-3840 | 3/4 Inches |
Inserting an Item
When you call the Add() method, it adds the new value to the end of the list. Sometimes, you will want the new value to be inserted somewhere inside the list. To support this operation, both the System.Collections.IList and the System.Collections.Generic.IList interfaces provide a method named Insert. The syntax of the System.Collections.IList.Insert() method is:
void Insert(int index, object value);
The syntax of the System.Collections.Generic.IList.Insert() method is:
void Insert(int index, T value);
This method takes two arguments. The second argument is the value or object that will be inserted into the list. The argument must hold a valid value or object. Because the System.Collections.IList.Insert() method takes an object value, if your collection is using a different type of value, you may have to cast it to object. The first argument is the index of the item that will precede the new one.
As mentioned for the System.Collections.IList.Add() method, there are a few things you should know about this operation's success or failure:
The System.Collections.Generic.IList.Insert() method can be implemented as follows:
using System.Collections.Generic; namespace Exercises.App_Code { public class Itemization<T> : IList<T> { . . . No Change public void Insert(int index, T value) { // If the index is the same as the current number of items // then call Add() and proceed as if we want to add a new item if (index >= size) { Add(value); return; } // If the index is positive but less than the current size, // then you need to insert the item inside the collection. if ((index >= 0) && (index < size)) { // First, push each item one position up to create // an empty space for (int i = (size - 1); i > index - 1; i--) items[i + 1] = items[i]; // Then put the new item in the indicated position items[index] = value; // Since the new item has been added, increase the // current number of items size++; } } public void CopyTo(T[] array, int index) { . . . No Change } . . . No Change } }
When calling the IList<>.Insert() method, if you pass an invalid index, the compiler would throw an ArgumentOutOfRangeException.
Locating an Item in the Collection
this Item of a Collection
While using a list, various operations require that you know the object you are currently accessing. To support this operation, the IList and the IList<> interfaces are equipped with an indexed property named Item that you must implement. The Item property of the System.Collection.Generic.IList interface is declared as follows:
T this[int index] { get; set; }
If you implement it right, this property can perform one of three operations:
Here is an example of implementing the indexed property of the IList<> interface:
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
. . .
// The index property can be used to add an item to a collection,
// or to get an item from the collection
public T this[int index]
{
get
{
return this.items[index];
}
set
{
this.items[index] = value;
}
}
}
After creating this property, you can then access an item using its index and applying the [] operator on its instance.
Practical Learning: Identifying this Item in the Collection
using System; using System.Collections; using System.Collections.Generic; namespace WaterDistributionCompany4.Models { [Serializable] public class Collection<T> : IList<T> { . . . No Change /* IList<T>: This indexed property makes it possible to access EACH item as done in an array. */ public T this[int index] { get { return items[index]; } set { items[index] = value; } } . . . No Change } }
Iterating Through a Collection
One of the most valuable operations to make available to a collection is the abilite to visit each one of its members. As we saw already, this operation is supported by the GetEnumerator() method provided by the IEnumerable<T> interface that each collection class implements.
Practical Learning: Iterating Through a Collection
using System; using System.Collections; using System.Collections.Generic; namespace WaterDistributionCompany3.Models { [Serializable] public class Collection<T> : IList<T> { . . . No Change /* IList<T>: This indexed property makes it possible to access EACH item as done in an array. */ public T this[int index] { get { return items[index]; } set { items[index] = value; } } . . . No Change private void CheckAndIncreaseIfNecessary() { // If there is not enough room to add a new item (low capacity), create room for 5 additional items. if (counter >= items.Length) Array.Resize<T>(ref items, items.Length + 5); } /* ICollection<T>: This method is used to add a new item to the collection.*/ public void Add(T item) { // Check whether there is still room in the array. // If there is not enough room, then increase the capacity CheckAndIncreaseIfNecessary(); // Add the item at the end items[counter] = item; // Increase the current number of items counter++; } . . . No Change /* IEnumerable<T>: Used for iteration */ public IEnumerator<T> GetEnumerator() { int number = 0; while (number < Count) { yield return items[number]; number++; } } . . . No Change /* IEnumerable: Used for iteration */ IEnumerator IEnumerable.GetEnumerator() { int number = 0; while (number < Count) { yield return items[number]; number++; } } } }
@{
ViewBag.Title = "Create Water Meter";
}
<div class="push-down">
<h2 class="text-center">Create Water Meter</h2>
</div>
@{
string strErrorMessage = string.Empty;
FileStream fsWaterMeters = null;
string strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr");
// Create a list of water meters. Since we don't know whether a list of meter waters exists already, let's start with an empty one
WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter> waterMeters = new WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>();
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bfWaterMeters = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
if (IsPost)
{
// Make sure a meter number was provided. If not, don't do nothing
if (string.IsNullOrEmpty(Request["meterNumber"]))
{
strErrorMessage = "You must enter a meter number. Otherwise, the water meter account cannot be created.";
}
else
{
WaterDistributionCompany40.Models.WaterMeter meter = new WaterDistributionCompany40.Models.WaterMeter()
{
MeterNumber = Request["MeterNumber"],
Make = Request["Make"],
Model = Request["Model"],
MeterSize = Request["MeterSize"]
};
// If a list of water meters was previously created and saved to a file...
if (File.Exists(strFileWaterMeters))
{
// ... open that file ...
using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read))
{
// ... get the list of water meters
waterMeters = (WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters);
}
}
// Whether the list of water meters is empty or not, add the new water meter to the list
waterMeters.Add(meter);
// Save the new list of water meters to a file
using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Create, FileAccess.Write, FileShare.Write))
{
bfWaterMeters.Serialize(fsWaterMeters, waterMeters);
// Reset the form in case the user wants to create another record
}
}
}
}
<hr />
<div align="center">
@using (Html.BeginForm())
{
<table>
<tr>
<td class="small ctl-deco"><label for="mNbr">Meter #:</label></td>
<td>@Html.TextBox("MeterNumber", "", new { @class = "form-control small" })</td>
</tr>
<tr>
<td class="ctl-deco"><label for="make"></label>Make:</td>
<td>@Html.TextBox("Make", "", new { @class = "form-control medium" })</td>
</tr>
<tr>
<td class="ctl-deco">Model:</td>
<td>@Html.TextBox("Model", "", new { @class = "form-control medium" })</td>
</tr>
<tr>
<td class="ctl-deco">Meter Size:</td>
<td>@Html.TextBox("MeterSize", "", new { @class = "form-control medium" })</td>
</tr>
<tr>
<td><hr /></td>
<td><hr /></td>
</tr>
<tr>
<td></td>
<td>
<input type="reset" name="btnReset" value="Reset" class="btn btn-primary medium" />
<input type="submit" name="btnSubmit" value="Submit" class="btn btn-primary medium" />
</td>
</tr>
</table>
}
<table>
<tr>
<td style="width: 120px"> </td>
<td>
@Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" })
@Html.ActionLink("New Customer", "NewCustomer", "BillsProcessing", new { @class = "navbar-brand" })
</td>
</tr>
</table>
<p class="text-center">@strErrorMessage</p>
@{
strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr");
waterMeters = new WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>();
if (File.Exists(strFileWaterMeters))
{
using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read))
{
waterMeters = (WaterDistributionCompany40.Models.Collection<WaterDistributionCompany40.Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters);
<table style="color: white" border="3">
<tr>
<td>Meter #</td>
<td>Make</td>
<td>Model</td>
<td>Meter Size</td>
</tr>
@foreach (WaterDistributionCompany40.Models.WaterMeter meter in waterMeters)
{
<tr>
<td>@meter.MeterNumber</td>
<td>@meter.Make</td>
<td>@meter.Model</td>
<td>@meter.MeterSize</td>
</tr>
}
</table>
}
}
}
</div>
Meter # | Make | Model | Meter Size |
627-425 | Breston | T-39478 | 5/8 Inches |
304-861 | Vashty Worldwide | DD-3840 | 3/4 Inches |
@{ ViewBag.Title = "Create Water Meter"; } <div class="push-down"> <h2 class="text-center">Create Water Meter</h2> </div> <hr /> <div align="center"> @using (Html.BeginForm()) { <table> <tr> <td class="small ctl-deco"><label for="mNbr">Meter #:</label></td> <td>@Html.TextBox("MeterNumber", "", new { @class = "form-control small" })</td> </tr> <tr> <td class="ctl-deco"><label for="make"></label>Make:</td> <td>@Html.TextBox("Make", "", new { @class = "form-control medium" })</td> </tr> <tr> <td class="ctl-deco">Model:</td> <td>@Html.TextBox("Model", "", new { @class = "form-control medium" })</td> </tr> <tr> <td class="ctl-deco">Meter Size:</td> <td>@Html.TextBox("MeterSize", "", new { @class = "form-control medium" })</td> </tr> <tr> <td><hr /></td> <td><hr /></td> </tr> <tr> <td></td> <td> <input type="reset" name="btnReset" value="Reset" class="btn btn-primary medium" /> <input type="submit" name="btnSubmit" value="Submit" class="btn btn-primary medium" /> </td> </tr> </table> } <table> <tr> <td style="width: 120px"> </td> <td> @Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" }) @Html.ActionLink("New Customer", "NewCustomer", "BillsProcessing", new { @class = "navbar-brand" }) </td> </tr> </table> </div>
using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Web.Mvc; namespace WaterDistributionCompany40.Controllers { public class WaterMetersController : Controller { // GET: WaterMeters public ActionResult Index() { return View(); } // GET: WaterMeters/Create public ActionResult Create(string MeterNumber, string MeterSize, string Make, string Model) { FileStream fsWaterMeters = null; BinaryFormatter bfWaterMeters = new BinaryFormatter(); string strFileWaterMeters = Server.MapPath("~/App_Data/WaterMeters.mtr"); Models.Collection<Models.WaterMeter> waterMeters = new Models.Collection<Models.WaterMeter>(); // Make sure a meter number was provided. If not, don't do nothing if (!string.IsNullOrEmpty(MeterNumber)) { // Create a water meter based on the values gotten from the form Models.WaterMeter meter = new Models.WaterMeter() { MeterNumber = MeterNumber, Make = Make, Model = Model, MeterSize = MeterSize }; // If a list of water meters was previously created and saved to a file... if (System.IO.File.Exists(strFileWaterMeters)) { // ... open that file ... using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Open, FileAccess.Read, FileShare.Read)) { // ... get the list of water meters waterMeters = (Models.Collection<Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters); } } // Whether the list of water meters is empty or not, add the new water meter to the list waterMeters.Add(meter); // Save the new list of water meters to a file using (fsWaterMeters = new FileStream(strFileWaterMeters, FileMode.Create, FileAccess.Write, FileShare.Write)) { bfWaterMeters.Serialize(fsWaterMeters, waterMeters); } // In case the user wants to create a new water meter, send him/her back to the form return RedirectToAction("Create"); } return View(); } } }
Meter # | Make | Model | Meter Size |
925-935 | Igawa International | DTT 8802 | 1 Inch |
779-958 | Igawa International | DMG 4000 | 3/4 Inches |
using System; namespace WaterDistributionCompany4.Models { [Serializable] public class Customer { public string AccountNumber { get; set; } public string MeterNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public string City { get; set; } public string County { get; set; } public string State { get; set; } public string ZIPCode { get; set; } public override bool Equals(object obj) { Customer client = (Customer)obj; if (client.AccountNumber == AccountNumber) return true; return false; } public override int GetHashCode() { return base.GetHashCode(); } } }
using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Web.Mvc; namespace WaterDistributionCompany4.Controllers { public class CustomersController : Controller { // GET: Customers public ActionResult Index() { return View(); } // GET: Customers/Create public ActionResult Create(string AccountNumber, string MeterNumber, string FirstName, string LastName, string Address, string City, string County, string State, string ZIPCode) { FileStream fsCustomers = null; BinaryFormatter bfCustomers = new BinaryFormatter(); string strFileCustomers = Server.MapPath("~/App_Data/Customers.cts"); Models.Collection<Models.Customer> customers = new Models.Collection<Models.Customer>(); if (!string.IsNullOrEmpty(AccountNumber) && !string.IsNullOrEmpty(MeterNumber)) { Models.Customer client = new Models.Customer() { AccountNumber = AccountNumber, MeterNumber = MeterNumber, FirstName = FirstName, LastName = LastName, Address = Address, City = City, County = County, State = State, ZIPCode = ZIPCode }; if (System.IO.File.Exists(strFileCustomers)) { using (fsCustomers = new FileStream(strFileCustomers, FileMode.Open, FileAccess.Read, FileShare.Read)) { customers = (Models.Collection<Models.Customer>)bfCustomers.Deserialize(fsCustomers); } customers.Add(client); using (fsCustomers = new FileStream(strFileCustomers, FileMode.Create, FileAccess.Write, FileShare.Write)) { bfCustomers.Serialize(fsCustomers, customers); } } return RedirectToAction("Create"); } return View(); } } }
@{ ViewBag.Title = "New Customer"; } <div class="push-down"> <h2 class="text-center">New Customer</h2> </div> <hr /> <div align="center"> @using (Html.BeginForm()) { <table style="width: 500px"> <tr> <td class="left-col ctl-deco">Account #:</td> <td>@Html.TextBox("txtAccountNumber", "", new { style = "width: 200px" })</td> <td> </td> <td> </td> </tr> <tr> <td class="ctl-deco">Meter #:</td> <td>@Html.TextBox("txtMeterNumber", "", new { style = "width: 200px" })</td> <td> </td> <td> </td> </tr> </table> <table style="width: 500px"> <tr> <td class="left-col ctl-deco">First Name:</td> <td>@Html.TextBox("txtFirstName", "", new { style = "width: 100px" })</td> <td class="ctl-deco"> Last Name:</td> <td>@Html.TextBox("txtLastName", "", new { style = "width: 100px" })</td> </tr> <tr> <td class="ctl-deco">Address:</td> <td colspan="3">@Html.TextBox("txtAddress", "", new { style = "width: 400px" })</td> </tr> <tr> <td class="ctl-deco">City:</td> <td>@Html.TextBox("txtCity", "", new { style = "width: 100px" })</td> <td class="ctl-deco"> County:</td> <td>@Html.TextBox("txtCounty", "", new { style = "width: 100px" })</td> </tr> <tr> <td class="ctl-deco">State:</td> <td>@Html.TextBox("txtState", "", new { style = "width: 100px" })</td> <td class="ctl-deco"> ZIP-Code:</td> <td>@Html.TextBox("txtZIPCode", "", new { style = "width: 100px" })</td> </tr> </table> <hr /> <table> <tr> <td style="width: 120px"> </td> <td><input type="reset" name="btnReset" value="Reset" style="width: 120px" /></td> <td> </td> <td><input type="submit" name="btnSubmit" value="Submit" style="width: 120px" /></td> </tr> </table> <table> <tr> <td style="width: 120px"> </td> <td>@Html.ActionLink("New Water Meter", "CreateWaterMeter", "BillsProcessing", new { @class = "navbar-brand" }) @Html.ActionLink("Customer Bill Review", "WaterBillStartUp", "BillsProcessing", new { @class = "navbar-brand" })</td> </tr> </table> } </div>
Account # | Meter # | First Name | Last Name | Address | City | County | State | ZIP Code |
2958-314-5294 | 627-425 | Nicholas | Thorn | 2599 Phenicia Road | Silver Spring | Montgomery | MD | 20906 |
8046-728-5060 | 304-861 | Augustino | Derbez | 7507 Westchester Ave | Washington | DC | 20008 | |
1848-205-3313 | 925-935 | Frank | Nunka | 13931 Wellington Street | College Park | Pringe George | MD | 20740 |
4024-850-0482 | 820-418 | Marion | Patters | 10572 Maya Blvd | Frederick | MD | 20111 | |
7029-371-8594 | 293-740 | Danielle | Dormand | 2515 Guthierez Street | Falls Church | VA | 22046 |
Checking Whether a Collection Contains a Certain Item
One of the routine operations you can perform on a list is to find out whether it contains a certain value. To assist you with this operation, the IList<> interface is equipped with a method named Contains. Its syntax is:
bool Contains(T item);
This method takes as argument the value to look for. If the value is found in the collection, the method returns true. If the value is not found in the collection, this method returns false. Here is an example of implementing this method:
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
. . .
public bool Contains(T value)
{
for (int i = 0; i < Count; i++)
if (items[i].Equals(value))
return true;
return false;
}
}
This method calls the Equals() method of the objects that make up the list to find out whether the value argument exists in the collection. If this method produces a wrong result, especially if you are using your own class to represent the item, you should (must) override the Equals() method. Here is an example of a class that indicates that two Employee objects are the same if they have the same employee number (because two employees in the same company/collection should not have the same employee number:
using System.Collections.Generic;
namespace Exercises.App_Code
{
public class Itemization<T> : IList<T>
{
. . . No Change
}
public class Employee
{
public long EmployeeNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public double HourlySalary { get; set; }
public override bool Equals(object obj)
{
Employee empl = (Employee)obj;
if (this.EmployeeNumber == empl.EmployeeNumber)
return true;
return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}
Practical Learning: Checking Whether a Collection Contains a Certain Item
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WaterDistributionCompany3.Models
{
[Serializable]
public class Collection<T> : IList<T>
{
. . . No Change
/* ICollection<T>: This method is used to find out whether
* the collection contains the item passed as argument. */
public bool Contains(T item)
{
for (int i = 0; i < counter; i++)
if (items[i].Equals(item))
return true;
return false;
}
. . . No Change
}
}
Getting the Index of an Item
The Contains() method is used to check whether a particular value (already) exists in the collection. If you know that a certain item exists but you don't know its index inside the list, the IList<> interface can assist you through a method named IndexOf. Its syntax is:
int IndexOf(T item);
This method takes as argument the value to look for in the list. If the value is found in the collection, the method returns its index. If there is no value defined like that, the method returns -1. Here is an example of implementing this method:
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
. . .
public int IndexOf(T value)
{
for (int i = 0; i < Count; i++)
if (items[i].Equals(value))
return i;
return -1;
}
}
Once again, this method calls the Equals() method of the objects that make up the collection. In most cases, you should make sure you override the Equals() method in the class that T represents.
Practical Learning: Getting the Index of an Item
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WaterDistributionCompany3.Controllers
{
[Serializable]
public class Collection<T> : IList<T>
{
. . . No Change
/* IList<T>: This method checks whether the item
* passed as argument exists in the collection. If so, it returns its index. */
public int IndexOf(T item)
{
for (int i = 0; i < counter; i++)
if (items[i].Equals(item))
return i;
return -1;
}
. . . No Change
}
}
Getting an Item from a Collection
Getting an item consists of finding and producing it, not necessarily its index or whether it exists. You can write code in your collection class to get the object or you can use a combination of the methods and properties we have already reviewed to locate and produce the item.
Practical Learning: Getting an Item from a Collection
using System; namespace WaterDistributionCompany3.Models { [Serializable] public class WaterBill { public int InvoiceNumber { get; set; } public DateTime MeterReadingStartDate { get; set; } public DateTime MeterReadingEndDate { get; set; } public int NumberOfDays { get; set; } public float CounterReadingStart { get; set; } public float CounterReadingEnd { get; set; } public float TotalHCF { get; set; } public int TotalGallons { get; set; } public double First15HCF { get; set; } public double Next10HCF { get; set; } public double RemainingHCF { get; set; } public double SewerCharges { get; set; } public double StormCharges { get; set; } public double WaterUsageCharges { get; set; } public double TotalCharges { get; set; } public double CountyTaxes { get; set; } public double StateTaxes { get; set; } public DateTime PaymentDueDate { get; set; } public double AmountDue { get; set; } public DateTime LatePaymentDueDate { get; set; } public double LateAmountDue { get; set; } public override bool Equals(object obj) { WaterBill bill = (WaterBill)obj; if (bill.InvoiceNumber == InvoiceNumber) return true; return false; } public override int GetHashCode() { return base.GetHashCode(); } } }
using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Web.Mvc; namespace WaterDistributionCompany4.Controllers { public class WaterBillsController : Controller { // GET: WaterBills public ActionResult Index() { return View(); } // GET: WaterBills/WaterBillStartUp public ActionResult WaterBillStartUp() { return View(); } // GET: WaterBills/BillPreparation public ActionResult BillPreparation(string AccountNumber, string MeterReadingStartDate, string MeterReadingEndDate, string CounterReadingStart, string CounterReadingEnd) { /* We will start our invoices numbers at 1000001. * For now, assuming that there is no bill yet, let's set the starting number at 100000. */ int invoiceNumber = 100000; Models.Customer cust = new Models.Customer(); BinaryFormatter bfCustomers = new BinaryFormatter(); BinaryFormatter bfWaterBills = new BinaryFormatter(); BinaryFormatter bfWaterMeters = new BinaryFormatter(); string strCustomersFile = Server.MapPath("~/App_Data/Customers.cts"); string strWaterBillsFile = Server.MapPath("~/App_Data/WaterBills.wbs"); string strWaterMetersFile = Server.MapPath("~/App_Data/WaterMeters.mtr"); // We will need a list customers. Since we don't know if one exists already, let's start with an empty one. Models.Collection<Models.Customer> customers = new Models.Collection<Models.Customer>(); Models.Collection<Models.WaterBill> waterBills = new Models.Collection<Models.WaterBill>(); Models.Collection<Models.WaterMeter> waterMeters = new Models.Collection<Models.WaterMeter>(); // Make sure the user provided all the minimum pieces of information that were requested. // if( !string.IsNullOrEmpty(AccountNumber) && !string.IsNullOrEmpty(MeterReadingStartDate) && !string.IsNullOrEmpty(MeterReadingEndDate) && !string.IsNullOrEmpty(CounterReadingStart) && !string.IsNullOrEmpty(CounterReadingEnd) ) { // Let's start with a customer whose at least has an account number cust.AccountNumber = AccountNumber; // If a list of customers exists in a file, ... if (System.IO.File.Exists(strCustomersFile)) { // ... open that file using (FileStream fsCustomers = new FileStream(strCustomersFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { // Get the list of customers from the file and put that list in our initial empty list of customers customers = (Models.Collection<Models.Customer>)bfCustomers.Deserialize(fsCustomers); /* Since it appears that we currently have a list of customers, let's find out if * it contains a customer with the account number that was entered in the form. */ if (customers.Contains(cust)) { // If there is a customer with that number, get the index of that account int index = customers.IndexOf(cust); // Get the Customer object stored at that index Models.Customer client = customers[index]; /* Now that we have a customer with that account number, * let's prepare to send the account information to the view. */ ViewBag.MeterNumber = client.MeterNumber; ViewBag.FirstName = client.FirstName; ViewBag.LastName = client.LastName; ViewBag.Address = client.Address; ViewBag.City = client.City; ViewBag.County = client.County; ViewBag.State = client.State; ViewBag.ZIPCode = client.ZIPCode; } else { ViewBag.ErrorMessage = "There is no customer with that account number."; } } using (FileStream fsWaterMeters = new FileStream(strWaterMetersFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { Models.WaterMeter meter = new Models.WaterMeter(); waterMeters = (Models.Collection<Models.WaterMeter>)bfWaterMeters.Deserialize(fsWaterMeters); meter.MeterNumber = ViewBag.MeterNumber; if (waterMeters.Contains(meter)) { Models.WaterMeter wm = waterMeters[waterMeters.IndexOf(meter)]; ViewBag.MeterDetails = wm.MeterNumber + ": " + wm.Make + " " + wm.Model + " (" + wm.MeterSize + ")"; } else { ViewBag.ErrorMessage = "There is no water meter with that number."; } } // If there exists a file with water bills already, open that file if (System.IO.File.Exists(strWaterBillsFile)) { using (FileStream fsWaterBills = new FileStream(strWaterBillsFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { // Get the list of water bills from the file and store that list in our initial empty list waterBills = (Models.Collection<Models.WaterBill>)bfWaterBills.Deserialize(fsWaterBills); // "Scan" the list from beginning to end to get the last invoice number. foreach (var wb in waterBills) { invoiceNumber = wb.InvoiceNumber; } } } /* Whether there was a list of invoice of numbers already or not, * increase the current invoice number by 1. * And send the number number to the view. */ ViewBag.InvoiceNumber = invoiceNumber + 1; /* Let's compute the numbers for the water bill. * The following calculations are not based on anything real. * I used my own rough estimates with weird non-extensive research based on nothing. * These calculations are for demonstrative purposes only for our lesson. */ float next10HCF = 0.00f; float first15HCF = 0.00f; float remainingHCF = 0.00f; TimeSpan tsDays = new TimeSpan(); DateTime dtMeterReadingEndDate = DateTime.Parse(MeterReadingEndDate); DateTime dtMeterReadingStartDate = DateTime.Parse(MeterReadingStartDate); float fCounterReadingStart = float.Parse(CounterReadingStart); float fCounterReadingEnd = float.Parse(CounterReadingEnd); tsDays = dtMeterReadingEndDate - dtMeterReadingStartDate; float totalHCF = fCounterReadingEnd - fCounterReadingStart; int totalGallons = (int)(totalHCF * 748.05); if(totalHCF <= 15) { first15HCF = totalHCF * 3.612f; next10HCF = 0f; remainingHCF = 0f; } else if(totalHCF <= 25f) { first15HCF = 15f * 3.612f; next10HCF = (totalHCF - 15f) * 3.918f; remainingHCF = 0f; } else { first15HCF = 15f * 3.612f; next10HCF = 10f * 3.918f; remainingHCF = (totalHCF - 25f) * 2.2763f; } double waterUsageCharge = first15HCF + next10HCF + remainingHCF; double sewerCharge = waterUsageCharge * 0.252; double stormCharge = waterUsageCharge * 0.0025; double totalCharges = waterUsageCharge + sewerCharge + stormCharge; double countyTaxes = totalCharges * 0.005; double stateTaxes = totalCharges * 0.0152; double amountDue = totalCharges + countyTaxes + stateTaxes; ViewBag.TotalHCF = totalHCF; ViewBag.Next10HCF = next10HCF; ViewBag.AmountDue = amountDue; ViewBag.StateTaxes = stateTaxes; ViewBag.First15HCF = first15HCF; ViewBag.SewerCharge = sewerCharge; ViewBag.StormCharge = stormCharge; ViewBag.CountyTaxes = countyTaxes; ViewBag.NumberOfDays = tsDays.Days; ViewBag.TotalGallons = totalGallons; ViewBag.RemainingHCF = remainingHCF; ViewBag.TotalCharges = totalCharges; ViewBag.LateAmountDue = amountDue + 8.95; ViewBag.WaterUsageCharge = waterUsageCharge; ViewBag.PaymentDueDate = dtMeterReadingEndDate.AddDays(28); ViewBag.LatePaymentDueDate = dtMeterReadingEndDate.AddDays(45); } } return View(); } public ActionResult SaveWaterBill(string InvoiceNumber, string MeterReadingStartDate, string MeterReadingEndDate, string NumberOfDays, string CounterReadingStart, string CounterReadingEnd, string TotalHCF, string TotalGallons, string First15HCF, string Next10HCF, string RemainingHCF, string SewerCharges, string StormCharges, string WaterUsageCharges, string TotalCharges, string CountyTaxes, string StateTaxes, string PaymentDueDate, string AmountDue, string LatePaymentDueDate, string LateAmountDue) { BinaryFormatter bfWaterBills = new BinaryFormatter(); string strWaterBillsFile = Server.MapPath("~/App_Data/WaterBills.wbs"); Models.Collection<Models.WaterBill> waterBills = new Models.Collection<Models.WaterBill>(); if (!string.IsNullOrEmpty(InvoiceNumber)) { Models.WaterBill bill = new Models.WaterBill() { InvoiceNumber = int.Parse(InvoiceNumber), MeterReadingStartDate = DateTime.Parse(MeterReadingStartDate), MeterReadingEndDate = DateTime.Parse(MeterReadingEndDate), NumberOfDays = int.Parse(NumberOfDays), CounterReadingStart = float.Parse(CounterReadingStart), CounterReadingEnd = float.Parse(CounterReadingEnd), TotalHCF = float.Parse(TotalHCF), TotalGallons = int.Parse(TotalGallons), First15HCF = double.Parse(First15HCF), Next10HCF = double.Parse(Next10HCF), RemainingHCF = double.Parse(RemainingHCF), SewerCharges = double.Parse(SewerCharges), StormCharges = double.Parse(StormCharges), WaterUsageCharges = double.Parse(WaterUsageCharges), TotalCharges = double.Parse(TotalCharges), CountyTaxes = double.Parse(CountyTaxes), StateTaxes = double.Parse(StateTaxes), PaymentDueDate = DateTime.Parse(PaymentDueDate), AmountDue = double.Parse(AmountDue), LatePaymentDueDate = DateTime.Parse(LatePaymentDueDate), LateAmountDue = double.Parse(LateAmountDue) }; if (System.IO.File.Exists(strWaterBillsFile)) { using (FileStream fsWaterBills = new FileStream(strWaterBillsFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { waterBills = (Models.Collection<Models.WaterBill>)bfWaterBills.Deserialize(fsWaterBills); } } waterBills.Add(bill); using (FileStream fsWaterBills = new FileStream(strWaterBillsFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write)) { bfWaterBills.Serialize(fsWaterBills, waterBills); } } return RedirectToAction("WaterBillStartUp"); } } }
@{ ViewBag.Title = "Water Bill Start-Up"; } <div class="push-down"> <h2 class="text-center">Water Bill Start-Up</h2> </div> <hr /> <div align="center"> @using (Html.BeginForm("BillPreparation", "BillsProcessing", FormMethod.Post)) { <table> <tr> <td class="large-col">Account #:</td> <td>@Html.TextBox("AccountNumber", "", new { @class = "form-control medium" })</td> <td class="large-col"> </td> <td> </td> </tr> <tr> <td class="large-col">Service Start Date:</td> <td>@Html.TextBox("MeterReadingStartDate", "", new { @class = "form-control medium" })</td> <td class="ctl-deco text-right">Service to:</td> <td>@Html.TextBox("MeterReadingEndDate", "", new { @class = "form-control medium" })</td> </tr> <tr> <td class="ctl-deco">Counter Reading Start:</td> <td>@Html.TextBox("CounterReadingStart", "", new { @class = "form-control medium" })</td> <td class="ctl-deco text-right">Counter Reading End:</td> <td>@Html.TextBox("CounterReadingEnd", "", new { @class = "form-control medium" })</td> </tr> </table> <hr /> <p class="text-center"><input type="submit" name="btnCreateWaterBill" value="Create Water Bill" class="btn btn-primary xlarge" /></p> } <table> <tr> <td style="width: 120px"> </td> <td> @Html.ActionLink("New Customer", "Create", "Customers", new { @class = "navbar-brand" }) @Html.ActionLink("New Water Meter", "Create", "WaterMeters", new { @class = "navbar-brand" }) </td> </tr> </table> </div>
@{ ViewBag.Title = "Bill Preparation"; } <div class="push-down"> <h2 class="text-center">Bill Preparation</h2> </div> @{ string strCity = ViewBag.City; string strState = ViewBag.State; string strCounty = ViewBag.County; string strAddress = ViewBag.Address; string strZIPCode = ViewBag.ZIPCode; string strLastName = ViewBag.LastName; string strFirstName = ViewBag.FirstName; string meterDetails = ViewBag.MeterDetails; float totalHCF = ViewBag.TotalHCF; float next10HCF = ViewBag.Next10HCF; double amountDue = ViewBag.AmountDue; double stateTaxes = ViewBag.StateTaxes; double first15HCF = ViewBag.First15HCF; int totalGallons = ViewBag.TotalGallons; int numberOfDays = ViewBag.NumberOfDays; double sewerCharges = ViewBag.SewerCharge; double stormCharges = ViewBag.StormCharge; double countyTaxes = ViewBag.CountyTaxes; float remainingHCF = ViewBag.RemainingHCF; int invoiceNumber = ViewBag.InvoiceNumber; double totalCharges = ViewBag.TotalCharges; DateTime paymentDueDate = ViewBag.PaymentDueDate; double waterUsageCharges = ViewBag.WaterUsageCharge; double lateAmountDue = ViewBag.LateAmountDue; DateTime latePaymentDueDate = ViewBag.LatePaymentDueDate; } @using (Html.BeginForm("SaveWaterBill", "BillsProcessing", FormMethod.Post)) { <div class="highligh">Customer Account Information</div> <br /> <table> <tr> <td class="large-col">Account #:</td> <td>@Html.TextBox("AccountNumber", "AccountNumber", new { style = "width: 200px" })</td> <td> </td> </tr> </table> <table> <tr> <td class="large-col">Customer Name:</td> <td>@Html.TextBox("FirstName", @strFirstName, new { style = "width: 100px" })</td> <td>@Html.TextBox("LastName", @strLastName, new { style = "width: 100px" })</td> </tr> </table> <table> <tr> <td class="large-col"><b>Address:</b></td> <td colspan="4">@Html.TextBox("Address", @strAddress, new { style = "width: 500px" })</td> </tr> <tr> <td> </td> <td>@Html.TextBox("City", @strCity, new { style = "width: 100px" })</td> <td>@Html.TextBox("County", @strCounty, new { style = "width: 100px" })</td> <td>@Html.TextBox("State", @strState, new { style = "width: 100px" })</td> <td>@Html.TextBox("ZIPCode", @strZIPCode, new { style = "width: 100px" })</td> </tr> </table> <br /> <div class="highligh">Meter Information</div> <br /> <table> <tr> <td class="large-col">Meter Details:</td> <td>@Html.TextBox("MeterNumber", @"MeterNumber", new { style = "width: 100px" }) @Html.TextBox("MeterDetails", @meterDetails, new { style = "width: 300px" })</td> </tr> </table> <table> <tr> <td class="large-col">Service Start Date:</td> <td>@Html.TextBox("MeterReadingStartDate", "MeterReadingStartDate", new { style = "width: 100px" })</td> <td class="ctl-deco text-right">Service to:</td> <td>@Html.TextBox("MeterReadingEndDate", "MeterReadingEndDate", new { style = "width: 100px" })</td> <td class="ctl-deco text-right">Number of Days:</td> <td>@Html.TextBox("NumberOfDays", @numberOfDays, new { style = "width: 100px" })</td> </tr> <tr> <td class="ctl-deco">Counter Reading Start:</td> <td>@Html.TextBox("CounterReadingStart", @"CounterReadingStart", new { style = "width: 100px" })</td> <td class="ctl-deco text-right">Counter Reading End:</td> <td>@Html.TextBox("CounterReadingEnd", @"CounterReadingEnd", new { style = "width: 100px" })</td> <td class="ctl-deco text-right">Total HCF:</td> <td>@Html.TextBox("TotalHCF", @totalHCF.ToString("F"), new { style = "width: 100px" })</td> </tr> <tr> <td> </td> <td> </td> <td> </td> <td> </td> <td class="ctl-deco text-right">Total Gallons:</td> <td>@Html.TextBox("TotalGallons", @totalGallons, new { style = "width: 100px" })</td> </tr> <tr> <td class="ctl-deco">1st 15 HCF at $3.6121:</td> <td>@Html.TextBox("First15HCF", @first15HCF.ToString("F"), new { style = "width: 100px" })</td> <td class="ctl-deco">Next 10 HCF at $3.9180:</td> <td class="text-right">@Html.TextBox("Next10HCF", @next10HCF.ToString("F"), new { style = "width: 100px" })</td> <td class="ctl-deco">Remaining HCF at $4.2763:</td> <td>@Html.TextBox("RemainingHCF", @remainingHCF.ToString("F"), new { style = "width: 100px" })</td> </tr> <tr> <td class="ctl-deco">Sewer Charges:</td> <td>@Html.TextBox("SewerCharges", @sewerCharges.ToString("F"), new { style = "width: 100px" })</td> <td class="ctl-deco text-right">Storm Charges:</td> <td class="text-right">@Html.TextBox("StormCharges", @stormCharges.ToString("F"), new { style = "width: 100px" })</td> <td class="ctl-deco text-right">Water Usage Charges:</td> <td>@Html.TextBox("WaterUsageCharges", @waterUsageCharges.ToString("F"), new { style = "width: 100px" })</td> </tr> <tr> <td></td> <td> </td> <td> </td> <td> </td> <td class="ctl-deco text-right">Total Charges:</td> <td>@Html.TextBox("TotalCharges", @totalCharges.ToString("F"), new { style = "width: 100px" })</td> </tr> </table> <br /> <div class="highligh">Water Bill Values</div> <br /> <table> <tr> <td class="xlarge-col text-right">County Taxes:</td> <td>@Html.TextBox("CountyTaxes", @countyTaxes.ToString("F"), new { style = "width: 100px" })</td> <td class="large-col text-right">State Taxes:</td> <td>@Html.TextBox("StateTaxes", @stateTaxes.ToString("F"), new { style = "width: 100px" })</td> </tr> </table> <hr /> <table> <tr> <td class="xlarge-col text-right">Payment Due Date:</td> <td>@Html.TextBox("PaymentDueDate", @paymentDueDate.ToShortDateString(), new { style = "width: 100px" })</td> <td class="large-col text-right">Amount Due:</td> <td>@Html.TextBox("AmountDue", @amountDue.ToString("F"), new { style = "width: 100px" })</td> </tr> <tr> <td class="xlarge-col text-right">Late Payment Due Date:</td> <td>@Html.TextBox("LatePaymentDueDate", @latePaymentDueDate.ToShortDateString(), new { style = "width: 100px" })</td> <td class="large-col text-right ">Late Payment Amount:</td> <td>@Html.TextBox("LateAmountDue", @lateAmountDue.ToString("F"), new { style = "width: 100px" })</td> </tr> </table> <hr /> <table> <tr> <td class="ctl-deco">Invoice #:</td> <td>@Html.TextBox("InvoiceNumber", @invoiceNumber, new { style = "width: 100px" })</td> <td class="xlarge-col"> </td> <td class="text-right"><input type="submit" name="btnSaveWaterBill" value="Save Water Bill" style="width: 300px" /></td> </tr> </table> }
Account # | 1848-205-3313 |
Service Start Date | 1/12/2018 |
Service To | 4/17/2018 |
Meter Reading Start | 274.68 |
Reading End | 316.53 |
Account # | 2958-314-5294 |
Service Start Date | 1/18/2018 |
Service To | 4/17/2018 |
Meter Reading Start | 5827.59 |
Reading End | 5883.17 |
Account # | 4024-850-0482 |
Service Start Date | 4/14/2018 |
Service To | 7/6/2018 |
Meter Reading Start | 1436.64 |
Reading End | 1454.86 |
Items Maintenance in a Collection
Editting an Item
Editing an item consists of changing its value or one of its details. The IList interface doesn't provide its own method to do this. The reason is that it depends on the item and what you are trying to do. For example, if the collection is made of value types or strings, it is easy to change the value. If the collection is made of objects, then you must decide what value of a property you want to change, and this can be unpredictable. As done in formal database, the best steps to follow are:
Deleting a Value by its Index
If a value is not necessary in your collection, you can delete it. Probably the simplest way to delete a value is to specify its position in the list. To support this operation, both the System.Collections.IList and the System.Collections.Generic.IList interfaces are equipped with a method named RemoveAt. The syntax of the RemoveAt() method is the same for both interfaces and it is:
void RemoveAt(int index);
This method takes as argument the index of the value to be removed. If the index is valid, the method removes the item at that position. If the index is not valid, the compiler would throw an ArgumentOutOfRangeException exception. Here is an example:
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
. . .
// This method is used to delete an item based on its position
public void RemoveAt(int index)
{
// First check that the user provided a positive index that
// is lower than the current total number of items
if ((index >= 0) && (index < size))
{
// If so, starting at that index, get the item ahead of
// each position and assign it to the current item
for (int i = index; i < size - 1; i++)
items[i] = items[i + 1];
// Since the last item is not useful anymore,
// decrease the number of items
size--;
}
}
}
If the item cannot be deleted for another reason, the compiler would throw a NotSupportedException exception.
Deleting an Item by its Value
The above method could deleting the wrong value or object if you provide the wrong index. An alternative is to first precisely define the value or the object you want to get rid of, and then hand the value or object to the compiler that would remove it. To support this approach, the System.Collections.IList interface is equipped with a method named Remove. Its syntax is:
bool Remove(T item);
This method takes as argument the value to be deleted. This means that the compiler will first look for the value in the list. If it finds that value, it removes it. If there is no value like that, nothing would happen. Here is an example of implementing this method:
public class Itemization<T> : IList<T>
{
private int size;
private T[] items;
. . .
// This method is used to delete an item
public bool Remove(T value)
{
// First try to get the index of the item to find out if it exists
int index = IndexOf(value);
// If the item was found ...
if (index >= 0)
{
// ... delete it, using its index
RemoveAt(index);
// Since the item has been removed, return true
return true;
}
// If the item was not removed (for any reason), return false
return false;
}
}
Clearing a Collection
To let you remove all values or objects from a list, the IList<> interface is equipped with a method named Clear. Its syntax is:
void Clear();
Here is an example of implementing it:
public class Itemization<T> : IList<T> { private int size; private T[] items; public Itemization() { size = 0; items = new T[10]; } public void CopyTo(T[] array, int arrayIndex) { T[] values = new T[size]; for (int i = 0; i < size; i++) values[i] = items[i]; array = values; } public int Count { get { return size; } } public bool IsReadOnly { get { return false; } } private void CheckAndIncreaseIfNecessary() { if (size >= items.Length) Array.Resize<T>(ref items, items.Length + 5); } // The index property can be used to add an item to a collection, // or to retrieve an item from the collection public T this[int index] { get { return this.items[index]; } set { this.items[index] = value; } } public void Add(T value) { // Check whether there is still room in the array. // If there is not enough room, then increase the capacity CheckAndIncreaseIfNecessary(); // Add the item at the end this.items[this.size] = value; // Increase the current number of items this.size++; } public void Insert(int index, T value) { // If the index is the same as the current number of items // then call Add() and proceed as if we want to add a new item if (index >= size) { Add(value); return; } // If the index is positive but less than the current size, // then you need to insert the item inside the collection. if ((index >= 0) && (index < size)) { // Firs, push each item one position up to create // an empty space for (int i = (size - 1); i > index - 1; i--) items[i + 1] = items[i]; // Then put the new item in the indicated position items[index] = value; // Since the new item has been added, increase the // current number of items size++; } } public bool Contains(T value) { for (int i = 0; i < Count; i++) if (items[i].Equals(value)) return true; return false; } public int IndexOf(T value) { for (int i = 0; i < Count; i++) if (items[i].Equals(value)) return i; return -1; } // This method is used to delete an item based on its position public void RemoveAt(int index) { // First check that the user provided a positive index that // is lower than the current total number of items if ((index >= 0) && (index < size)) { // If so, starting at that index, get the item ahead of // each position and assign it to the current item for (int i = index; i < size - 1; i++) items[i] = items[i + 1]; // Since the last item is not useful anymore, // decrease the number of items size--; } } // This method is used to delete an item public bool Remove(T value) { // First try to get the index of the item to find out if it exists int index = IndexOf(value); // If the item was found ... if (index >= 0) { // ... delete it, using its index RemoveAt(index); // Since the item has been removed, return true return true; } // If the item was not removed (for any reason), return false return false; } // This method is used to delete all items from a collection public void Clear() { // Visit each item in the collection and set it to the default value // (This is not really necessary) for (int i = 0; i < size; i++) items[i] = default(T); // Reset the number of items of the collection to 0 size = 0; } public IEnumerator<T> GetEnumerator() { int counter = 0; while (counter < Count) { yield return items[counter]; counter++; } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { int counter = 0; while (counter < Count) { yield return items[counter]; counter++; } } }
Practical Learning: Ending the Lesson
|
||
Previous | Copyright © 2008-2019, FunctionX | Next |
|