|
|

|
|
Code
I've been working a lot more with LINQ2SQL and LINQ2Entities lately, and I ran into my first issue at the end of last week. I couldn't figure out why null parameters were causing the query to return strange results. The original query looked like this:
List<MenuLayoutDataEntity> ret = (from c in _Context.tblMenuLayouts
where c.ConceptID == entity.ConceptID
&& c.ItemID == entity.ItemID
&& c.MarketID == entity.MarketID
&& c.DepartmentID == entity.DepartmentID
orderby c.SortOrder
select new MenuLayoutDataEntity()
{
MenuID = c.MenuID,
ItemNamePOS = c.ItemNamePOS,
ItemID = c.ItemID.HasValue ? c.ItemID.Value : -1
}).ToList();Sometimes, the MarketID and DepartmentID would be null, and that's what was causing the problem. After some Googling, I found that I need to enclose the equality using the Equals method in the object class. Here's the query using Equals:
List<MenuLayoutDataEntity> ret = (from c in _Context.tblMenuLayouts
where c.ConceptID == entity.ConceptID
&& c.ItemID == entity.ItemID
&& Equals(c.MarketID, entity.MarketID)
&& Equals(c.DepartmentID, entity.DepartmentID)
orderby c.SortOrder
select new MenuLayoutDataEntity()
{
MenuID = c.MenuID,
ItemNamePOS = c.ItemNamePOS,
ItemID = c.ItemID.HasValue ? c.ItemID.Value : -1
}).ToList();Unfortunately, this only works for LINQ2SQL, and this query is actually a LINQ2Entities query. So back to Googling. I finally found a hint in a post over at StackOverflow. Apparently, there's is a bug in the LINQ2Entities parser when trying to use IS NULL functionality in the where clause. The solution was to check the value on the entity class, and then select a specific query. Here's the results that actually gave me the results that I wanted:
List<MenuLayoutDataEntity> ret = null;
// Check if this is a store item, i.e. Departmental
if (entity.MarketID.HasValue && entity.DepartmentID.HasValue)
{
ret = (from c in _Context.tblMenuLayouts
where c.ConceptID == entity.ConceptID
&& c.ItemID == entity.ItemID
&& c.MarketID == entity.MarketID
&& c.DepartmentID == entity.DepartmentID
orderby c.SortOrder
select new MenuLayoutDataEntity()
{
MenuID = c.MenuID,
ItemNamePOS = c.ItemNamePOS,
ItemID = c.ItemID.HasValue ? c.ItemID.Value : -1
}).Distinct().ToList();
}
// Check if this is a market item, i.e. MarketID is not null
if (entity.MarketID.HasValue && !entity.DepartmentID.HasValue)
{
ret = (from c in _Context.tblMenuLayouts
where c.ConceptID == entity.ConceptID
&& c.ItemID == entity.ItemID
&& c.MarketID == entity.MarketID
&& c.DepartmentID == null
orderby c.SortOrder
select new MenuLayoutDataEntity()
{
MenuID = c.MenuID,
ItemNamePOS = c.ItemNamePOS,
ItemID = c.ItemID.HasValue ? c.ItemID.Value : -1
}).Distinct().ToList();
}
// Check if this is a global item, i.e. both MarketID and DepartmentID are null
if (!entity.MarketID.HasValue && !entity.DepartmentID.HasValue)
{
ret = (from c in _Context.tblMenuLayouts
where c.ConceptID == entity.ConceptID
&& c.ItemID == entity.ItemID
&& c.MarketID == null
&& c.DepartmentID == null
orderby c.SortOrder
select new MenuLayoutDataEntity()
{
MenuID = c.MenuID,
ItemNamePOS = c.ItemNamePOS,
ItemID = c.ItemID.HasValue ? c.ItemID.Value : -1
}).Distinct().ToList();
}Very annoying, but at least I know how to handle this. Hopefully this will help another soul out there.
Posted in: Code, Databases
I got tired of having to hand-copy file paths into the InnoSetup script for PainlessSVN. I'm specifically referring to the Subversion server files. There's something like 278 files, that are in nested folders. I had just finished pasting in all the paths for Subversion 1.6.13, when 1.6.15 was released and I was seriously dreading repeating this whole excersise again. I needed something that would recurse through both files and folders. It's easy doing one or the other, but not both at the same time (at least for me). I spent about 3 hours last night working on this. The good thing about this code is that I needed something similar for the new DeleteDevCrumbs utility that I'm polishing for release. As it currently stands, this code will create a list of files from the directory that is passed in through a command-line argument, scan the child directories, and create a text file for you. The file will contain a ready made list that can be pasted right in the [Files] section of an InnoSetup script. Here's a console app with this code: using System.Collections.Generic; using System.IO; namespace ScanFilesTest
{
class Program
{
private static List<string> _tree = new List<string>();
static void Main(string[] args)
{
string rootDirectory = args[0];
var rootInfo = new DirectoryInfo(rootDirectory);
if (!rootInfo.Exists)
{
throw new DirectoryNotFoundException(string.Format("{0} does not exist!", rootInfo.FullName));
}
FileInfo[] files = rootInfo.GetFiles();
if (files.Length > 0)
{
ScanFolders(files);
}
else
{
DirectoryInfo[] folders = rootInfo.GetDirectories();
ScanFolders(folders);
}
TextWriter subversionList = new StreamWriter("D:\\subversionfilelist.txt");
foreach (var file in _tree)
{
var currInfo = new FileInfo(file);
string currDir = currInfo.FullName.Replace(rootDirectory, string.Empty);
subversionList.WriteLine(string.Format("Source: {0}; DestDir: {{pf}}\\Subversion{1}; Components: Subversion", file, currDir));
subversionList.Flush();
}
subversionList.Close();
}
private static void ScanFolders(FileInfo[] files)
{
foreach (var file in files)
{
if (file != null && !string.IsNullOrEmpty(file.FullName))
{
_tree.Add(file.FullName);
}
}
if (files.Length > 0)
{
string root = files[0].DirectoryName;
var rootInfo = new DirectoryInfo(root);
DirectoryInfo[] folders = rootInfo.GetDirectories();
if (folders.Length > 0)
{
ScanFolders(folders);
}
}
}
private static void ScanFolders(IEnumerable<DirectoryInfo> folders)
{
foreach (var folder in folders)
{
if (folder.GetFiles().Length > 0)
{
ScanFolders(folder.GetFiles());
}
else
{
if (folder.GetDirectories().Length > 0)
{
ScanFolders(folder.GetDirectories());
}
else
{
if (folder != null && !string.IsNullOrEmpty(folder.FullName))
{
_tree.Add(folder.FullName);
}
}
}
}
}
}
}
I'm involved in creating a COM interface from a C# 4.0 library. This took a lot of hair-pulling and banging-head-against-the-wall moments. This skillset appears to be quickly disappearing; sad, but quite understandable. I wanted to store how this is done, because I found many little slivers across the Intertubes, but nowhere did I find the slivers put into a coherent picture. I'm going to use a simple class that I'm calling ComDog. Here is the definition: using System; using System.Runtime.InteropServices;
namespace ComDog { [ComVisible(false)] public delegatevoid DogEventHandler();
[ComVisible(true)] [Guid("2406DD50-A3CE-43A6-9F20-112B621CB784")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)] public interfaceIDogEvents { [DispId(1)] void Bark(); [DispId(2)] void Howl(); [DispId(3)] void Eat(); }
[ComVisible(true)] [Guid("8C6DAD17-0612-4166-AD35-3A55DDEAF62E")] [ClassInterface(ClassInterfaceType.AutoDual)] [ComSourceInterfaces(typeof(IDogEvents))] public class Dog : MarshalByRefObject { public event DogEventHandler Bark; public event DogEventHandler Howl; public event DogEventHandler Eat; public void MakeDogBark() { if (Bark != null) { Bark(); } } public void MakeDogHowl() { if (Howl != null) { Howl(); } } public void MakeDogEat() { if (Eat != null) { Eat(); } } } Now let's get this compiled and registered. To get this registered with COM, well need to issue this command, in an elevated command prompt: C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm comdog.dll /tlb:ComDog.tlb Now on to Delphi 7... Copy both the ComDog.dll and ComDog.tlb to the same folder as your Delphi project. I'm doing this, because I don't want to mess with the GAC. In the Delphi IDE, select Project --> Import Type Library. It will look like this: 
This in turn, should show the Import Type Library dialog, that looks like this: 
If you registered the assembly (dll in .NET parlance), you should see an entry for ComDog, like the selected one in the dialog above. Click on the "Create Unit" button. This will add a "ComDog_TLB.pas" file to your Delphi project. 
One important section that I want to direct your attention to: // *********************************************************************//
// OLE Server Proxy class declaration
// Server Object : TDog
// Help String :
// Default Interface: _Dog
// Def. Intf. DISP? : No
// Event Interface: IDogEvents
// TypeFlags : (2) CanCreate
// *********************************************************************//
{$IFDEF LIVE_SERVER_AT_DESIGN_TIME}
TDogProperties= class;
{$ENDIF}
TDog = class(TOleServer)
private
FOnBark: TNotifyEvent;
FOnHowl: TNotifyEvent;
FOnEat: TNotifyEvent;
FIntf: _Dog; The events are of type TNotifyEvent, because they do not have parameters. This comes from the IDogEvents. Delphi will ALWAYS use the definition in your event interface, instead of the events listed in the main class. Make absolutely sure that the signature in the event interface match the signature of the events in your main C# class. Here is what the definition of the events will look like in your Delphi main form/unit: uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ActiveX, ComObj, ComDog_TLB, StdCtrls, OleServer;
type
TForm1 = class(TForm)
btnBark: TButton;
btnHowl: TButton;
btnEat: TButton;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure btnBarkClick(Sender: TObject);
procedure btnHowlClick(Sender: TObject);
procedure btnEatClick(Sender: TObject);
private
{ Private declarations }
protected
procedure Barked(Sender: TObject);
procedure Howled(Sender: TObject);
procedure Ate(Sender: TObject);
public
{ Public declarations }
end;
var
Form1: TForm1;
TestDog: TDog; The proctected section above defines the events. This you will have to type manually. Make sure that your _TLB file is listed in the uses section above. Now to actually have the events handled, you will have to create a procedure that matches the signature of event. Like so: procedure TForm1.Barked(Sender: TObject);
begin
Memo1.Lines.Add('Dog Barked');
end;
procedure TForm1.Howled(Sender: TObject);
begin
Memo1.Lines.Add('Dog Howled');
end;
procedure TForm1.Ate(Sender: TObject);
begin
Memo1.Lines.Add('Dog ate something');
end; This is how you wire up C# COM events up to a Delphi COM client app. The next question is how to deal with events that have parameters. It's basically the same thing. You'll just have to make sure that the signatures for event definitions and event handlers in Delphi, match what's in the _TLB file. I did a lot of hair-pulling in order to get this working. I wanted to post this in my blog, for the next time I'm doing this. Please feel free to comment here, if you need more help.
I just finished creating a new release on CodePlex for SVNManagerLib 0.5.6. Here is a list of what is new: NEW - Upgraded the solution to Visual Studio 2008.
NEW - Started introducing interfaces
NEW - Introduced lazy loading of anything below repository level
NEW - Deprecated SVNController for SubversionServerController
NEW - Deprecated SVNRepository for ISubversionRepository, SubversionRepositoryBase, and SvnServeRepository. This will open the way for eventually adding Apache support.
NEW - Broke out RepositoryRootDirectoryDoesNotExistException into a file
NEW - Added support for using a global configuration file.
NEW - Added support for Subversion Realms.
NEW - Finished the code for loading dump files. Hasn't been tested yet!
NEW - Added RepositoryRootDirectoryDoesNotExistException.cs to help deal with the issue of disconnected mapped drives in Vista.
NEW - Many little code refactorings to help readability http://www.codeplex.com/svnmanagerlib
I was installing one of my new 1 tb drives on my system at home. I went ahead and moved all my virtual machines to this new drive. While doing this, I found that I had downloaded a VMWare image from the Mono Project. This has Mono 2.0.1 and Suse 11.0. Since I was wide awake from my migraine medication, I decided to see if could still compile SVNManagerLib on Linux. To my delight, MonoDevelop compiled it fine, without any errors. Here's a screeshot fo SVNManagerLib compiled in MonoDevelop 1.0 running in Suse 11.0: 
And here is a screenshot of the TestHarness winform app compiled successfully as well: 
And here is the TestHarness running from MonoDevelop: 
This is soooo cool! I just downloaded the zip file from the CodePlex page at www.codeplex.com/svnmanagerlib I clicked on the Source tab, then downloaded the last changeset. It worked without any changes to the source. This is the library that powers PainlessSVN and SVN Backup Widget. Now I just need to find a good tutorial on how to install Subversion in Suse 11. I'm a total n00b when it comes to Linux, and none of the How-To docs out there have helped me here. Anybody has a good resource for installing Subversion in Suse 11?
The integration that I had with my ecommerce DNN module (ActivePurchase) broke when I moved the site to PowerDNN. I have been fighting with this for a couple weeks now. Here's what happened:
The part that sends the serial number did not work anymore, because the SMTP server was not on the same network. Changing the settings did not work at first. I went googling for sending emails from DNN. I got some good hits for sending email from inside DNN modules. The API for this is fairly simple (relatively). You first make a reference to DotNetNuke.Services.Mail. Then you make a call to the Mail.SendMail() method, with the appropriate parameters. There are 5 overloads. The call from a module would look like this: DotNetNuke.Services.Mail.Mail.SendMail(FromAddress, SendTo, "", Subject, Body, "", "", "", "", "", "") Well, that didn't work. So now I was scratching my head. I am using a special type of integration called a custom step, which is just an assembly in the bin directory for the DotNetNuke install. I had to go and dig into the event log that DNN keeps. There were several exceptions there. The message was that the SMTP connection needed to be authenticated. WTF?? Custom DNN modules use the SMTP settings that the administrator has setup. However, the custom step does not get treated as a module. So I had to do this: DotNetNuke.Services.Mail.Mail.SendMail(FromAddress, SendTo, "", Subject, Body, "", "", "", "", UserName, Password) Once I did that, the custom step started sending emails like before. YES! Now that this is fixed, I can get my undivided attention back to PainlessSVN and SVN Backup Widget stuff.
I finally got PainlessSVN to read the icons that are associated with files in the Windows shell. That took some Win32 API hacking though. Here's a screenshot of the left hand side treeview: 
Here is a screenshot of the details pane: 
I got the base code for doing this from this Microsoft link: www.microsoft.com/downloads/details.aspx This code is VB.NET, but it's fairly easy to convert to C#. Ok, I cheated. I pasted the VB.NET code into SharpDevelop 3.0, and let it convert it to C#. Didn't have to change anything. Visual Studio took the code without complaining.
Well, I had a hair pulling session trying to recover the passwords on my hobby site. Both my admin and host paswords got hosed, so I couldn't just login and fix them. I first went Googling to find a winform app to help with this problem. Well, all the solutions were just modules to install on your DotNetNuke installation. Since I can't even login as host, this approach is just totally useless. Thankfully, DNN uses the ASP.NET 2.0 Membership Providers. So, I started with Membership.Provider.RetrievePassword(). Oops, "Bad Data". Crap! I tried other account passwords with the same result. Double-Crap! I modified my little winform app, to now reset passwords. This time I used Membership.Provider.ResetPassword(). Got a new password for host, and I was able to login now. YES! I'm using .NET 2.0 for this winform app. So if you want to get this working, here is what you need to do: Make sure that you include the following sections from your DNN's website web.config file: connectionStrings system.web You only need the membership and machineKey sections inside system.web. Import System.Configuration and System.Web into your project. All of the needed API calls will be under Membership.Provider namespace. You'll find a lot of samples on how to use on Google. Anyways, I wanted to put it somewhere, so that I can find it later, WHEN I would need to do this again.
I have several Subversion servers on my network, so I'm registering new servers in PainlessSVN fairly often. I got tired of trying to figure out what server I was hooked to, so I added a little visual tweak on the server node. 
So now, I can tell exactly what server I'm working with at a glance. I will probably end up adding the ability to keep several servers registered, a la Enterprise Manager and SQL Server Management Studio. I had to mess with Win32 APIs in order to get this working correctly. Here are the API calls that I used:
public const uint DRIVE_UNKNOWN = 0; // unknow drive type
public const uint DRIVE_NO_ROOT_DIR = 1; // invalid root path was given to the function
public const uint DRIVE_REMOVABLE = 2; // removeable drive like a floppy
public const uint DRIVE_FIXED = 3; // a fixed drive like a hard disk
public const uint DRIVE_REMOTE = 4; // a network drive
public const uint DRIVE_CDROM = 5; // a cd-rom drive
public const uint DRIVE_RAMDISK = 6; // a ram disk
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint GetDriveType(string lpRootPathName);
[DllImport("mpr.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static extern int WNetGetUniversalName(
string lpLocalPath,
[MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
After some more testing I found that the URIBuilder class does not work correctly with UNC path. Here's the updated code: public static string PathToFileUrl( string pathToConvert ) { string fileURL = ""; bool isUNC; UriBuilder fileURI = null; // This is to capture whether the incoming path is // an UNC path or not. if ( pathToConvert.StartsWith( @"\\" ) ) { isUNC = true; } else { isUNC = false; } try { fileURI = new UriBuilder( pathToConvert ); } catch( UriFormatException ex ) { // This is for debugging string msg = ex.Message; } if ( isUNC ) { if ( fileURI != null ) fileURL = fileURI.ToString(); } else { if ( fileURI != null ) fileURL = fileURI.ToString().Replace( "file://", "file:///" ); } return fileURL; }
| |
|
|