Archive for June, 2007

CodeSmith template to generate LINQ To SQL Data Context

June 2007 – October 2009 .NET (, , ) • 4,483 views • 2 responses

If you are interested in what LINQ to SQL generates and don’t have Orcas installed or available right now but use CodeSmith try the following template to generate very similar code.

The primary difference is that this writes out the System types rather than the C# aliases (e.g. System.Int32 instead of int) but that could easily be changed but is binary compatible and otherwise almost identical to the source.

Download LINQ to SQL template (CodeSmith) (4 KB)

<%@ CodeTemplate Src="LinqFunctions.cs" Inherits="LinqFunctions" Language="C#" TargetLanguage="C#"
  Description="Generates a data context and entities for given tables." %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Assembly Name="System.Data" %>
<%@ Import Namespace="SchemaExplorer" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.Data" %>
<%@ Property Name="DataContextClassName" Type="System.String" Category="Naming" Optional="True"Description="Name of the data context class to generate." %>
<%@ Property Name="Namespace" Type="System.String" Category="Naming" Optional="True" Description="Namespace for the data context class to generate." %>
<%@ Property Name="SourceTables" Type="SchemaExplorer.TableSchemaCollection" Category="Connection"Description="Tables to be mapped." %>
<%SchemaExplorer.DatabaseSchema database = SourceTables[0].Database;
string className = (String.IsNullOrEmpty(DataContextClassName)) ? DatabaseName(database) + "DataContext" : DataContextClassName;
Dictionary<TableSchema, List<TableKeySchema>> reverseForeignKeys = new Dictionary<TableSchema, List<TableKeySchema>>();
foreach(TableSchema table in SourceTables) {
    foreach(TableKeySchema keySchema in table.ForeignKeys) {
        if (reverseForeignKeys.ContainsKey(keySchema.PrimaryKeyTable)) {
            reverseForeignKeys[keySchema.PrimaryKeyTable].Add(keySchema);
        }
        else {
            List<TableKeySchema> keySchemas = new List<TableKeySchema>();
            keySchemas.Add(keySchema);
            reverseForeignKeys.Add(keySchema.PrimaryKeyTable, keySchemas);
        }
    }
}%>//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     CodeSmith template DeLINQuent.cst v0.1
//     Generated by <%=CurrentUserName()%> at <%=DateTime.Now%>
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace <%=Namespace%>{
    public partial class <%=className%> : global::System.Data.Linq.DataContext
    {
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public <%=className%>(string connection) :
                base(connection)
        {
        }

        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public ProjectDataContext(global::System.Data.IDbConnection connection) :
                base(connection) {
        }
<%    foreach(TableSchema table in SourceTables) {
        string entityName = EntityName(table);
        string propertyName = TableName(table);%>
        public global::System.Data.Linq.Table<<%=entityName%>> <%=propertyName%> {
            get {
                return this.GetTable<<%=entityName%>>();
            }
        }
<%     } %>
    }<%    foreach(TableSchema table in SourceTables) {
        string entityName = EntityName(table);%>
    [global::System.Data.Linq.Table(Name="<%=table.FullName%>")]
    public partial class <%=entityName%> :global::System.Data.Linq.INotifyPropertyChanging,global::System.ComponentModel.INotifyPropertyChanged
    {
<%    foreach(ColumnSchema column in table.Columns) {%>
        private <%=NullableSystemType(column)%> _<%=PropertyName(column)%>;
<%    }
      if (table.ForeignKeys.Count > 0) {%>
<%        foreach(TableKeySchema keySchema in table.ForeignKeys) {
            string foreignEntityName = EntityName(keySchema.PrimaryKeyTable);%>
        private global::System.Data.Linq.EntitySet<<%=foreignEntityName%>> _<%=foreignEntityName%>;<%
          }
      }
      if (reverseForeignKeys.ContainsKey(table)) {%>
<%        foreach(TableKeySchema keySchema in reverseForeignKeys[table]) {
            string propertyName = TableName(keySchema.ForeignKeyTable);%>
        private global::System.Data.Linq.EntityRet<<%=EntityName(keySchema.ForeignKeyTable)%>> _<%=propertyName%>;
<%        }
      } %>
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public <%=entityName%>() {
        }
<%    foreach(ColumnSchema column in table.Columns) {
        string propertyName = PropertyName(column);%>
        [global::System.Data.Linq.Column(Storage="_<%=propertyName%>", Name="<%=column.Name%>", DBType="<%=AttributeColumnDbType(column)%>"<%
            if (column.IsPrimaryKeyMember) { %>, IsPrimaryKey=true<% }
            if (!column.AllowDBNull) { %>, CanBeNull=false<% } %>)]
        public <%=NullableSystemType(column)%> <%=propertyName%> {
            get {
                return this._<%=propertyName%>;
            }
            set {
                if (this._<%=propertyName%> != value) {
                    this.OnPropertyChanging("<%=propertyName%>");
                    this._<%=propertyName%> = value;
                    this.OnPropertyChanged("<%=propertyName%>");
                }
            }
        }

<%    } %>
<%    foreach(TableKeySchema keySchema in table.ForeignKeys) {
        string propertyName = EntityName(keySchema.PrimaryKeyTable);
        string tableName = TableName(keySchema.PrimaryKeyTable);%>
        [global::System.Data.Linq.Association(Name="<%=keySchema.Name%>",
            Storage="_<%=propertyName%>", OtherKey="<%=AttributeColumnList(keySchema.ForeignKeyMemberColumns)%>",
            ThisKey="<%=AttributeColumnList(keySchema.PrimaryKeyMemberColumns)%>", IsForeignKey=true)]
        public <%=propertyName%> <%=propertyName%> {
            get {
                return this._<%=propertyName%>.Entity;
            }
            set {
                if (this._<%=propertyName%>.Entity != value) {
                    this.OnPropertyChanging("<%=propertyName%>");
                    if ((this._<%=propertyName%>.Entity != null)) {
                        <%=propertyName%> temp = this._<%=propertyName%>.Entity;
                        this._<%=propertyName%>.Entity = null;
                        temp.<%=TableName(table)%>.Remove(this);
                    }
                    this._<%=propertyName%>.Entity = value;
                    if ((value != null)) {
                        value.<%=TableName(table)%>.Add(this);
                    }
                    this.OnPropertyChanged("<%=propertyName%>");
                }
            }
        }
<%    } %>
<%    if (reverseForeignKeys.ContainsKey(table)) {
        foreach(TableKeySchema keySchema in reverseForeignKeys[table]) {
            string propertyName = TableName(keySchema.ForeignKeyTable);%>
        [global::System.Data.Linq.Association(Name="<%=keySchema.Name%>",
            Storage="_<%=propertyName%>", OtherKey="<%=AttributeColumnList(keySchema.PrimaryKeyMemberColumns)%>",
            ThisKey="<%=AttributeColumnList(keySchema.ForeignKeyMemberColumns)%>")]
        public IEnumerable<<%=EntityName(keySchema.ForeignKeyTable)%>> <%=propertyName%> {
            get {
                return this._<%=propertyName%>;
            }
        }

<%             }
        } %>
        public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanging;
        public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        protected void OnPropertyChanging(string propertyName) {
            if (this.PropertyChanging != null)
                this.PropertyChanging(this, new global::System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }

[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        protected void OnPropertyChanged(string propertyName) {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new global::System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
<%     } %>
}

[)amien

Guernsey Software Developers Forum, June 28th

June 2007 – March 2011 Guernsey (, , ) • 2,539 views • 3 responses

Changes

A number of changes are happening within the Guernsey Developer Users Group!

We have changed the name to the Guernsey Software Developers Forum to better reflect the nature of the events and moved the Guernsey Software Developers Forum web site over to a new location, host and software.

It is also our intention to arrange presentations as often as possible as fewer members were interested in the free-format discussion events.

Next event

The next event will be on Thursday 28th of June 2007 starting at 6pm at the Guernsey Training Agency offices above the post-office in Smith Street, St. Peter Port.

As always the doors are open to everyone so if you have an interest in any of these subjects just turn up and grab a chair. The planned agenda is:

Opening

Welcome to the forum, news and announcements.

(6:00 pm)

XP2007 Conference Report

The XP2007 8th International Conference on Agile Process in Software Engineering and Extreme Programming took place this month in Como, Italy.

Filippo Borselli will be sharing what he learnt over the course of this five day event as he attended many of the keynotes, tutorials, workshops and panels on offer.

(30 minutes)

The OGRE 3D Graphics Engine

OGRE is an open source library that makes it easier to render highly detailed virtual worlds in real time using the latest in consumer graphics hardware.

The founder of the project, Steve Streeting, will introduce some basic 3D graphics principles, show what OGRE brings to the table, a quick technical overview and code example, and will end with some sample demos and videos from people around the globe who use it.

(45 minutes)

Questions & Answers

Questions and answers session.

Group Discussion

Open-format discussion between attendees.

Closing

Discuss the date for the next event, determine what presentations are possible and what attendees would like to see.

(8:00 pm)

[)amien

Discarding new entity objects in LINQ to SQL beta 1

June 2007 – September 2009 .NET (, ) • 1,149 views • 2 responses

An unusual bug in .NET Framework 3.5 beta 1 that relates to creating a new entity object in LINQ to SQL has been getting in my way.

Bug description

When you create a new entity object it is not automatically added to the database unless you either attach it to the table or create an association to another existing entity.

The problem arises when you decide to discard the new object instead of persisting it. Setting the property of the association(s) to null should achieve this and the LINQ to SQL designer code shows that it removes the new object from the association.

When SubmitChanges is called it is apparent that the new object has not been discarded but is in fact attempting to persist to the database.

Reproduction

  1. Create a new C# Console project
  2. Add a new a LINQ to SQL file and name it Northwind
  3. Use Server Explorer to drag Orders and Customers tables from a Northwind database connection
  4. Paste the following code snippet into the static main method
  5. Run the program
  6. Examine the Orders table and notice a new a record consisting of null fields
NorthwindDataContext dc = new NorthwindDataContext();
Customer alfredCustomer = dc.Customers.Single(c => c.CustomerID == "ALFKI");
Order newOrder = new Order();
newOrder.Customer = alfredCustomer;
newOrder.Customer = null;
dc.SubmitChanges();

Workaround

It would appear there is at least a temporary workaround which is to force the association that was modified to reload. You can do this by calling the associations Load method after the entity has been de-associated but before SubmitChanges.

In our reproduction this would therefore be:

newOrder.Customer = null;
alfredCustomer.Orders.Load();
dc.SubmitChanges();

Calling Load does not disrupt other still-valid associations that have not yet persisted.

Curiously accessing the Count property of the association also causes the problem to disappear because the EntitySet<T> class that is responsible for representing these one-to-many associations calls Load every time you access Count. This is a potential performance hit as accessing a Count property is considered to be a fast operation that does not go off and rebuild collections.

Here’s hoping that Microsoft fix those two bugs before release and ideally before beta 2.

[)amien

Guernsey mobile plans

June 2007 – April 2008 Guernsey (, , , , ) • 1,180 views • one response

Now that Sure and Wave/JT are operating in both islands competition is heating up unless you want 3G in Guernsey because the government awarded the licence to somebody who doesn’t operate whilst refusing a licence to the biggest operator. Way to go.

Basic plans

Wave/JT should be commended for offering the same plan and rates across the islands.

Sure have some balancing to do between island and need to offer a lightweight plan below their basic £20 a month rental in Guernsey. They do offer cheaper rental pricing when taking out an 18 month contract however.

Both operators have a make-your-own-plan scheme available.

Locations Plan Months Price £ Minutes Texts Pictures Data KB
Gsy & Jsy Wave/JT 25/25 12 9.99 25 25 0 0
Gsy C&W Islander1 12 10.00   30 0 0
Jsy Sure 120 12 12.50 120 60 5 0
Gsy C&W Roamer 12 15.00 1002 1002 0 0
Gsy & Jsy Wave/JT 50/50 12 15.99 50 50 0 0
Gsy Sure 100 12 20.00 100 100 10 100
Gsy Sure 100 18 20.00 200 100 10 100
Gsy & Jsy Wave/JT 100/100 12 21.99 100 100 0 0
Gsy Sure 150 12 25.00 150 150 15 150
Jsy Sure 360 12 25.00 360 180 15 0
Gsy Sure 150 18 25.00 300 150 15 150
Gsy & Jsy Wave/JT 250/250 12 33.99 250 250 0 0
Gsy Sure 300 12 35.00 300 300 30 300
Gsy Sure 300 18 35.00 600 300 30 300
Gsy & Jsy Wave/JT 500/500 12 49.99 500 500 0 0
Gsy Sure 500 12 50.00 500 500 50 500
Jsy Sure 1000 12 50.00 1000 500 50 0
Gsy Sure 500 18 50.00 1000 500 50 500
Gsy & Jsy Wave/JT 1000/1000 12 69.99 1000 1000 0 0
  1. No roaming
  2. Minutes or texts, not both

Voice calls

Measured in £ per minute dependent on destination.

Destination Wave/JT Sure Gsy Sure Jsy
Local same network 0.07 0.10 0.10
Local other network 0.07-0.14 0.15 0.15
National land-line 0.10 0.10 0.10
National mobile 0.20 0.15 0.15
European land-line 0.20 0.20 0.25
European mobile 0.20 0.30 0.30
US & Canada 0.20 0.25 0.25
Rest of the world 0.20 0.75 0.99

Messaging

Measured in £ per-message independent of destination whilst you are on your home network.

Type Wave/JT Sure Gsy Sure Jsy
Text (non-premium) 0.07 0.10 0.06
Picture 0.20-0.80 0.25-0.30 0.25-0.50

Data packets

Measured in pence per kilobyte (KB).

Type Wave/JT Sure Gsy Sure Jsy
GPRS 0.293 0.100 0.100
3G 0.293 n/a n/a

Video calls

Measured in £ per minute dependent on destination and network.

Destination Wave/JT Sure Gsy Sure Jsy
Same network & island 0.07 n/a n/a
Same network 0.20 n/a n/a
National 0.45 n/a n/a
Same network 0.55 n/a n/a

[)amien