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();
            });
    }

    

}

Saturday, October 29, 2016

Migrating Asp .Net MVC to .Net Core .. Authentication

Hi all, long time no post...

I am learning the new .NET CORE project type now, and want to rewrite my application from the old ASP .NET MVC in the new project template. Reasons for this for me, are really just that my old project is AngularJS 1.0, and I want to try to rebuild using Angular 2.0 with Typescript.

So one stumbling block I hit was the Identity and Authentication database from the old project would not work with the new project. It gave some errors about certain columns missing. What I have had some partial success with is a migration script to update the old DB. This has allowed me to login using a user name and password, and to create a new user. That is the extent of my testing :


Alter Table ASPNETROLES
ADD
 ConcurrencyStamp varchar(255) null,              
 NormalizedName varchar(255) null

 Drop Table AspNetUserTokens

 CREATE TABLE [AspNetUserTokens] (
    [UserId]        NVARCHAR (450) NOT NULL,
    [LoginProvider] NVARCHAR (450) NOT NULL,
    [Name]          NVARCHAR (450) NOT NULL,
    [Value]         NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_AspNetUserTokens]
PRIMARY KEY CLUSTERED ([UserId] ASC, [LoginProvider] ASC, [Name] ASC)
)

Alter Table AspNetUsers
 Add
 ConcurrencyStamp varchar(255) null,
 LockoutEnd DateTime null,
 NormalizedEmail varchar(255) null,
 NormalizedUserName varchar(255) null

Drop Table [AspNetRoleClaims]

CREATE TABLE [AspNetRoleClaims] (
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [ClaimType]  NVARCHAR (MAX) NULL,
    [ClaimValue] NVARCHAR (MAX) NULL,
    [RoleId]     NVARCHAR (128) NOT NULL,
    CONSTRAINT [PK_AspNetRoleClaims]
PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId]
FOREIGN KEY ([RoleId])
REFERENCES [dbo].[AspNetRoles] ([Id]) ON DELETE CASCADE
)


GO
CREATE NONCLUSTERED INDEX [IX_AspNetRoleClaims_RoleId]
    ON [AspNetRoleClaims]([RoleId] ASC)

Alter Table AspNetUserLogins
   Add  ProviderDisplayName varchar(255) null





You may find this discussion relevant if you have gotten this error :
SqlException: Invalid column name 'NormalizedUserName'. Invalid column name 'ConcurrencyStamp'. Invalid column name 'LockoutEnd'. Invalid column name 'NormalizedEmail'. Invalid column name 'NormalizedUserName'.

Monday, June 02, 2014

ASP. Net site with many Update Panels?

So you have an ASP. Net site with many Ajax Update Panels? The problem : trying to do client side javascript interactions when the page keeps posting back partial. The scripts need to run after the partial page returns, so they are not overridden by the back end.
The solution:
Capture the page upon return with the following:
            var sysApplication = Sys.WebForms.PageRequestManager.getInstance();
            sysApplication.add_initializeRequest(beginRequest);
            sysApplication.add_endRequest(endRequest);
 
            function beginRequest() {
               // document.getElementById("divMessage").style.display = "inline";
              
            }
 
            function endRequest() {
                setWizardProgress();
            }      

Saturday, March 16, 2013

GOOGLE Reader is going away :-(

Well, I am sorry to see Google Reader retire this June. I love it at work. I can read the non-blocked part of the websites I enjoy. And I can take in a lot of information without having to navigate all the sites. For now, I am running my own RSS Reader page at the following URL : http://www.customConfiguration.net/MVC/ Here is a picture of what I have built so far : Anybody who wants to, can register for my page, and I will save his/her favorite feeds list in a SQL Server database I am renting from DiscountASP.Net. I don't anticipate alot of traffic, if any besides myself. That said, the page just persists the list of feeds for the user, not the actual RSS postings.

Thursday, January 31, 2013

Knockout Web API SPA

This is just to explore the new technologies in the Microsoft .Net and Javascript communities. The Web API allows for a lightweight web services tier, while Knockout provides productivity in the browser side. Most of this technique I found in tutorials on www.asp.net and other like sites. I am not feigning originality here.
I am describing the building of a SPA - Single Page Application.
I have built a quick application which performs CRUD operations on the single table using Knockout, Web API, and NHibernate data layer.
Here is the browse data view of the application. There are links to edit, delete and add new items. Also it does paging, because the table is large.

Here is the Edit data view :


How is it built?

A standard MVC project is created in C#, selecting the WEB API project template along the way. A Project Reference is added to the data layer project.


There are 3 main components needed to get a UI running : a Controller class, a Javascript file, and an HTML View file.


The HTML View


References to the various script libraries are added

It is easiest is to build the View and then animate it with code. The View I have above contains edit and list sections which are hidden and shown alternatively, using the JQuery “Hide” and “Show” commands. Just to explain the syntax, here is the relevant part of the Edit section :
And the pertinent code for the List section :
The edit and delete links are accomplished with Knockout Bindings as shown here:

The data paging functionality is also done with Knockout bindings :
The Scripting
Knockout implements the ViewModel part of the MVVM architecture which means we have to code this in the javascript for the html page. Here is my example ViewModel which works in the single page application :
This ViewModel contains objects for the single item and the list of those items.
The Controller code
This is a class which implements the API methods to return data for Knockout to display. The WEB API uses convention to determine which method to execute. Objects are posted intact to the methods, and parameters passed with names are converted to querystring. If passed without name to a GET function, then the value just becomes part of the path "API/THING/1/" for example. Posts and Deletes are translated into methods with those names.

Wednesday, January 16, 2013

Windows 8 RSS Reader Continued

All I added to the template provided by Visual Studio is here : In the HTML document, I did very little except add a ListView, and a WinJS Binding Template for items in the list. I added an Article element for the Blog entry detail pane. The code was added to the existing JS file provided as below :
    // To get the data from the website
                getFeeds(blogPosts, null); 

    // To expose the data to the page.
                var publicMembers =
                    { blogItems: blogPosts };

                WinJS.Namespace.define("DataRSS", publicMembers);

   // To finish setting up the page for view
    args.setPromise(
                WinJS.UI.processAll()
                .done(function () {

   // Add event handlers for the controls
                    var button1 = document.getElementById("bttnMore");
                    button1.addEventListener("click", buttonMoreClick, false);


                   var myList = document.getElementById("myRSSItems");
                   myList.addEventListener("selectionchanged", _selectionChanged);
                }));

Then there is specific acquire and data parsing code for the blogs. First get the data, saving the most recent post date into a variable:
function getFeeds(blogs, after) {

    // To Get more items need to login.
    // // http://www.google.ca/reader/atom/feed/http://forums.corvetteforum.com/external.php?type=RSS2&n=50

    var urlString =
        "http://forums.corvetteforum.com/external.php?type=RSS2";

    var today = new Date();

    today.setDate(today.getDate() - 1);

    var dataPromise = acquireSyndication(urlString, today.toLocaleString());
    
    dataPromise.done(
        function completed(articlesResponse) {

            var lastDate = null;

            lastDate = getPostsAfter(articlesResponse, blogs, after);

            var lastToday = new Date(lastDate);

            // populate a SPAN on the page with the recent date.
            spLastPub.innerText = lastToday.toLocaleString();
        });

}

The RSS feed won't give but the most recent 15 items unless you login to Google
function getPostsAfter(xmlDoc, blogs, afterDate) {

    var articleSyndication = xmlDoc.responseXML;

    if (afterDate != null) {
        var aftDate = new Date(afterDate);
    }

    var posts = articleSyndication.querySelectorAll("item");

    var lastPublished = null;

    for (var postIndex = 0; postIndex < posts.length; postIndex++) {
        // debugger;

        var postTitle = posts[postIndex].querySelector("title").textContent;
        var postLink = posts[postIndex].querySelector("link").textContent;
        var description = posts[postIndex].querySelector("description").textContent;
        var published = posts[postIndex].querySelector("pubDate").textContent;
        var contentEncoded = posts[postIndex].querySelector("encoded").textContent;

        if (postIndex == 0) {
            lastPublished = published;
        }

        var compareDate = new Date(published);

        if (afterDate == null ||
            compareDate > aftDate) {
            blogs.push({
                title: postTitle,
                subtitle: postLink,
                desc: description,
                pubDate: published,
                content: contentEncoded
            });
        }

    }

    return lastPublished;
}
The function for acquiring the feed asynchronously was generously provided in the tutorial, so I used it verbatim :
function acquireSyndication(url, modified) {

    return WinJS.xhr(
        {
            url: url,
            headers: { "If-Modified-Since": modified }
        });

}
The only way to get full coverage of the blog without the login overhead, was to provide a button which would update the list with any new entries since the original opening of the page :
 function buttonMoreClick(arg) {

        getFeeds(blogPosts, spLastPub.innerText);
    }
The Microsoft article is here - http://msdn.microsoft.com/en-us/library/windows/apps/jj663506.aspx

Tuesday, January 15, 2013

My First Windows 8 Store application

I am learning to write code in Windows 8 and wanted an application for reading CorvetteForum.com. While there are other products for reading RSS feeds, I wanted a full application for just this one feed. Here are some screen shots of the user interface :
This app is from the Visual Studio 2012 project template Javascript - Windows Store - Blank Application.
There is a lengthy tutorial about how to write a full blog reader, however I wanted to learn the parts without just pasting a huge code volume and not having time to read it all. I wanted to do alot of my own coding.

Friday, October 19, 2012

ASP .NET MVC 3 RAZOR Dropdownlist Ajax AutoPostback

ASP .NET MVC 3 RAZOR Dropdownlist Ajax AutoPostback
Wow, what a mouthful. I am a bit sick of StackOverflow's policy for today.
So here it is : I want a dropdown list to cause submission of my Ajax form to retrieve some JSON formatted results for use elsewhere in the page.
  @{using (Ajax.BeginForm("SelectState",
           "Census", 
           new AjaxOptions() { 
             HttpMethod = "Post", 
             OnSuccess = "SelectComplete",
             OnFailure = "Fail" })) 
    {
                         
      @Html.DropDownList("ddlState", 
                         (SelectList)ViewData["StateList"],
                         new { onchange = "$('#bSetName').click()" });  
                                                    
      <input id="bSetName" name="bSetName" 
             style="display: block;" type="submit" />
                                                                                                    
    }
   }
 
I hate this, but it works. Any better ideas?

Thursday, March 01, 2012

Back to XmlDomDocument

While we are all moving along to LINQ and other ways to parse XML, I got stuck doing my first pass with the old System.Xml classes. So I needed to solve this problem with parsing when you have many varied namespaces in the data.

Here is my Twitter client code :


WebClient Client = new WebClient();

Stream stream =
Client.OpenRead("http://search.twitter.com/search.atom?q=" +
txtSearchVal.Text);

StreamReader reader = new StreamReader(stream);
XmlDocument doc = new XmlDocument();

string fullResponse = reader.ReadToEnd();
doc.LoadXml(fullResponse);

XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);

mgr.AddNamespace("google", "http://base.google.com/ns/1.0");
mgr.AddNamespace("openSearch", "http://a9.com/-/spec/opensearch/1.1/");
mgr.AddNamespace("atom", "http://www.w3.org/2005/Atom");
mgr.AddNamespace("twitter", "http://api.twitter.com/");
mgr.AddNamespace("georss", "http://www.georss.org/georss");

txtAllXML.Text = doc.OuterXml;

XmlNodeList list = doc.SelectNodes("//atom:entry", mgr);
foreach (XmlNode node in list)
{
listView1.Items.Add(
new ListViewItem() {
Text = node.SelectSingleNode("atom:title", mgr).InnerText })
.SubItems
.AddRange(new string[] {
node.SelectSingleNode("atom:author", mgr).InnerText,
node.SelectSingleNode("atom:content", mgr).InnerText
}
);
}



I had to add the "atom" prefix as no prefix was supplied by the data.

Wednesday, October 12, 2011

RAZOR cascading DropDownList

RAZOR


@Html.DropDownList("ddlElementCode",
TempData["ElementSelects"] as SelectList,
new { onchange = "populateValueTexts(this)" })




// Where the parameter “dropdown” is the first of the two

function populateValueTexts(dropdown) {
var myindex = dropdown.selectedIndex;
var selValue = dropdown.options[myindex].value
var xReq = jQuery.getJSON("ElementTexts",
{ elementCode: selValue },
null)
.complete(receiveValueTexts);
}

function receiveValueTexts(context, textStatus) {
var data = jQuery.parseJSON(context.responseText);

document.getElementById("ddlValueText").options.length = data.length;

jQuery.each(data, function (i, item) {
document.getElementById("ddlValueText").options[i].text = item.Text;
});
}

[AcceptVerbs(HttpVerbs.Get)]
public JsonResult ElementTexts(string elementCode)
{
Data d = new Data();

IList codes =
d.GetElementsByCode(Convert.ToInt32(elementCode));

SelectList items =
new SelectList((from c in codes select c.ELEMENT_VALUE_TXT).Distinct());

return Json(items, JsonRequestBehavior.AllowGet);
}

Tuesday, October 04, 2011

On getting NHibernate to Log the SQL

This feature is very nice. You get to see the SQL that is generated for you.

While logging is not rocket science, I don't want to learn it again on the next project, so I am saving it here. ( I am using Log4Net, but I am sure Enterprise Library or others will work similarly. )

// Configure log4net using the .config file in GLOBAL.ASAX
[assembly: XmlConfigurator(Watch = true)]
protected void Application_BeginRequest(object sender, EventArgs e)
{
XmlConfigurator.Configure();
}

In the Web.config

<log4net debug="true">
<!-- Define some output appenders -->
<appender name="trace" type="log4net.Appender.TraceAppender, log4net">
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %c{1}:%L - %m%n"/>
</layout>
</appender>
<appender name="console" type="log4net.Appender.ConsoleAppender, log4net">
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %c{1}:%L - %m%n"/>
</layout>
</appender>
<appender name="rollingFile" type="log4net.Appender.RollingFileAppender,log4net">
<param name="File" value="hib_log.txt"/>
<param name="AppendToFile" value="true"/>
<param name="maximumFileSize" value="500KB"/>
<param name="RollingStyle" value="Size"/>
<param name="DatePattern" value="yyyy.MM.dd"/>
<param name="StaticLogFileName" value="true"/>
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n"/>
</layout>
</appender>
<!-- Setup the root category, add the appenders and set the default priority -->
<root>
<priority value="DEBUG"/>
<appender-ref ref="rollingFile"/>
</root>
<logger name="NHibernate">
<level value="WARN"/>
</logger>
<logger name="NHibernate.SQL">
<level value="DEBUG"/>
</logger>
</log4net>

Monday, October 03, 2011

How to stream a File in MVC

In Classic ASP or ASP .Net, you can simply change the response type and start writing binary. In MVC you have a Controller method which returns an ActionResult. So this class will properly provide that :


public class BinaryResult : ActionResult
{
private byte[] _fileBinary;
private string _contentType;
private string _fileName;

public BinaryResult(byte[] fileBinary, string contentType, string fileName)
{
_fileBinary = fileBinary;
_contentType = contentType;
_fileName = fileName;
}

public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = _contentType;
context.HttpContext.Response.AddHeader("Content-Disposition",
"filename=" + _fileName);

if (_fileBinary != null)
{
context.HttpContext.Response.BinaryWrite(_fileBinary);
}
}
}


In my case the only other challenge was getting the Byte array prepared, because I had to call Convert.FromBase64String, because of the way the data was stored.

Sunday, October 02, 2011

Developing for MVC4 using Razor and JSON

Just playing around this weekend, I wanted some data listings in a page that did not post back in MVC. While not such a big deal in regular ASP, MVC does not have page event handlers in a code-behind file. There is instead a Controller class that you can call in Routing code.



First : the client side javascript handler, this is what is called when the Controller method completes.



using (Ajax.BeginForm("SearchForms", "DBForms", new AjaxOptions() { OnSuccess="jsonFSearchComplete", OnFailure="searchFail" })) Then … function jsonFSearchComplete(context) { $("#DBFormsDiv").html(context.Data); }



Next, the Razor syntax for the page :


@{using (Ajax.BeginForm("JsonSearchRequests",
"Request",
new AjaxOptions() {
OnComplete = "searchComplete",
OnFailure= "searchFail"
}))

// Not including the whole form I created here, just
// labels and textboxes, etc..
}





Then the Controller code for getting the Partial view built and sent down (Got this from StackOverflow postings):


private string RenderRazorViewToString(string viewName)
{
using (var sw = new System.IO.StringWriter())
{
var viewResult =
ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
var viewContext =
new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}


For a form posting in a page, there must be a Controller method ( :


[AcceptVerbs(HttpVerbs.Post)]
public JsonResult JsonSearchRequests(string AccountNumber,
string Subfirm,
string RegistrationType)
{
Data d = new Data();

if (AccountNumber.Length > 0)
{
ViewData["Requests"] =
d.GetRequestsByAccount(AccountNumber);
}
else if (Subfirm.Length > 0)
{
ViewData["Requests"] =
d.GetDBRequests(Subfirm, RegistrationType)
.Skip(0).Take(100);
}
return Json(new { Data = RenderRazorViewToString("dbRequests") });
// Note the HttbVerb must be stated for Posting a form
}


For a link that a user can click in the page :



[AcceptVerbs(HttpVerbs.Get)]
public JsonResult JsonSelectDbRequest(string Id)
{
Data d = new Data();
ViewData["Generations"] = d.GetGenerations(Id);

return Json(new { Data = RenderRazorViewToString("dbGenerations") },
JsonRequestBehavior.AllowGet);
}

// Note the Http Verb for GET must be stated
}



Code for a Partial View of a list that does Ajax posting :


@Ajax.ActionLink("SELECT",
"JsonSelectDbRequest",
"DocRequest",
new { Id = req.REQUEST_ID },
new AjaxOptions() { OnComplete = "requestSelectComplete" });



EDIT: There is no need to include the old MVCAjax script files.

Saturday, September 10, 2011

Generics For Dummys (re:NHibernate)

I am working on a rogue project that is not being paid for by the company for which I work. This is building something that just helps me do my job, so I have to be very quick. I am using NHibernate, and wanted to get listable data without writing even the smallest functions of my own. So I created this (not for production use):

   public IList GetAll()
        {                     
            return Session.GetSession()
            .CreateCriteria(typeof(T))
            .List();
         }


Because NHibernate uses generics, this function can act as a pass-through, requesting the List method be run, but not much more. My ASP .NET webforms application has to give it the table name of interest.

Again, just a fun way to rapidly get a "table viewer" application running but also providing some code value for potential full life cycle development later.

* Re-reading years later, I think I meant
 GetAll(T) 
as the Type would have to be passed to the function.

Text Template Transformation

My team stores some database logic in an XML file that is part of a CSharp project.
This seems to be working efficiently for the application in runtime and for developers to maintain their SQL statements at design. I don't like it because the file has gotten large and finding things is a manual process.
I am trying to keep up with innovations, so I wrote a transformation that changes that XML file into a class. That way Visual Studio can provide its drop-down navigation controls for finding statements in the file.


<#@ template language="C#" #>
<#@ output extension = "cs" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace = "System.Xml" #>


public class SQLConfig {
<#
XmlDocument doc = new XmlDocument();
doc.Load("C:\\Projects\\MySolution\\MyDataLayer\\ORACLESQL.config");

foreach(XmlNode node in doc.SelectNodes("//add")) {

#>

public const string <#= node.Attributes[0].Value.Replace(".","_").Replace("-","_") #> = @"<#= node.InnerText #>";

<#
}

doc = null;
#>
}



What this does is create a Class file using the above code every time the transform template is changed, or the developer can right-click "Run Custom Tool" on it.

Wednesday, August 24, 2011

Developing for Oracle

Since I have seen a lot of hand-wringing over how to write a connection string to Oracle Express, I want to save a note about this.


(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XE)));User Id=Me;Password=dev;


This is just one of those things, that as a programmer I don't know how to build when I need it. It's a bit of technical stuff that I don't endeavor to deeply understand.

Friday, October 31, 2008

.Net Pie Chart challenge

Note* This is still running at : https://www.customconfiguration.net/ASP/DefaultPie.aspx
My first response to this was very blah. Having seen this stuff done by every product since Access 2.0, made it unexciting to think about. But then my friend added a twist : make the pie chart clickable in a drill-down way. So as easy as it is, to create pie slices from an image of a circle, I was stumped at how to handle a "hotspot", and did not even know there was an ImageMap control available for a .Net webform.

So to hurry up and get something to chart, I created a dataset with one column of numeric values, and a web user control to house the ImageMap control. My control exposes a method called "Draw" and raises an event called "HotSpotClicked" which is declared as follows :


public event ImageMapEventHandler HotSpotClicked;


The graphics to write the circle and pie slices are easy to call in the System.Drawing namespace. Here is Draw method, which calls a separate method to add a percentage column to the DataSet table passed to it. It uses random colors for the pie slices, and selects the colors from the Brushes namespace of System.Drawing using Reflection. It creates a GUID for a temporary file name in which to store the Bitmap. For each pie slice it draws, it saves coordinates for where to put the map hotspot in another column in the dataset. So how to compute X,Y coordinates of the outer edge of a pie slice. My good enough answer is to use a triangle defined by center and two end points of the slice. Those should be easy enough to figure out by conversion from angles and knowing the radius of the bounding circle. But not when you have a large slice, say greater than 100 degrees. For that size and higher the triangle it defines is squeezed thinner and thinner and loses usability as a hot regaion. So in that case I added one more coordinate dividing the "sweep" angle by 2 in my computation.


public void Draw(DataSet ds)
{
// Rectangle of that is the width of the ImageMap control.
Rectangle rect =
new Rectangle(0, 0,
Convert.ToInt32(ImageMap1.Width.Value),
Convert.ToInt32(ImageMap1.Height.Value));

/ Using floats as they are small enough and liked by the Math namespace.
float radius = rect.Width / 2;

string imageName = System.Guid.NewGuid().ToString();
Bitmap bitmap = new Bitmap(rect.Width, rect.Height);

Graphics g = Graphics.FromImage(bitmap);

g.DrawRectangle(Pens.Khaki, rect);
g.FillRectangle(Brushes.LightBlue, rect);

g.DrawEllipse(Pens.Khaki, rect);
g.FillEllipse(Brushes.LightPink, rect);

calcPercents(ds);

// Using reflection to get an array of colors from which to choose.
PropertyInfo[] penInfos = typeof(Pens).GetProperties();
PropertyInfo[] brushInfos = typeof(Brushes).GetProperties();

float startAngle = 0;

// For selecting colors.
Random rand = new System.Random();

// Add a column to the dataset to record color used.
ds.Tables[0].Columns.Add("ChartColor");

for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
DataRow row = ds.Tables[0].Rows[i];

decimal fSweepAngle = 360 * (Convert.ToDecimal(row[1]) / 100);
float sweepAngle = (float)Math.Truncate(fSweepAngle);

if (i == ds.Tables[0].Rows.Count - 1)
{
sweepAngle += 360 - (startAngle + sweepAngle);
}

int colorIndex = rand.Next(brushInfos.Length - 1);

Brush myBrush = (Brush)brushInfos[colorIndex].GetValue(null, null);

g.DrawPie((Pen)penInfos[colorIndex].GetValue(null, null),
rect,
startAngle,
sweepAngle);

g.FillPie(myBrush,
rect,
startAngle,
sweepAngle);


// Calculate the Hot Spot polygon

// First line from center of the pie out to the edge.
string coords = radius.ToString() + "," +
radius.ToString() + ",";

coords += getCoordinates(startAngle, radius);

if (sweepAngle > 90)
{
//TODO: add more coordinates to get better hotspot coverage.
float intermediateAngle = startAngle + (sweepAngle / 2);

coords += getCoordinates(intermediateAngle, radius);
}

// move the needle up to where we just stopped for the next draw operation.
startAngle += sweepAngle;

coords += getCoordinates(startAngle, radius);

row[2] = coords;
row[3] = ((System.Drawing.SolidBrush)myBrush).Color.Name;
}

setHotSpots(ds);

ImageMap1.ImageUrl = "images/" + imageName + ".bmp";

bitmap.Save(Server.MapPath("images/" + imageName + ".bmp"),
System.Drawing.Imaging.ImageFormat.Bmp);
}



Setting hotspots in the map is easy once the coordinates have been solved :


private void setHotSpots(DataSet ds)
{
ImageMap1.HotSpots.Clear();

foreach (DataRow row in ds.Tables[0].Rows)
{
PolygonHotSpot phs = new PolygonHotSpot();
phs.Coordinates = row[2].ToString();
phs.AlternateText = row[0].ToString() + " "
+ row[1].ToString() + " "
+ row[3].ToString();
phs.PostBackValue = row[0].ToString();
phs.HotSpotMode = HotSpotMode.PostBack;
phs.NavigateUrl = "";
ImageMap1.HotSpots.Add(phs);
}
}


Code for calculating the percentage value of each data point provided in the dataset is very trivial ,but I list it here for future reference :


private void calcPercents(DataSet ds)
{
DataColumn PercentColumn = ds.Tables[0].Columns.Add("DataPercentage");
decimal total = 0;

foreach (DataRow row in ds.Tables[0].Rows)
{
total += Convert.ToDecimal(row[0]);
}

decimal totalPercent = 0;
foreach (DataRow row in ds.Tables[0].Rows)
{

decimal percentage = Convert.ToDecimal(row[0]) / total;
row[1] = Math.Round(percentage * 100, 1);
totalPercent += Convert.ToDecimal(row[1]);
}


if (totalPercent < 100)
{
DataRow lastRow = ds.Tables[0].Rows[ds.Tables[0].Rows.Count - 1];
decimal lastRowPercent = Convert.ToDecimal(lastRow[1]) + (100 - totalPercent);
lastRow[1] = lastRowPercent;
}

// add a column for coordinate set.
DataColumn coordinatesColumn = ds.Tables[0].Columns.Add("MapAreaCoordinates");

ds.AcceptChanges();
}


The real brain candy of the project was how to get the X.Y coordinates for an ImageMap knowing the size of the circle in the drawing and the "Sweep Angle" or angle of the pie slice. After some trial and error and merely remembering where to look from high school math, I wrote some calculations using SIN and COSINE. The fatal trap from hell, is that MATH.SIN does not accept Degrees, it accepts Radians. Intellisense doesn't tell us that. So here are the computations :


float Sin90 = (float)Math.Sin(ToRadian(90));

private string getCoordinates(float startAngle,
float radius)
{
float rise = 0;
float run = radius;
string coords = string.Empty;

rise = (radius * SinOfDegree(startAngle)) / Sin90;
run = (radius * CosOfDegree(startAngle)) / Sin90;

coords += Math.Round(radius + run, 0).ToString() + "," +
Math.Round(radius + rise, 0).ToString() + ",";

return coords;
}

private float SinOfDegree(float Degrees)
{
float radianOfDegree = ToRadian(Degrees);
return (float)Math.Sin(radianOfDegree);
}


private float CosOfDegree(float Degrees)
{
float radianOfDegree = ToRadian(Degrees);
return (float)Math.Cos(radianOfDegree);
}

private static float ToRadian(float Degrees)
{
return Degrees * (float)Math.PI / 180;
}

Sunday, October 26, 2008

Update to Config Builder

It has been requested that my tool create configuration elements that look more like old .Net Framework configuration elements, without the "add, remove, clear" syntax.

The new syntax looks like the following :

<BaseballConfig>
<WorldSeriess>
<add Year="2008">
<Teams>
<add Name="Rays" />
</Teams>
</add>
</WorldSeriess>
</BaseballConfig>


While the older, more traditional format looks like :

<BaseballConfig>
<WorldSeriess>
<WorldSeries Year="2008">
<Teams>
<Team Name="Phillies" />
<Team Name="Rays" />
</Teams>
</WorldSeries>
</WorldSeriess>
</BaseballConfig>


In order to get the xml elements to match the configuration class names, an override can be added to the ElementCollection class as follows :


public override ConfigurationElementCollectionType CollectionType
{
get
{
return ConfigurationElementCollectionType.BasicMap;
}
}



So to handle this update (or not) I have created the following function for my custom configuration tool:


private static void addConfigurationElementCollectionTypeProperty(CodeTypeDeclaration configElements)
{
CodeTypeReferenceExpression ctre = new CodeTypeReferenceExpression(typeof(ConfigurationElementCollectionType));
CodeTypeReference ctr =
new CodeTypeReference(typeof(ConfigurationElementCollectionType));
CodeMemberProperty propCollectionType = new CodeMemberProperty();
propCollectionType.Name = "CollectionType";
propCollectionType.Attributes = MemberAttributes.Override | MemberAttributes.Public;
propCollectionType.HasSet = false;
propCollectionType.HasGet = true;
propCollectionType.Type = ctr;
propCollectionType.GetStatements.Add(
new CodeMethodReturnStatement(
new CodePropertyReferenceExpression(ctre, "BasicMap")));
configElements.Members.Add(propCollectionType);
}