April 2004 - Posts
Rob and I discussed this very same thing the other night. It should be a delicate balance. Microsoft has really opened a window (pun intended) into their development process in a very public forum. I DO want to know about Longhorn, Avalon, Indigo, Whidbey, V2, etc.., but I also want to get advanced insight to the current version of the framework. MSDNMag has gotten ridiculous about covering future stuff. From the estimates these things are at the VERY LEAST 1 or more years away. I would be happy seeing 1/4 of the content be targeted towards V2 stuff. Right now I still have to work in the 1.1 world. I need more 1.1 content.
Gonzalez Out.
ISerializable posted about using Enterprise Project Templates for organization of projects...
I thought it was interesting.
The way we organize our code at work follows a very specific structure.
We take Microsoft's suggestion on namespaces to heart.
Our assemblies are all created using the Microsoft “Company.Technology.Project” namespace organizational notation.
Context:
ObjectFoundry.Core (ObjectFoundry.Core.dll)
ObjectFoundry.Core.DataAccess (ObjectFoundry.Core.DataAccess.dll)
ObjectFoundry.Core.Utility (ObjectFoundry.Core.Utility.dll)
ObjectFoundry.Tracking (ObjectFoundry.Tracking.dll)
ObjectFoundry.Tracking.Business (ObjectFoundry.Tracking.dll)
ObjectFoundry.Tracking.DataAbstraction (ObjectFoundry.Tracking.dll)
ObjectFoundry.Tracking.ConsumerWeb (ObjectFoundry.Tracking.ConsumerWeb.dll)
Core is self-explanatory, it contains all of the assemblies/projects/classes for our core or foundation. This foundation or framework can be used across all applications we develop.
Tracking is a particular project we are working on. It might have several applications, for example a consumer facing web application, and an administration application. Both of these applications use the same business/dataabstraction code, but different interface code.
All of the source code exists in a directory called src. All external, third party assemblies exist in a directory called lib. Documentation is put in a directory called, yep, you guessed it... docs. We labored over where to put our tests, but we finally decided that tests are technically source code, so we ended up putting them in src also. We place solution files in the root of the src directory as well. All references between projects are project references and with this directory structure, a developer can check out all of the code from source control, and no matter his/her directory structure, everything “should“ build correctly.
We can also have as many solution files as needed for focusing granularity. Say for example I want only the projects in the ObjectFoundry.Core namespace, I would create a blank solution file called ObjectFoundry.Core.sln, and load in only the projects I want. Or if I wanted to load the ConsumerWeb application (which also references Core) and all its reference projects, I would create a solution file called ObjectFoundry.Tracking.ConsumerWeb-With-Core.sln.
This method isn't perfect by any means. We have run into issues where we dont follow our own rules (or Microsoft's). For example, you will notice a clear delineation between ObjectFoundry.Core.DataAccess and ObjectFoundry.Core.Utility. What if I wanted to share a class between those 2 projects/namespaces. The solution we came up with was to create another project and just call it ObjectFoundry.Core. Same thing for ObjectFoundry.Tracking. We also decided that if we needed namespaces longer than the organizational notation, they should probably reside in the assembly most related. One example of this would be our core Web assembly. ObjectFoundry.Core.Web is the name of the assembly, but it has a few different child namespaces. ObjectFoundry.Core.Web.Controls contains server controls than can be used across any application, ObjectFoundry.Core.Web.Helpers contains helper methods for stuff like data binding, dynamic stylesheet attachment, etc.. However in doing this we had an issue with the way we are doing our usercontrols for web applications. If we have a consumer facing application and an administrative application, our idea was to share all of the user controls between these 2 applications. The consumer facing application would invoke the rendering view of the control, while the admin application would invoke the editing view. More information on that here. We ended up creating a web project called ObjectFoundry.Tracking.Web.UI and adding our shared pages, usercontrols to this project.
So finally, this is the structure of our project when checked out from source control.
D:\Projects\ObjectFoundry
\src
ObjectFoundry.Core.sln
\ObjectFoundry.Core
\Core
ObjectFoundry.Core.csproj
\DataAccess
ObjectFoundry.Core.DataAccess.csproj
\Utility
ObjectFoundry.Core.Utility.csproj
\ObjectFoundry.Tracking
\Tracking
ObjectFoundry.Tracking.csproj
\Business
\DataAbstraction
\ConsumerWeb
ObjectFoundry.Tracking.ConsumerWeb.csproj
\AdminWeb
ObjectFoundry.Tracking.AdminWeb.csproj
\Web.UI
ObjectFoundry.Tracking.Web.UI.csproj
\ObjectFoundry.Core.Testing
\DataAccess
ObjectFoundry.Core.Testing.DataAccess.csproj
\Utility
ObjectFoundry.Core.Testing.Utility.csproj
\lib
nunit.framework.dll
log4net.dll
I know I understand this, but I am not sure I was able to convey it accurately.
Found this link today...http://www.xtras.net/xdn/. Apparently this guy, Mike Schinkel, the president of this website, Xtras.Net, is giving away subscriptions until the end of May 2004. Read more about it here. I am always on the lookout for new tools and things for my C:\Downloads folder. Typically the way it works, I download something and completely forget about it.
This seemed to work well for Gavin, the creator of NTierGen, as well.
I struggled with how to approach this. I think being completely honest is the only ay to go. I personally don't like “magic button” code generators, after working with CodeSmith on a daily basis. NTierGen is no exception.
The application itself worked fine for me. No huge errors or anything. What did happen when I generated code did concern me.
I pointed it at my development database for a product we are currently working on. After going through the options and adding just the tables (only 160 of them) and not creating anything special like views, or filters, the generator took over 1 hour to run (would have gone longer but I stopped it) and it was sitting at 95-99% processor utilitization the whole time.
What is even more interesting, it never made it through the sql after 1 hour. What was even more frustrating, IT UPDATED THE DATABASE WITH THE STORED PROCEDURES. I had no clue when I started generating that I was actually going to be updating my database. There should be a warning, or a notice, or the option to generate the sql into files that I can run at a time of my choosing. I would prefer the option to name my stored procedures the way I want, not just a prefix option. I would prefer to generate sql in MY style of coding. This is the main problem with “magic button” code generators. They lock you into an architecture. They take control away from you. Honestly, at this point, I have not even bothered to really look at the code this tool generates. For a developer that doesn't have expertise in designing, developing and maintaining tiered systems, I can see how this would be useful. However, we tend to want to control things ourselves where I work, and need a tool like CodeSmith to accomplish this.
If you don't want to build your own architecture then NTierGen might be something worth looking at. However if you need explicit control over your code, with the ability to change the architecture to fit your needs as time progresses, then CodeSmith is probably a better choice.
If anyone is interested and local to the Dallas/Fort Worth area, Shannon Davidson and I will be presenting May 6th at the DFW .NET Users Group, Special Interest Group for C#. We will be presenting on CodeSmith. Come and check it out if you can.
Ok, I have been reading some information regarding these 2 methods, and I would like someone to set me straight.
I realize there are a couple of concepts that seem key to understanding this. If your class does not override the Equals method, you are doing an identity check (do 2 objects point to the same heap reference?) It is possible to override the Equals method so that your objects are tested for equivelence in addition to identity.
When I override Equals, I get a compiler warning stating that I should be overriding GetHashCode as well. In the documentation for GetHashCode there are a couple of properties that a hash function should have.
If two objects of the same type represent the same value, the hash function must return the same constant value for either object.
For the best performance, a hash function must generate a random distribution for all input.
The hash function must return exactly the same value regardless of any changes that are made to the object.
I understand the first property, you can use GetHashCode in your overriden Equals method to make it faster (You are short circuiting the logic before going into your deep field by field comparison). I understand the second property because it reduces the possibility of collisions between hashes. I am unclear regarding the 3rd property, but I am assuming that it has to do with HashTables and how they sort based on key hashes. If an object returned a different hashcode after being added to a specific bucket, the hashtable could lose it, leading to inefficiency. However it seems to me that property1 and property3 could collide with each other. Let's assume the following scenario... Class1 has 2 properties called x and y.
Class1 o1 = new Class1();
o1.x = 100;
o1.y = 200;
Class1 o2 = new Class1();
o2.x = 200;
o2.y = 100;
Assuming we have overriden Equals and GetHashCode, these 2 objects should return different hashcodes, based on the rules we defined above. What happens when I change o2.x = 100; o2.y = 200; and call GetHashCode again? Assuming we are using the immutable hashcode rule, both of these objects should still return different hashcodes, even though they are now equivilent. This seems to go against the first property. This is where my disconnect seems to be.
I understand overriding Equals could be useful for some of the stuff we are doing at work, but I am unsure how to proceed with the implementation of the GetHashCode method. Below I have included some code that I have been toying around with, to demonstrate my findings. Thanks to Peter Drayton for his Effective Types presentation|pdf that opened my eyes to this.
The questions I currently am still asking and investigating are...
When should I worry about overriding Equals and GetHashCode?
Should I be doing this on most of my objects?
What is a good GetHashCode algorithm?
(This question is of particular interest to me. I decompiled System.Uri, as an example, and noticed that there is a private readonly static byte[] field called CaseInsensitiveHashCodeTable. The internal static method, CalculateCaseInsensitiveHashCode, seems to be doing some “interesting“ stuff to calculate the hashcode. I don't completely understand what is going on in there, so maybe it is just a lack of knowledge helping me understand this whole thing)
My Findings
Does anyone know of a way to export and import favorites for Internet Explorer via code without user intervention? I managed to use the ShellUIHelper api to programmatically import/export my favorites to a file, but a dialog box is displayed asking for confirmation first. I was wondering if it is at possible to turn this off.
Below is the code I used after creating the Runtime Callable Wrapper around SHDOCVW.dll
ShellUIHelperClass helper = new ShellUIHelperClass();
helper.ImportExportFavorites(false, "c:\\favorites.html");
helper = null;
More Posts