Archive for June, 2007
CodeSmith template to generate LINQ To SQL Data Context
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
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
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
- Create a new C# Console project
- Add a new a LINQ to SQL file and name it Northwind
- Use Server Explorer to drag Orders and Customers tables from a Northwind database connection
- Paste the following code snippet into the static main method
- Run the program
- 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
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 |
- No roaming
- 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