Wednesday, November 01, 2017

StackOverflow is starting to suck..

And so is the whole cyberspace of help with technical or programming problems.

Take this example :

https://stackoverflow.com/questions/35204499/hangfire-get-last-execution-time

The original poster asked a simple question about how to get the last run time of a job in the wonderful tool called Hangfire.

The problem is a challenge, because within the job code, the date is already calculated and saved upon the job. So you can't get the prior run time.

The responder suggested simply decrementing an integer counter for jobs within Hangfire and reading that job's run time. That only works in the case where ONLY ONE job exists in the tool. And nobody is going to use Hangfire to do just one thing, in most cases. That's a heavy way to get something done.

Now I tried to reply to this on StackOverflow, only to be told I don't have enough reputation points.

This is the chicken and egg syndrome big time that the internet is supposed to help stop. Stop denying people opportunity just because they haven't done something before.

The rest of the internet is chock full of crappy answers to this, and other problems in general. Some try to charge for those crappy answers.

For example, this guy is also dead wrong about how this works :

http://www.niceonecode.com/Q-A/DotNet/MVC/HangFire-get-last-and-next-execution-datetime/20439

Thanks


Monday, July 10, 2017

ANSI NULLS what?

Most people working with SQL Server are probably up to date on this; but I figure it is worth mentioning.

SQL Server treats NULL values in some un-intuitive ways.

Consider this in my 'Where' clause :



code NOT IN (32,37,33,39,40,41,47,48,83,94,93,95,501)


I would think this automatically means, anything that is not one of these numbers, including any NULL. However, that is not how it works. It ignored NULL, removing valuable data from the returned data, so I had to add an explicit inclusion of that potential table value :



code NOT IN (32,37,33,39,40,41,47,48,83,94,93,95,501) OR code IS NULL


Microsoft has said, all future versions of SQL Server will use ANSI NULLS ON with no option to turn it on or off. So what? You might ask. Well, this means that " When SET ANSI_NULLS is ON, a SELECT statement that uses WHERE column_name = NULL returns zero rows even if there are null values in column_name"

The best guidance I found in the MSDN article was " For a script to work as intended, regardless of the ANSI_NULLS database option or the setting of SET ANSI_NULLS, use IS NULL and IS NOT NULL in comparisons that might contain null values."

https://docs.microsoft.com/en-us/sql/t-sql/statements/set-ansi-nulls-transact-sql

My takeaway is just to be sure to specify in every query what SQL Server should do when encountering a NULL. 

Friday, March 17, 2017

Twitter from C# .NET MVC..

 I wrote the Stock tracker application as mostly just a learning drill for Angular 1 and 2.
But I have found, it could be actually useful, with a touch of Twitter.

I find myself searching Twitter for charting,analysis, links, and just plain prognosticating by people, by searching the stock symbol preceded by "$".

So I set out to add some Twitter links to my site today.
There was a ton of helpful documentation by others already on the internet. Problem was the format and varying usage needs. I really needed to get the grunt work done in C# .NET. Some of the Gurus are using Node.js and other things, and some others were just making it harder than it needed for me to be.

What I wanted to end up with, and now have looks like this :




So the user can click the bird and see what has been tweeted about a company stock.

I would offer up that the heavy lifting is done by getting an OAuth token from Twitter first, and post that code for reference sake :

public string GetAuthToken()
        {

            string encodedKeyAndSecret = Convert.ToBase64String(
                new System.Text.UTF8Encoding().GetBytes(
                  ConsumerKey + ":" + ConsumerSecret));

            string urlToken = "https://api.twitter.com/oauth2/token";

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(urlToken));

            req.Headers.Add("Authorization","Basic " + encodedKeyAndSecret);
              
            req.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
            req.Method = "POST";

            string requestBody = "grant_type=client_credentials";

            Stream dataStream = req.GetRequestStream();

            byte[] byteArray = new System.Text.UTF8Encoding().GetBytes(requestBody);
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            WebResponse response = req.GetResponse();

            string jsonResponse = "";

            using (var reader = new StreamReader(response.GetResponseStream()))
            {
               jsonResponse = reader.ReadToEnd();
            }

            TokenResponse t = 
                Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse);
      
            return t.access_token;

        }

The rest of the act of querying Twitter is super easy to code, but can take some to get working, deserializing to something useable in your app, and then modeled by your classes and client-side objects.


Tuesday, March 07, 2017

ASP .NET MVC Angular 2, what I have so far..

So I set out to upgrade the first project of this kind which was written using Angular 1. My first few attempts with downloadable samples, were of the new MVC "wwwroot" Core format. These either did not run for me, or I could not see how to learn what I needed to learn using them.

So I had to start humble and create a traditional ASP .NET project and add MVC functionality to it. I figured this would be a good way to be able to download and learn from the quickstart at :

https://angular.io/docs/ts/latest/cookbook/visual-studio-2015.html

I got some help from the internet with adding MVC "areas" to my project, so as to add MVC functionality, but not lose the traditional ASP application behaviors.

I don't remember where exactly I picked up the "AREAS" education, but the topic is heavily explained :
https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=how+to+add+MVC+areas&*

Jumping to the end, my new stock tracker application runs at :
https://www.customconfiguration.net/ng/Stocks/StocksAngular


New users would want to be logged into Twitter for easy signup, or create a new account to use the app.

Forgiving my choice of traditional (Non-WebApi) controllers for a second, it only seems useful to look at a couple of sample methods :

   
     [Authorize()]
        [AcceptVerbs(HttpVerbs.Get)]
        public JsonResult ReadStockQuote(string Id)
        {
            Quote q;

            if (Id.Length > 0)
            {
                q = getQuoteFromYahoo(Id);
            }
            else
            {
                q = new Quote();
            }

            return Json(q, JsonRequestBehavior.AllowGet);
        }


        [Authorize()]
        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(Duration = 360, VaryByParam = "Id")]
        public JsonResult ReadPrice(string Id)
        {
            string lastTrade = string.Empty;
            string color = string.Empty;
            string error = string.Empty;

            try
            {
                Quote q = getQuoteFromYahoo(Id);

                lastTrade = "$" + q.LastTrade;
                if (q.PercentChange.Contains("-"))
                {
                    color = "Red";
                }
                else
                {
                    color = "Green";
                }

            }
            catch (System.Exception exception)
            {
                lastTrade = "error";
                error = exception.Message;
            }

            return Json(
             new Stock()
             {
                 Price = lastTrade,
                 Color = color,
                 //                Message = error,
                 Symbol = Id
             },
             JsonRequestBehavior.AllowGet);
        }

So the code for the controllers just does basic Add, Delete, Get, And some RSS feed reading from Yahoo's generous web services.

Following the example provided at the Angular website, I created and "app" folder with "components" and "services" folders included> Everything prescribed in the tutorial, I copied into my project unchanged.

I then built the app I wanted following the example where relevant. I jumped around to get ahead to Routing, because I really wanted that feature even though my small app could certainly have existed in one view.

My main view for the single page app, just has one selector tag, along with the referenced scripts in the head tag :
 <base href="@Url.Content("~")">

 @Styles.Render("~/Content/css")

         <!-- Polyfill(s) for older browsers -->
         <script src="~/node_modules/core-js/client/shim.min.js"></script>
             <script src="~/node_modules/zone.js/dist/zone.js"></script>
 <script src="~/node_modules/systemjs/dist/system.src.js"></script>
 <script src="~/systemjs.config.js"></script>
 <script>

 System.
import('main.js')
.
catch(function (err) { console.
error(err); });

</script>



<stocks>Loading AppComponent content here </stocks>



The app Module script file has been updated from the example with my components :

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule, JsonpModule } from '@angular/http';
import { Http } from '@angular/http';
import { Router } from '@angular/router';

import { AppServiceStocks } from './services/app.service.stocks';
import { AppStocks } from './components/app.component.stocks';
import { AddComponent } from './components/app.component.stockadd';
import { RemComponent } from './components/app.component.stockrem';
import { NewsComponent } from './components/app.component.stocknews';
import { AppComponent } from './components/app.component';

import { PageNotFoundComponent } from './components/not-found.component';
import { AppRoutingModule } from './app-routing';

@NgModule({
    imports: [BrowserModule, HttpModule, FormsModule, AppRoutingModule],
    declarations: [
        AppComponent,
        AppStocks,
        AddComponent,
        PageNotFoundComponent,
        RemComponent,
        NewsComponent],
    bootstrap: [AppComponent]
})
export class AppModule {

    // Diagnostic only: inspect router configuration
    constructor(router: Router) {
       // console.log('Routes: ', JSON.stringify(router.config, undefined, 2));
    }
}



The app Routing script file also was built from tutorial and just fleshed out with my own paths :


import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AppComponent } from './components/app.component';
import { AppStocks } from './components/app.component.stocks';
import { AddComponent } from './components/app.component.stockadd';
import { RemComponent } from './components/app.component.stockrem';
import { NewsComponent } from './components/app.component.stocknews';

import { PageNotFoundComponent } from './components/not-found.component';

// import { CanDeactivateGuard } from './can-deactivate-guard.service';
// import { AuthGuard } from './auth-guard.service';

import { SelectivePreloadingStrategy } from './components/selective-preloading-strategy';

const appRoutes: Routes = [
    {
        path: 'Stocks/StocksAngular/Add',
        component: AddComponent
    },
    {
        path: 'Stocks/StocksAngular/Delete/:id',
        component: RemComponent
    },
    {
        path: 'Stocks/StocksAngular/News/:id',
        component: NewsComponent
    },
    {
        path: 'Stocks/StocksAngular',
        component: AppStocks
    },    
    { path: '', redirectTo: 'Stocks/StocksAngular', pathMatch: 'full' },
    { path: '**', component: PageNotFoundComponent }
];

@NgModule({
    imports: [
        RouterModule.forRoot(
            appRoutes,
            { preloadingStrategy: SelectivePreloadingStrategy }
        )
    ],
    exports: [
        RouterModule
    ],
    providers: [
   //     CanDeactivateGuard,
        SelectivePreloadingStrategy
    ]
})
export class AppRoutingModule { }


It is important to remember to not allow MVC to interfere with Angular's routing :
In the RouteConfig file place something like -

  routes.MapRoute(
              name: "ngOverride",
              url: "Stocks/StocksAngular/{*.}",
              defaults: new { controller = "Stocks", action = "StocksAngular" }
            );


The app service script file is called app.service.stocks.js :

import { Injectable } from '@angular/core';
import { Http, Response, URLSearchParams, RequestOptions, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { stock } from '../components/stock';
import { rssitem } from '../components/rssitem';
import { quote } from '../components/quote';

@Injectable()
export class AppServiceStocks {

    private _getStocksListUrl = 'Stocks/StocksJSON';
    private _getStockDetailUrl = "Stocks/ReadStockQuote?Id=";
    private _getPriceUrl = "Stocks/ReadPrice?Id=";
    private _getNameUrl = "Stocks/GetNameFromSymbol?Id=";
    private _getNewsUrl = "http://feeds.finance.yahoo.com/rss/2.0/headline?s=";

    private _deleteStockUrl = "Stocks/Remove";
    private _addStockUrl = "Stocks/AddJSON";

    private _stockslist: stock[];

    constructor(private http: Http) {
    }

    stockslist(): Observable {
        return this.http.get(this._getStocksListUrl)
            .map(this.extractData)
            .catch(this.handleError);
    }

    stockDetail(symbol : string): Observable {
        return this.http.get(this._getStockDetailUrl + symbol)
            .map(this.extractData)
            .catch(this.handleError);
    }

    getNameFromSymbol(symbol: string): Observable {
        return this.http.get(this._getNameUrl + symbol)
            .map(this.extractData)
            .catch(this.handleError);
    }

    readPrice(symbol: string): Observable {
        return this.http.get(this._getPriceUrl + symbol)
            .map(this.extractData)
            .catch(this.handleError);
    }

    readNews(symbol: string): Observable {
        var newsLink = this._getNewsUrl
            + symbol
            + "&region=US&lang=en-US";

        let params: URLSearchParams = new URLSearchParams();
        params.set('Link', newsLink);
    
        return this.http.get("Stocks/ReadNewsData?",
            { search: params })
            .map(this.extractData)
            .catch(this.handleError);
    }

    readYahooNews(symbol: string): Observable {
       
        let params: URLSearchParams = new URLSearchParams();
        params.set('Symbol', symbol);

        return this.http.get("Stocks/ReadYahooNewsData",
            { search: params })
            .map(this.extractData)
            .catch(this.handleError);
    }

    remove(s : stock) : Observable {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this.http.post(this._deleteStockUrl, { item : s }, options)
            .map(this.extractData)
            .catch(this.handleError);
    }

    add(s: string): Observable {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this.http.post(this._addStockUrl, { Symbol : s }, options)
            .map(this.extractData)
            .catch(this.handleError);
    }

    private handleError(error: Response | any) {
        // In a real world app, we might use a remote logging infrastructure
        let errMsg: string;
        if (error instanceof Response) {
            const body = error.json() || '';
            const err = body.error || JSON.stringify(body);
            errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
        } else {
            errMsg = error.message ? error.message : error.toString();
        }
        console.error(errMsg);
        return Observable.throw(errMsg);
    }

    private extractData(res: Response) {
        let body = res.json();
        return body || {};
    }
The main controller, the Stocks list controller, which rotates down the list getting the current prices from Yahoo : I think this came out cleaner to code than the Angular 1 version of it.
import { Component } from '@angular/core';
import { stock } from './stock';
import { quote } from './quote';
import { Observable } from 'rxjs/Observable';
import { AppServiceStocks } from '../services/app.service.stocks';

@Component({
    selector: 'stocks',
    templateUrl: './app/components/app.component.stocks.html?v=4',
    providers: [AppServiceStocks]
})

export class AppStocks {

    name = 'Angular Stocks';
    stockslist: stock[];
    mode = 'Observable';
    statusMessage = "";
    newStockName = "";
    newStockSym = "";
    newStock = new stock();
    rownum = 0;
    interval = 2;
    
    constructor(private _appService: AppServiceStocks) {
        
    }

    ngOnInit() {
        this.newStockName = "";
        this.getStocks();
    }

    private readPrice() {
        this._appService.readPrice(this.stockslist[this.rownum].Symbol)
            .subscribe(result => {
                this.stockslist[this.rownum].Price = result.Price;
                this.stockslist[this.rownum].Color = result.Color;
                this.rownum++;
                if (this.rownum == this.stockslist.length) {
                    this.rownum = 0;
                }
                this.getNextQuote();
            });
    }

    private getNextQuote() {
      setTimeout(() => { this.readPrice() }, this.interval * 1000);
    }

    getStocks() {
        this._appService.stockslist()
            .subscribe(
            stocks =>
            {
                this.stockslist = stocks;
                this.getNextQuote();
            });
    }

    

}