path.combine idiosyncrasies

I have this small program :

var s = Path.Combine(@"E:\andrei", UnknownVar);
Console.WriteLine(Directory.GetFiles(s).FirstOrDefault());

In what conditions , if I have an “C:\andrei” folder with a single file , the output will be the name of the file ?
And where you want to be aware of this behaviour ?
Hint : The output is : \andrei\New Text Document.txt

A programmer day with “That assembly does not allow partially trusted callers”

I make a website, works on local IIS. Performs user registration  + uploading some zip files + generating custom feeds for those zip files.

Using SharpZipLib_0860_Bin ( to unzip file ) , StringTemplate.dll ( to perform custom feed generation ) and NUnit-2.5.7.10213 ( to perform tests).

So far ,so good. Moving into production . User registration works, upload works, trying feeds

“That assembly does not allow partially trusted callers”
Feeds were generated with StringTemplate- it was custom feeds ;-).

Searching , talking with hosting – seeing that this happens if your asp.net does not run under full trust , but under “Medium trust”.

Normally the provider does not want to change and send me advice to put AllowPartiallyTrustedCallersAttribute (APTCA) on the class:
http://msdn.microsoft.com/en-us/library/system.security.allowpartiallytrustedcallersattribute.aspx

This does not work without signing with a strong name – so I generate the snk file , sign mine assemblies, re-deploy. Same error:

“That assembly does not allow partially trusted callers”

That is normally and I have suspected – because I have to sign the stringtemplate, not mine dll.

Trying to see if the stringtemplate is signed  -it is! When put AllowPartiallyTrustedCallersAttribute  and trying to re-build – failed because I do not have his snk

Then, in disperation, I remove the snk from stringtemplate and re-make my project.

And now –

System.Security.SecurityException: Request failed.

Antlr.StringTemplate.FileSystemTemplateLoader.InternalLoadTemplateContents(String templateName) +0

That means that the files to read contents to generate feeds are not accesible to read. But, oh my dear .NET framework :could you not tell from the beginning ?

So, possible solutions to identify causes :  identify the assembly that  causes the harm and either

1. put a snk to this assembly and for all assemblies in superior chaining

2. remove the snk from this assembly

In either case, you should see what the error is.
So how I managed to solve ?
1. Put Reflector and dissasemble the source.
2. Re-compile without snk.
3. Put in Web.Config 4. Compile and run with debug
Now it stops at :
string templateText = InternalLoadTemplateContents(templateName);

This , in StringTemplate class, was

  protected override string InternalLoadTemplateContents(string templateName)
        {
            string templateText = null;
            string templateLocation = null;

            try
            {
                //templateLocation = Path.Combine(LocationRoot, GetLocationFromTemplateName(templateName));
                templateLocation = string.Format("{0}/{1}", LocationRoot, GetLocationFromTemplateName(templateName)).Replace('\\', '/');
                StreamReader br;
                try
                {
                    br = new StreamReader(templateLocation, encoding);
                }
                catch(FileNotFoundException)
                {
                    return null;
                }
                catch(DirectoryNotFoundException)
                {
                    return null;
                }
                catch(Exception ex)
                {
                    throw new TemplateLoadException("Cannot open template file: " + templateLocation, ex);
                }

                try 
                {
                    templateText = br.ReadToEnd();
                    if ((templateText != null) && (templateText.Length > 0))
                    {
                        //templateText = templateText.Trim();

                        if (filesWatcher == null)
                        {
                            filesWatcher = new FileSystemWatcher(LocationRoot, "*.st");
                            //filesWatcher.InternalBufferSize *= 2;
                            filesWatcher.NotifyFilter = 
                                NotifyFilters.LastWrite 
                                | NotifyFilters.Attributes
                                | NotifyFilters.Security 
                                | NotifyFilters.Size 
                                | NotifyFilters.CreationTime 
                                | NotifyFilters.DirectoryName 
                                | NotifyFilters.FileName;
                            filesWatcher.IncludeSubdirectories = true;
                            filesWatcher.Changed += new FileSystemEventHandler(OnChanged);
                            filesWatcher.Deleted += new FileSystemEventHandler(OnChanged);
                            filesWatcher.Created += new FileSystemEventHandler(OnChanged);
                            filesWatcher.Renamed += new RenamedEventHandler(OnRenamed);
                            filesWatcher.EnableRaisingEvents = true;
                        }
                    }
                    fileSet.Remove(templateLocation);
                }
                finally
                {
                    if (br != null) ((IDisposable)br).Dispose();
                    br = null;
                }
            }
            catch (ArgumentException ex) 
            {
                string message;
                if (templateText == null)
                    message = string.Format("Invalid file character encoding: {0}", encoding);
                else
                    message = string.Format("The location root '{0}' and/or the template name '{1}' is invalid.", LocationRoot, templateName);

                throw new TemplateLoadException(message, ex);
            }
            catch (IOException ex) 
            {
                throw new TemplateLoadException("Cannot close template file: " + templateLocation, ex);
            }
            return templateText;
        }

Does something ring a bell to you ?
Wait for an answer
Wait for an answer
Wait for an answer
Wait for an answer
Wait for an answer
Wait for an answer
Wait for an answer
Wait for an answer

Yes – FileSystemWatcher .
http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx
[IODescriptionAttribute(“FileSystemWatcherDesc”)]
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = “FullTrust”)]
[PermissionSetAttribute(SecurityAction.InheritanceDemand, Name = “FullTrust”)]
public class FileSystemWatcher : Component,

Solution : Once defined my FileSystemTemplateLoader_MT – without FileSystemWatcher – all works well
So – the idea is : reflector, sources, find something strange. Remove, rebuild, re-test
(Thanks to OrcsWeb team for helping me on this problem !)

Find the Error

This code was written by me, in a late night moment, when copy paste seems the best option
It started this way :

public string ImportFeed(string URL)
        {
            string ret = "";
            XmlDocument xd = new XmlDocument();
            xd.Load(URL);            
            for(int i=0;i<xd.ChildNodes.Count;i++)
            {
                ret += xd.ChildNodes[i].Name;
            }
            return ret;
            
        }

So far , nothing special. Just open the url, read into an XML , return first nodes. Then I wanted more – next nodes. Nothing easier – why bother with recursion or proper variable names ? Just copy and paste :

public string ImportFeed(string URL)
        {
            string ret = "";
            XmlDocument xd = new XmlDocument();
            xd.Load(URL);            
            for(int i=0;i<xd.ChildNodes.Count;i++)
            {
                ret += xd.ChildNodes[i].Name;
                for (int j = 0; i < xd.ChildNodes[i].ChildNodes.Count; j++)
                {
                    ret += xd.ChildNodes[i].ChildNodes[j].Name;
                }
            }
            return ret;
            
        }

Could you spot the error ?
Hint : I have discovered after 10 seconds …but not looking at the code…

Five common mistakes for ASP.NET (MVC) accesing resources : css, js, images, ajax

To have once for all the link to show to people, because too much makes the same error again and again.

(From here – you can use ResolveUrl or Url.Content – it’s the same for me. I use ResolveUrl because I used first …)

Case 1 The image does not display

Please check you have the following :

<img src='<%= ResolveUrl("~/Content/images/YOURIMAGE.jpg" )%>'  alt="image" style="border:0" />

Case 2 The css does not show

Please check you have the following :

<%= string.Format("<link href='{0}' type='text/css' rel='stylesheet' />", ResolveUrl("~/Content/your.css")) %>

or

<style type="text/css">
@import '<%= ResolveUrl("~/Content/your.css") %>';

</style>

Case 3 The js does not execute (undefined error)

Please check you have the following :

<script type="text/javascript" src='<%= ResolveUrl("~/Scripts/yourjs.js")%>'></script>

Case 4 The js does execute in aspx page, but did not execute under js file

Please check you DO NOT have the following

<%

in the js file. The js file is not interpreted by the same engine as aspx, so can not interpret asp.net tags. Instead , add a parameter to your function : the path.

Simple example : Let’s say in aspx you have :

<script type=”text/javascript”>

function Generate(){

window.open(‘<% Url.Action(“About”)%>’);
}

</script>

and you call Generate();

When you put in .js file, please put this :

function Generate(url){

window.open(url);
}

and call like this :

Generate('<% Url.Action(“About”)%>');

Case 5 The ajax request gives you a 404 error.

Please ensure that you have

<%= ResolveUrl("~/path”)%>

and not

/path

Bonus 1: T4MVC , http://mvccontrib.codeplex.com/releases

Bonus 2: Edit the project file and put <MvcBuildViews>true</MvcBuildViews>

( short url: http://bit.ly/asp5Mistakes)

About programming – 7 points

So many times I see people saying : “I can not have time to follow the tutorials – but give me some fast acces to some tutorials that solve my problem”.

I have

  1. Programming is not about code – programming is about decisions that you do about what code to write.
  2. Programming can not be learned by one tutorial – find more , skip it the obvious and read more
  3. Programming is not an easy job to do : you must have  knowledge (about at least the language used), it takes divide et impera and many more (testing being the obvious forgotten one …)
  4. Programming is a self-continuing learning : the products that you are using change – and I am more productive today with VS 2010 (and .NET4 ) than I was with VB  3 ( although VB 3 was EXCELLENT for that times )
  5. Programming is about experience : you can learn from others , but from yours mistakes , things that worked and right things ( my least : error to not de-normalize tables…)
  6. Programming is about keeping happy your technical leader ( or, if you are the technical leader, the project manager) . And I advise you to being one if you can, to see their problems.
  7. Finally  – there is not a royal way to programming  – take your time and enjoy!

Generating history trigger with EF , edmx and TT files

I have wrote in an older post ( http://msprogrammer.serviciipeweb.ro/2010/06/28/ef-automatic-history-of-table-and-t4-files-tt-files/ ) how to generate history code for tables . The easy solution was to create a tt file that track for the ObjectContext the SaveChanges for each table that has a “history” in name. the limitation is that , when you raise an sql command such as “update table ” you must load from database a lot of rows for this….

Now I want to show the same thing, but generating triggers in database for that ! I start also from edmx file and with a template stealed from http://forums.asp.net/p/1599616/4083198.aspx ( to have type of fields in the database ) and another stealed and modified from StackOverflow(to generate trigger for after insert , update, delete) I manage to have a solution to generate sql tables and trigger code.

When is that good ?At the beginning stages of a project when the table structure changes by adding a new parameter.

Sample code generated for table Notes(ID, TextNote, PersonID)

print 'Create table Notes_History ';
Create Table Notes_History(
		ID_Notes_History BIGINT IDENTITY NOT NULL
		,History_Action varchar(50)
		,History_From varchar(100) default HOST_NAME()
		,History_User varchar(50) default SYSTEM_USER
		,History_Date varchar(50) default  getdate()
	   ,Id  int   NOT NULL
	   ,Textnote  nvarchar (255)  NULL
	   ,PersonId  int   NULL
	)
print 'end Create table Notes_History ';
GO
print 'create trigger for Notes'
GO
CREATE TRIGGER dbo.TR_IUP_Notes
   ON  Notes
   AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    SET NOCOUNT ON;
	DECLARE @Ins int

DECLARE @Del int

SELECT @Ins = Count(*) FROM inserted

SELECT @Del = Count(*) FROM deleted
if(@Ins  + @Del = 0)
	return;

declare @operation varchar(50)
	set @operation ='update';
	if(@ins < @del)
		set @operation ='delete';
	if(@ins > @del)
		set @operation ='insert';

	if(@ins <= @del)
	begin
		INSERT INTO Notes_History(History_Action ,Id,Textnote,PersonId)
			select @operation ,Id,Textnote,PersonId from deleted
	end
    else
	begin
    INSERT INTO Notes_History(History_Action ,Id,Textnote,PersonId)
			select @operation ,Id,Textnote,PersonId from inserted
	end
END

The drawback of the code : the user that executes is not always the logged sql server user…
Anyway, what you have to do to use this automatically generated history/audit for tables ?

download historysql and modify

string inputFile = @”Model1.edmx”;

from historysql.tt to your edmx name. Then take the generated code and execute in sql server.

LogParser, PowerShell and Quick and dirty parsing of IIS files

For a local enterprise IIS system you do not have to resort to Google analytics or other beasts that interprets IIs logs. After all, users are identified through ActiveDirectory, does not matter from what city do they come, and so on. But it will help to have some details of wjhat happened on the system this day( or the day before , if you want to send an email about the previous day totals)

So logparser to help  – he knows already to read IIS logs with  -i:IISW3C

So I have come up with the syntax  :

C:\LogParser -e:10 -i:IISW3C “SELECT cs-uri-stem as url, DIV(SUM(time-taken),1000) as Seconds, Count(time-taken) as Requests, DIV(Seconds ,Requests) as TimeExecuting   INTO C:\newfile FROM   C:\Windows\System32\LogFiles\W3SVC1\ex100909.log GROUP BY cs-uri-stem Having SUM(time-taken)>0 and Seconds>0 order by Seconds   desc” -o:TPL -tpl:%2\iistime.tpl

Basically , this will do this report about statuses of URL requested  :

Status Requests
200 1541
302 89
401 11
403 61

The problem is that C:\newfile and  C:\Windows\System32\LogFiles\W3SVC1\ex100909.log are hard-coded – we need to modify every time… So PowerShell to the rescue (Ok, I could do a C# Console program – but

1. it is more fun this way – fun meaning I want to learn something new

2. the script could be modified easily

)

So the same command is written this way with arguments , in order to can be executed each time :

%2\LogParser -e:10 -i:IISW3C "SELECT cs-uri-stem as url, DIV(SUM(time-taken),1000) as Seconds, Count(time-taken) as Requests, DIV(Seconds ,Requests) as TimeExecuting   INTO %2\%4 FROM   %5\*%1  GROUP BY cs-uri-stem Having SUM(time-taken)&gt;0 and Seconds&gt;0 order by Seconds   desc" -o:TPL -tpl:%2\iistime.tpl

But who will give arguments ( such as the system date ) ?Now powershell  to the rescue :

$namepc = (gc env:computername)
$a = get-date
$a = (get-date).AddDays(-1)
$allpath= Split-Path -Parent $MyInvocation.MyCommand.Path;
$logfolders = $env:WINDIR +"\system32\Logfiles\W3SVC*"
foreach($logfolder in  Get-ChildItem $logfolders)
{
$logfiles= $logfolder.FullName
Write-Host "parsing"  $logfiles
$log =  $a.ToString("yyMMdd") + ".log"
$process = [Diagnostics.Process]::Start($allpath + "\iis.bat" , $log + " "+ $allpath + " "+ $log + ".html" + " TIME" +$log + ".html" + " " + $logfiles)
$process.WaitForExit()
$content = "&lt;h1&gt;IIS REPORT " + $namepc  + "&lt;/H1&gt;"
$content += (get-content ($allpath  + "\" + $log + ".html"))
$content += (get-content ($allpath  + "\TIME" + $log + ".html"))

$SmtpClient = new-object system.net.mail.smtpClient
$SmtpServer = "your server"
$SmtpClient.host = $SmtpServer

$mm = new-Object System.Net.Mail.MailMessage(“<a href="mailto:from@yourcompany.com">from@yourcompany.com</a>”,"<a href="mailto:to@yourcompany.com">to@yourcompany.com</a>")
$mm.Subject = "Report IIS " + $namepc
$mm.Body = $content
$mm.Body=$mm.Body.Replace("&lt;cmp&gt;",$namepc  )
$mm.IsBodyHtml = 1
$SmtpClient.Send($mm)
}

Explanation of code :
line 1: I take the computer name to put in the report
line 2 : take the date ( if you want the current date , just comment the line 3)
line 5 : I go to usual path to logfiles (did I say quick and dirty ?)
line 6 : get all W3SVC folders and iterate to send report
line 11 : launching the bat (that contains logparser command ) in order to parse  arguments
line 12 :waiting for the process to exit – in order to can send files.
line 13 to 15: get the output
line 17 to 27 : send the output by email

Homework :
1. Execute script on a system and modify if it does not work
2. Clean up the temporary files after sending email
3. Instead of sending an email, write into a database with the current date.

You can execute sc.bat at regular times ( such as 1:00 AM)

Here is the zip file with sources logiisparser

LogParser download : http://www.microsoft.com/downloads/en/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en

Powershell scripts : http://gallery.technet.microsoft.com/ScriptCenter/en-us/site/search?f[1].Type=SearchText&f[1].Value=internet&f[0].Value=applications&f[0].Type=RootCategory&f[0].Text=Applications&x=0&y=0

Logparser quick and dirty

Sometimes you must find information in text files. Many,many text files, like IIS logs or other custom non-regular formats.

I have a bot from http://www.imified.com/ – and I log the messages with log4net in text files, with another messages.

An entry looks like that :

System.ArgumentException: ;channel=private;botkey=<guid>;userkey=<guid>;user=name@yhaoo.com;network=Yahoo;msg=hello;step=1;value0=hello;to=asdasd

And there are multiple log files that I want to parse and find the email adresses to collect feedback from those persons that use my bot.

LogParser to the rescue! Download from http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en and use this command line

LOGPARSER “Select Text into a.csv from current* where Text like ‘%@%'” -i:TEXTLINE

Explanation of command :

Select Text into a.csv from current* where Text like ‘%@%’ –means find in files that begin with current(current*) all text that contains emails ( ‘%@%’) and put in file a.csv  the results.

-i:TEXTLINE – means the format is text

What can be more simple ?

(Ok, for finding the user name I had to resort to excel, to remove duplicates … )

More I think it is fast enough : for parsing 114 files with 58.8 MB (PC with a 2GB RAM + 7200 RPM ) the results are :

Statistics:
———–
Elements processed: 487176
Elements output:    1044
Execution time:     7.69 seconds

Also logparser can be used for more than text files :

http://support.microsoft.com/kb/910447

http://www.stevebunting.org/udpd4n6/forensics/logparser.htm

More, it can be as a COM DLL in every .NET project, making it a usefull tool . See

http://www.codeproject.com/KB/recipes/SimpleLogParse.aspx

Next time I will show the using Powershell in combination with LogParser.

First Install of tools for programmer

My primary tools are Visual Studio ( and the Express suite) , Sql Server ( and SQL Server Management Studio )( and the Express suite) and Office (Excel, Word)

Those are the modification that I do every time … I wish there were enabled by default :

For VS2010

Go to=> Tools, Options , Html, Formatting ,Check “ Insert attribute value quotes when typing”

image

This saves me a lot of time , when I put : input type=”text” , the “  are inserted automatically.

For SSMS

Go to=> Tools, Options , Designers , uncheck “Prevent saving changes that require table re-creation”

image

As a developer, I do modify tables … and this wont let me the first time …

For Excel, Word

Alt+F11 (Visual Basic Editor), Tools, Options, Editor Tab, uncheck “Auto syntax check”, check “require variable declaration”

image

“require variable declaration”  :This saves you a lot of time debugging or writing “Option Explicit” .

“Auto syntax check “ : I do not want a message box each time I do an error. The error is seen in red … no need for a Msgbox.

How about you, dear reader ? Do you have programs that require after installation modifying of options ?

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.