Refactoring is an important process to crafting readable and maintainable code. Functions should follow the single responsibility principle – a function should do one thing and one thing only and should have few lines of code.
Like with everything, you should apply this with measure and wisdom.
I was asked to write a function that would read stock sales at a particular time, and read financial information from Yahoo Finance, then dump all the data into an excel file.
Quick and dirty was the need, but now I will go back and refactor it as this function as I will be using it for other projects.
This function reads the stocks from the database – used linq for that – parses the URL, make a bunch of calls to Yahoo Finance and reads the results – including if the stock ticker was found or not, displays the results on a grid.
The Highlighted code, gets the database information and dumps it into a IQueryable collection. This functionality should be somewhere else, I will create a method called ReadStockData, this function that returns a list with stock data values.
private void btnStockSales_Click(object sender, RoutedEventArgs e) { TickerDataCollection = new ObservableCollection(); LinqFiles.StockSalesDataContext stockContext = new LinqFiles.StockSalesDataContext(); var stocks = from sales in stockContext.vwStockSales orderby sales.TICKER select new { sales.TICKER, sales.DATE }; var stockList = stocks.Distinct(); List results = new List(); foreach (var item in stockList) { int month = DateTime.Parse(item.DATE.ToString()).Month; month -= 1; string url = "http://finance.yahoo.com/q/hp?s"+ item.TICKER + "&a=" + month.ToString() + "&b=" + DateTime.Parse(item.DATE.ToString()).Day.ToString() + "&c=" + DateTime.Parse(item.DATE.ToString()).Year.ToString() + "&d=" + month.ToString() + "eb=" + DateTime.Parse(item.DATE.ToString()).Day.ToString() + "&f=" + DateTime.Parse(item.DATE.ToString()).Year.ToString() + "&g = d"; TickerData x = new TickerData(); x.Ticker = item.TICKER; Scraper y = new Scraper(); x = y.Scraping(url); if (x == null) { x = new TickerData(); x.Found = false; } else { x.Found = true; } x.Ticker = item.TICKER; x.TransactionDate = item.DATE.Value; TickerDataCollection.Add(new TickerData() { Ticker = x.Ticker, High = x.High, Low = x.Low, Found = x.Found, TransactionDate = x.TransactionDate }); } LstStocks.DataContext = TickerDataCollection; }
Creating a new method will require some work. Linq statements return anonymous types, and your will likely get an error such as
Cannot implicitly convert type ‘System.Linq.IQueryable<AnonymousType#1>’ to ‘System.Collections.Generic.IEnumerable
To avoid this you need the following steps:
Create an extra class – it makes your code simple and extensible, and the sensible thing to do.
public class StockData { public string Ticker { get; set; } public DateTime? Date { get; set; } }
Notice that the variable date is defined as nullable, as to follow the same definition on the database.
Now you need to change your Linq statement to create an instance of your StockData object
var stocks = from sales in stockContext.vwStockSales orderby sales.TICKER select new StockData() { Ticker = sales.TICKER, Date = sales.DATE };
This Linq statement now returns a list of StockData, making your call extensible on your code.
Here is the implementation of the refactored method ReadStockData
private static List&amp;amp;amp;amp;lt;StockData&amp;amp;amp;amp;gt; ReadStockData() { LinqFiles.StockSalesDataContext stockContext = new LinqFiles.StockSalesDataContext(); var stocks = from sales in stockContext.vwStockSales orderby sales.TICKER select new StockData() { Ticker = sales.TICKER, Date = sales.DATE }; return stocks.Distinct().ToList(); ; }
We encapsulate how the data is read, any changes on how the data is loaded, does not affect the rest of your function.
Finally, here is how the initial function looks like, notice the changes on the variable names from TICKER and DATE (from Linq) to Ticker and Date from the StockData object.
private void btnStockSales_Click(object sender, RoutedEventArgs e) { TickerDataCollection = new ObservableCollection(); // no longer care where the data is coming, we just get the data var stockList = ReadStockData(); List results = new List(); foreach (var item in stockList) { int month = DateTime.Parse(item.Date.ToString()).Month; month -= 1; string url = &amp;amp;amp;amp;amp;quot;http://finance.yahoo.com/q/hp?s=&amp;amp;amp;amp;amp;quot; + item.Ticker+ &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;a=&amp;amp;amp;amp;amp;quot; + month.ToString() + &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;b=&amp;amp;amp;amp;amp;quot; + DateTime.Parse(item.Date.ToString()).Day.ToString() + &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;c=&amp;amp;amp;amp;amp;quot; + DateTime.Parse(item.Date.ToString()).Year.ToString() + &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;d=&amp;amp;amp;amp;amp;quot; + month.ToString() + &amp;amp;amp;amp;amp;quot;eb=&amp;amp;amp;amp;amp;quot; + DateTime.Parse(item.Date.ToString()).Day.ToString() + &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;f=&amp;amp;amp;amp;amp;quot; + DateTime.Parse(item.Date.ToString()).Year.ToString() + &amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;g = d&amp;amp;amp;amp;amp;quot;; TickerData x = new TickerData(); x.Ticker = item.TICKER; Scraper y = new Scraper(); x = y.Scraping(url); if (x == null) { x = new TickerData(); x.Found = false; } else { x.Found = true; } x.Ticker = item.Ticker; x.TransactionDate = item.Date.Value; TickerDataCollection.Add(new TickerData() { Ticker = x.Ticker, High = x.High, Low = x.Low, Found = x.Found, TransactionDate = x.TransactionDate }); } LstStocks.DataContext = TickerDataCollection; }
Next post we will continue to refactor this mess.
2 thoughts on “Refactoring I -Convert Linq Anonymous Types to List”