Tag Archives: MS SQL Server

Entity Framework is open source, why not SqlClient?

If you’re watching at least a little what’s going on in .NET world you noticed that Entity Framework is open source for couple months now. That’s a good thing.

On the other had there are still some pieces of the complete stack that are not open source. The one that I’m missing most is SqlClient. I’m writing provider for Firebird and although the MSDN documentation contains a lot of content sometimes it’s not obvious how things should behave. Then the SqlClient‘s behavior is (at least by me) taken as de-facto standard. I think everybody expects behavior that adheres to it. Over the last few years I’ve spent maybe thousands of hours trying things with SqlClient and ILDASM-ing into internals. But it could be easier if you could just step into the code and see the behavior, variables, … Also other people might look at the code and instead of just reporting bug posting also related info where to find the proper implementation or how it differs internally.

I wish to go back to this post in the foreseeable future and happily strike-though the complete text and just say that SqlClient is open source too.

Application using Entity Framework’s Code First to dynamically connect to two different databases

I was doing Entity Framework training roughly two weeks ago and normally I have 99% of people working with MS SQL. This however I had two guys from some company, where they worked with different databases, as the product they are working on is able to use couple of different databases. What a great setup. Finally I can show how to use Entity Framework with non-MS SQL databases.

They were interested particularly in Oracle Database, but I’m not that familiar with the 3rd party providers for Oracle Database so we agreed on using Firebird. ;) The code we wanted to try was to connect with same application (without recompile) to two different databases based on connection string (or connection object itself), without mapping hassle etc.

I chose Code First, because it’s the mapping itself is more separated from other parts than in EDMX (although the EDMX can be split to CSDL, MSL and SSDL files as well, you’ll then need to edit there manually or temporarily create EDMX file back again to use designer easily).

Let’s start with the structure. It’s basic Books – Authors example (sorry, names in Czech, but Book = Kniha, Author = Autor).
Firebird:

create table Knihy(
  ISBN varchar(20) character set ASCII primary key,
  Nazev varchar(100) character set utf8 not null,
  Cena decimal(10,4) not null,
  ID_Autor int not null
);
create table Autori(
  ID int primary key,
  Jmeno varchar(100) character set utf8 not null,
  Prijmeni varchar(100) character set utf8 not null
);
alter table Knihy add foreign key (ID_Autor) references Autori(ID);

MS SQL:

create table Knihy(
  ISBN varchar(20) primary key,
  Nazev nvarchar(100) not null,
  Cena money not null,
  ID_Autor int not null
);
create table Autori(
  ID int primary key,
  Jmeno nvarchar(100) not null,
  Prijmeni nvarchar(100) not null
);
alter table Knihy add foreign key (ID_Autor) references Autori(ID);

Now the C# code. I’ll start with classes, then basic Code First structure and finally the mapping.

Classes:

class Autor
{
	public int ID { get; set; }
	public string Jmeno { get; set; }
	public string Prijmeni { get; set; }
	public ICollection<Kniha> Knihy { get; set; }
}

class Kniha
{
	public string ISBN { get; set; }
	public string Nazev { get; set; }
	public decimal Cena { get; set; }
	public Autor Autor { get; set; }
	public int AutorID { get; set; }
}

First interesting point. The DbContext class needs to create proper mapping based on connection object used:

class MyContext : DbContext
{
	public MyContext(DbConnection connection)
		: base(connection, true)
	{ }

	protected override void OnModelCreating(DbModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);

		if (Database.Connection is FbConnection)
		{
			modelBuilder.Configurations.Add(new FirebirdAutorConfiguration());
			modelBuilder.Configurations.Add(new FirebirdKnihaConfiguration());
		}
		else if (Database.Connection is SqlConnection)
		{
			modelBuilder.Configurations.Add(new MSSQLAutorConfiguration());
			modelBuilder.Configurations.Add(new MSSQLKnihaConfiguration());
		}
		else
			throw new NotSupportedException();
	}

	public IDbSet<Autor> Autori { get; set; }
	public IDbSet<Kniha> Knihy { get; set; }
}

Looking at Database.Connection type I switch between two different configurations. So the DbContext itself isn’t dealing with the mapping.

Finally the mapping (second interesting point):

class MSSQLAutorConfiguration : EntityTypeConfiguration
{
	public MSSQLAutorConfiguration()
	{
		this.ToTable("Autori");
	}
}

class MSSQLKnihaConfiguration : EntityTypeConfiguration<Kniha>
{
	public MSSQLKnihaConfiguration()
	{
		this.HasKey(x => x.ISBN);
		this.Property(x => x.AutorID).HasColumnName("ID_Autor");
		this.HasRequired(x => x.Autor).WithMany(x => x.Knihy).HasForeignKey(x => x.AutorID).WillCascadeOnDelete(false);
		this.Map(map =>
			{
				map.Properties(x => new { x.ISBN, x.Nazev, x.Cena, x.AutorID });
				map.ToTable("Knihy");
			});
	}
}

class FirebirdAutorConfiguration : EntityTypeConfiguration<Autor>
{
	public FirebirdAutorConfiguration()
	{
		this.Property(x => x.ID).HasColumnName("ID");
		this.Property(x => x.Jmeno).HasColumnName("JMENO");
		this.Property(x => x.Prijmeni).HasColumnName("PRIJMENI");
		this.ToTable("AUTORI");
	}
}

class FirebirdKnihaConfiguration : EntityTypeConfiguration<Kniha>
{
	public FirebirdKnihaConfiguration()
	{
		this.HasKey(x => x.ISBN);
		this.Property(x => x.ISBN).HasColumnName("ISBN");
		this.Property(x => x.Nazev).HasColumnName("NAZEV");
		this.Property(x => x.Cena).HasColumnName("CENA").HasColumnType("decimal").HasPrecision(10, 4);
		this.Property(x => x.AutorID).HasColumnName("ID_AUTOR");
		this.HasRequired(x => x.Autor).WithMany(x => x.Knihy).HasForeignKey(x => x.AutorID).WillCascadeOnDelete(false);
		this.Map(map =>
		{
			map.Properties(x => new { x.ISBN, x.Nazev, x.Cena, x.AutorID });
			map.ToTable("KNIHY");
		});
	}
}

For MS SQL I took a chance to save some typing and let conventions to kick in and do some work for me. For Firebird, I had to type a little bit more. What may not be absolutely clear is fact, that the mapping classes (the ones derived from EntityTypeConfiguration<T>) can create hierarchy. It doesn’t have to be just one level deep. So if you have i.e. same column names in both databases you can use HasColumnName in some base class and use i.e. just HasColumnType and Map in derived classes used later to configure model builder.

And to test it actually works and produces expected queries and so on, simple test code.

static void Main(string[] args)
{
	Database.SetInitializer<MyContext>(null);
	var fb = new FbConnection("database=localhost:test;user=sysdba;password=masterkey");
	var mssql = new SqlConnection(@"Data Source=.\sqlexpress;Initial Catalog=test;Integrated Security=True");
	Test(fb);
	Test(mssql);
}

static void Test(DbConnection conn)
{
	using (MyContext c = new MyContext(conn))
	{
		Console.WriteLine(c.Autori
			.Where(a => a.Knihy.Any(k => k.Cena > 10.0m)).ToString());
	}
}

I hope everything is clear. If you have a question, comments are here for you, I’ll try my best to answer. If you’d like to have a training, let me know.

TeamCity, PowerShell and sqlcmd problem (and solution)

In my current project we’re using TeamCity as a continuous integration server and psake to run all out build and deployment tasks. Part of the deployment is execution of SQL scripts to create database and create structures in it. And as a heavy-duty console user, I’m using sqlcmd to do all my work with database. So I simply called sqlcmd with according parameters and to execute the scripts. Sadly for some strange reason, the PowerShell runner in TeamCity kept running in a loop eventually ending with timeout. Even worse, running it locally directly in PowerShell was fine. After small research done by my colleague we found the reason is sqlcmd.

The quick’n'dirty solution was to run it using Start-Process, sadly we lost the output (using -noNewWindow resulted in same problem), so any error was about guessing. Simple techniques, like redirecting output from cmdlet ended with same problem.

But I actually found a solution. I redirected the output from sqlcmd directly with -o switch and used Unicode using -u. Then I echoed the file via Get-Content cmdlet with -encoding Unicode switch.

After I rigorously specified the encodings for output and input, everything started running fine. Probably there was some problem with BOM being in input, causing headache to the runner.

Never mind, now it’s fine, and I’m happy, because I see errors in processing SQL scripts eventually.

Ultimate EFv4 CTP4 Code First (full mapping) example (using Firebird)

There’s a lot of content for latest CTP, CTP4, for Entity Framework “new features”. It’s mainly focused on Code First stuff, that fills the triad with Database First and Model First. I like Code First but what I also like is maintainable code. Hence I was more trying not to use convention-over-configuration, in opinion good for only small projects, and focus on specifying everything the way I want it to be, especially with my database structure (yes, I’m data consistency and storage freak).

Let’s model some kind of simple library and try to use there couple of Entity Framework’s features. And to make things worse, try to do it with ADO.NET provider for Firebird.

Good news is I succeeded. My database structure was my first and non-touchable object (to be honest the first was the idea what to create and how to represent it in database) together with the idea of objects.

Here’s the script for Firebird database (Did you noticed we’re fully Entity Framework 4 compatible?). See the quoted column names? Yes, not nice, but later in mapping in C# it looks nicer. ;) And I mixed the mapping with one for MS SQL Server (see below).

RECREATE TABLE Authors (
	"id" INT NOT NULL,
	"FirstName" VARCHAR(255) NOT NULL,
	"LastName" VARCHAR(255) NOT NULL,
CONSTRAINT PK_Authors PRIMARY KEY ("id")
);

RECREATE TABLE Books (
	"id" INT NOT NULL,
	"Discriminator" char(2) NOT NULL,
	"Title" VARCHAR(1000) NOT NULL,
	"Published" TIMESTAMP NOT NULL,
	"ID_Author" INT NOT NULL,
	"IssuesPerYear" SMALLINT,
	"Price" DECIMAL(9,0),
CONSTRAINT PK_Books PRIMARY KEY ("id")
);

RECREATE TABLE Languages (
	"id" INT NOT NULL,
	"LanguageName" VARCHAR(100) NOT NULL,
	"LanguageAbbrevation" CHAR(3),
CONSTRAINT PK_Languages PRIMARY KEY ("id")
);

RECREATE TABLE Translators (
	"id" INT NOT NULL,
CONSTRAINT PK_Translators PRIMARY KEY ("id")
);

RECREATE TABLE Translators_Languages (
	"ID_Language" INT NOT NULL,
	"ID_Translator" INT NOT NULL,
CONSTRAINT PK_Translators_Languages PRIMARY KEY ("ID_Language", "ID_Translator")
);

ALTER TABLE Books ADD CONSTRAINT FK_Book_Author FOREIGN KEY ("ID_Author")
REFERENCES Authors("id")
ON DELETE CASCADE
;

ALTER TABLE Translators_Languages ADD CONSTRAINT FK_TL_Languages FOREIGN KEY ("ID_Language")
REFERENCES Languages("id")
ON DELETE NO ACTION
;

ALTER TABLE Translators_Languages ADD CONSTRAINT FK_TL_Translators FOREIGN KEY ("ID_Translator")
REFERENCES Translators("id")
ON DELETE NO ACTION
;

ALTER TABLE Translators ADD CONSTRAINT FK_TypeConstraint FOREIGN KEY ("id")
REFERENCES Authors("id")
ON DELETE NO ACTION
;

So it’s time to create mapping right? Nope. Now I’ll switch my brain from ER thinking into OO thinking mode. Here’s the world of entities (sure I made some adjustments to fit some Entity Framework features, like Complex Types):

public abstract class Book
{
	public int ID { get; protected set; }
	public string Title { get; set; }
	public DateTime Published { get; set; }
	public Author Author { get; set; }
	public int AuthorID { get; set; }
}

public class RealBook : Book
{
	public decimal Price { get; set; }
}

public class Magazine : Book
{
	public short IssuesPerYear { get; set; }
}

public class Author
{
	public int ID { get; protected set; }
	public Name FullName { get; set; }
	public ICollection<Book> Books { get; set; }

	public Author()
	{
		this.Books = new List<Book>();
	}
}

public class Translator : Author
{
	public ICollection<Language> Languages { get; set; }

	public Translator()
	{
		this.Languages = new List<Language>();
	}
}

public class Language
{
	public int ID { get; protected set; }
	public string LanguageName { get; set; }
	public string LanguageAbbrevation { get; set; }
}

#region Complex Types
public class Name
{
	public string FirstName { get; set; }
	public string LastName { get; set; }
}
#endregion

As you see I’m using pure POCOs.

So far we haven’t touched any Entity Framework related stuff. So it’s time to create our context and all DAL related stuff. Mine is very simple and exposes only few properties and methods, just to keep it simple and focus on the aim.

public class LibraryContext : DbContext
{
	public LibraryContext(DbConnection connection)
		: base(connection)
	{
		this.ObjectContext.ContextOptions.LazyLoadingEnabled = false;
	}

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);

		modelBuilder.Configurations.Add(new BookConfiguration());
		modelBuilder.Configurations.Add(new AuthorConfiguration());
		modelBuilder.Configurations.Add(new TranslatorConfiguration());
		modelBuilder.Configurations.Add(new LanguageConfiguration());
		modelBuilder.Configurations.Add(new NameConfiguration());
	}

	public string CreateDatabaseScript()
	{
		return this.ObjectContext.CreateDatabaseScript();
	}

	public IDbSet<Book> Books
	{
		get { return this.Set<Book>(); }
	}

	public IDbSet<Author> Authors
	{
		get { return this.Set<Author>(); }
	}
}

public class DoNothingWithMyDatabase<TContext> : IDatabaseInitializer<TContext>
	where TContext : DbContext
{
	public void InitializeDatabase(TContext context)
	{ }
}

Pretty simple, isn’t it. I’m using new stripped down objects DbContext (← ObjectContext) and IDbSet/DbSet (← IObjectSet/ObjectSet).

You may also notice, I’ve created object which implements IDatabaseInitializer. That’s because, by default, Entity Framework will try to create database for you and check whether your database matches model (CreateDatabaseOnlyIfNotExists). I‘m controlling my database. You have to add somewhere into your application before doing anything with the context call to SetInitializer:

Database.SetInitializer(new DoNothingWithMyDatabase<LibraryContext>());

And finally we’re ready to dive into the mapping. Again, you can type the mapping directly into overridden OnModelCreating, useful if you have only couple of diversions from default conventions. I created configuration classes where I specified as much as I want. And I need (almost) everything to be nailed down (remember, data consistency and storage freak ;) ) as I don’t want to be surprised when something in database or in entities changes and application will start behave weird.

class BookConfiguration : EntityConfiguration&ltBook>
{
	public BookConfiguration()
	{
		this.HasKey(x => x.ID);
		this.Property(x => x.ID).IsIdentity();
		this.Property(x => x.Title).IsRequired().IsVariableLength().HasMaxLength(1000).IsUnicode();
		this.Property(x => x.Published);
		this.HasRequired(x => x.Author).WithMany(a => a.Books).WillCascadeOnDelete().HasConstraint((b, a) => b.AuthorID == a.ID);

		this.MapHierarchy()
			.Case<Book>(x => new
			{
				id = x.ID,
				Title = x.Title,
				Published = x.Published,
				ID_Author = x.AuthorID,
			})
			.Case<RealBook>(x => new
			{
				Discriminator = "B",
				Price = x.Price,
			})
			.Case<Magazine>(x => new
			{
				Discriminator = "M",
				IssuesPerYear = x.IssuesPerYear,
			})
#if FB
			.ToTable("BOOKS");
#else
			.ToTable("Books");
#endif
	}
}

class AuthorConfiguration : EntityConfiguration<Author>
{
	public AuthorConfiguration()
	{
		this.HasKey(x => x.ID);
		this.Property(x => x.ID).IsIdentity();
		this.HasMany(x => x.Books);

		this.MapHierarchy(x => new
		{
			id = x.ID,
			FirstName = x.FullName.FirstName,
			LastName = x.FullName.LastName,
		})
#if FB
		.ToTable("AUTHORS");
#else
		.ToTable("Authors");
#endif

		this.MapHierarchy().Case<Translator>(x => new
		{
			id = x.ID,
		})
#if FB
		.ToTable("TRANSLATORS");
#else
		.ToTable("Translators");
#endif
	}
}

class TranslatorConfiguration : EntityConfiguration<Translator>
{
	public TranslatorConfiguration()
	{
		this.HasMany(x => x.Languages).WithMany()
#if FB
			.Map("TRANSLATORS_LANGUAGES",
#else
			.Map("Translators_Languages", 
#endif
				(t, l) => new
				{
					ID_Translator = t.ID,
					ID_Language = l.ID,
				});
	}
}

class LanguageConfiguration : EntityConfiguration<Language>
{
	public LanguageConfiguration()
	{
		this.HasKey(x => x.ID);
		this.Property(x => x.ID).IsIdentity();
		this.Property(x => x.LanguageName).IsRequired().IsVariableLength().HasMaxLength(100).IsUnicode();
		this.Property(x => x.LanguageAbbrevation).IsFixedLength().HasMaxLength(3).IsUnicode();

		this.MapSingleType(x => new
		{
			id = x.ID,
			LanguageName = x.LanguageName,
			LanguageAbbrevation = x.LanguageAbbrevation
		})
#if FB
		.ToTable("LANGUAGES");
#else
		.ToTable("Languages");
#endif
	}
}

class NameConfiguration : ComplexTypeConfiguration<Name>
{
	public NameConfiguration()
	{
		this.Property(x => x.FirstName).IsRequired().IsVariableLength().HasMaxLength(255).IsUnicode();
		this.Property(x => x.LastName).IsRequired().IsVariableLength().HasMaxLength(255).IsUnicode();
	}
}

I don’t know whether it’s worth to describe the lines. Should be understandable if you know how the entities and database look like. Just maybe small notice. Besides Complex Types we used two most common inheritance mapping scenarions – TPH aka Table Per Hierarchy for Books and TPT aka Table Per Type for Authors (there’s also TPC (Table Per Concrete Type)). However if you have questions feel free to use comments, if I’ll know answer I’ll be happy to reply.

And finally some really simple application to test the result:
If you define #define FB it’ll use Firebird database else MS SQL Server.

Database.SetInitializer(new DoNothingWithMyDatabase<LibraryContext>());

Action<LibraryContext> doSomething = (context) =>
{
	Console.WriteLine(context.CreateDatabaseScript());
	var query = context.Books
		.Select(b => new
		{
			BookName = b.Title,
			AuthorName = b.Author.FullName.LastName + ", " + b.Author.FullName.FirstName
		})
		.OrderBy(x => x.BookName);
	Console.WriteLine((query as ObjectQuery).ToTraceString());
	var data = query.ToArray();
	foreach (var item in data)
	{
		Console.WriteLine("Book {0} written by {1}.", item.BookName, item.AuthorName);
	}
};

#if FB
using (LibraryContext context = new LibraryContext(new FbConnection(@"database=localhost:ctp4;username=sysdba;password=masterkey;pooling=true;")))
{
	doSomething(context);
}
#else
using (LibraryContext context = new LibraryContext(new SqlConnection(@"Data Source=.\sqlexpress;Initial Catalog=ctp4;Integrated Security=True;Pooling=False;MultipleActiveResultSets=True")))
{
	doSomething(context);
}
#endif

I’m glad I was able to figure out how to map everything without touching database structure (although it’s pretty straightforward) or letting the default rules to kick in. Hope it will help you if you struggle with something (and hope the naming will not change much in future). It’s also nice to see the whole infrastructure fits together and changing provider, in my case for Firebird’s, doesn’t make the code to blow out.

Running queries in parallel with Entity Framework (and not only with it)

From time to time I have to run two or more queries that I know will always be two or more – like some first/skip records and also total count. If you write it as two queries and execute, that means two round trips to database. Although it may not matter if the network latency is very small, why not to challenge myself and try to find some workarounds.

Sure you can create some stored procedures and get the data back from these, but I was thinking about more LINQ to Entitiesish way. I recalled a way I one time used inside one project. Although it was done in pure SQL, it, as it turned out, works, kind of, for LINQ to Entities as well.

The idea is using “one row table” and put the queries as columns. Let me demonstrate:

select
  (select foo, bar from table1 where ...),
  (select baz, foo from table2 where ...)
from OneRowTable;

Where the OneRowTable can be specially created table or i.e. for Firebird RDB$DATABASE or for Oracle Database dual. It isn’t the nicest SQL (and also challenges optimizer), but works. In columns as queries you can put anything you want as long as it is syntactically correct.

OK, what about the Entity Framework or LINQ to Entities respectively. I created the “one row table” first:

create table OneRowTable(x bit primary key);
insert into OneRowTable values (0);

The table needs to have the primary key to be able to import it into entity model, the datatype doesn’t matter (I was using MS SQL, hence the bit).

What about the queries? Similar approach:

var allinone = context.OneRowTable.Select(_ => new
{
	AData = context.a.Where(a => a.x.HasValue && a.x.Value > 10).Select(a => new { A1 = a.id, A2 = a.id * 2 }),
	BData = context.b.Where(b => b.id < 999).Select(b => new { B1 = b.id, B2 = b.y }),
});
string query = (allinone as ObjectQuery).ToTraceString();
var data = allinone.First();
var adata = data.AData;
var bdata = data.BData;

The a and b are my testing tables. You can check there’s only one query executed. Encapsulating this into some method is only piece of cake.

And how the query looks like? Well for my MS SQL test:

SELECT 
[UnionAll1].[x] AS [C1], 
[UnionAll1].[C2] AS [C2], 
[UnionAll1].[C1] AS [C3], 
[UnionAll1].[id] AS [C4], 
[UnionAll1].[id1] AS [C5], 
[UnionAll1].[C3] AS [C6], 
[UnionAll1].[C4] AS [C7], 
[UnionAll1].[C5] AS [C8], 
[UnionAll1].[C6] AS [C9]
FROM  (SELECT 
	[Project1].[C2] AS [C1], 
	[Extent1].[x] AS [x], 
	1 AS [C2], 
	[Project1].[id] AS [id], 
	[Project1].[id] AS [id1], 
	[Project1].[C1] AS [C3], 
	CAST(NULL AS int) AS [C4], 
	CAST(NULL AS int) AS [C5], 
	CAST(NULL AS varchar(1)) AS [C6]
	FROM  [dbo].[OneRowTable] AS [Extent1]
	LEFT OUTER JOIN  (SELECT 
		[Extent2].[id] AS [id], 
		[Extent2].[id] * 2 AS [C1], 
		1 AS [C2]
		FROM [dbo].[a] AS [Extent2]
		WHERE ([Extent2].[x] IS NOT NULL) AND ([Extent2].[x] > 10) ) AS [Project1] ON 1 = 1
UNION ALL
	SELECT 
	2 AS [C1], 
	[Extent3].[x] AS [x], 
	[Extent4].[id] AS [id], 
	CAST(NULL AS int) AS [C2], 
	CAST(NULL AS int) AS [C3], 
	CAST(NULL AS int) AS [C4], 
	[Extent4].[id] AS [id1], 
	[Extent4].[id] AS [id2], 
	[Extent4].[y] AS [y]
	FROM  [dbo].[OneRowTable] AS [Extent3]
	CROSS JOIN [dbo].[b] AS [Extent4]
	WHERE [Extent4].[id] < 999) AS [UnionAll1]
ORDER BY [UnionAll1].[x] ASC, [UnionAll1].[C1] ASC

Not exactly the original shape. The translator took another way creating two one row results and using union all to get it into one query. Except this, the query is in general the same (the explicit joins are as result same as the subselects, though little bit more confusing in this case).

Again, this isn’t general purpose way of doing it and may result in worse performance than running queries separately and I would recommend using it only after careful testing and on controlled limited set of queries.

Spatial data and Entity Framework – from real world usage

Julie Lerman created a post about spatial data usage in Entity Framework. Her conclusions are correct. But because from last three projects I did on Entity Framework two of them are working with spatial data (every customer wants a Google Maps or Bing Maps on website, it’s like cup holders in cars) I would like share my solution as well some ideas behind why I used this approach.

First and foremost Entity Framework doesn’t support spatial data now. So you have to create some workaround. The idea getting the data through blob is exactly what I created. In my tables where I need to save some coordinates (it’s mainly a point) I create simple column with this data type – I consider this clean initial work better, because maybe sometimes/somewhere/somebody will work with this structures so to have it clean. Then for the workaround I add XXX_bin column where I’ll store the binary representation of spatial data and use it for Entity Framework. Something like this:

[Location] geography Default geography::STGeomFromText('POINT EMPTY', 4326) NOT NULL,
[Location_bin] Varbinary(max) NOT NULL,

Because I’m working only through Entity Framework I need to update the Location automatically as there might be (and are) indices or other stuff working with it. To make it transparent I’m using insert or update trigger:

if (update(Location_bin))
begin
  update %tablename% set
    Location = geography::STGeomFromWKB(Location_bin, 4326)
  where
     ID in (select ID from inserted);
end

If you expect somebody updating the Location column directly too, you need to create another trigger and make sure you’ll not end up in infinite loop.

To support the – in my case – points for UI developers I create in every entity computed property turning Location_bin into my LatLong struct. This struct is using Microsoft.SqlServer.Types namespace.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Types;

namespace FooBar
{
    public struct LatLong
    {
        public double Lat { get; set; }
        public double Long { get; set; }

      public static LatLong FromSqlBytes(byte[] bytes)
      {
        SqlGeography g = SqlGeography.STGeomFromWKB(new SqlBytes(bytes), 4326);
        return new LatLong { Lat = g.Lat.Value, Long = g.Long.Value };
      }
  
      public byte[] ToSqlBytes()
      {
        return SqlGeography.Point(this.Lat, this.Long, 4326).STAsBinary().Buffer;
      }
    }
}
public LatLong Location
{
  get
  {
    return LatLong.FromSqlBytes(this.Location_bin);
  }
  set
  {
    this.Location_bin = value.ToSqlBytes();
  }
}

This creates almost seamless support for other developers when using spatial data in Entity Framework – inserting, updating, deleting all works without notice from others (if you’re not looking into internal implementation).

There are only two problems, both solvable.

First one is that developer cannot filter/sort/… using Location property. This isn’t probably a big problem. As (s)he doesn’t have spatial methods (like i.e. Distance) available, so the queries will not make sense. Which brings me to the other problem and that’s the querying. With EFv1 I created stored procedures for these queries and returned data through these. Same works for EFv4. If you have strict set of results you need, this is probably easiest solution. The other one, working in EFv4 is LINQ function imports via EdmFunction attribute. For computations I’m going to use I create function in T-SQL doing the work and I import it into my solution thru above noted attribute. The simple Distance method may look like:

create function Distance
(
  @Location_bin1 varbinary(max),
  @Location_bin2 varbinary(max)
)
returns int
as
begin
  return geography::STGeomFromWKB(@Location_bin1, 4326).STDistance(geography::STGeomFromWKB(@Location_bin2, 4326));
end

And

[EdmFunction("model.Store", "Distance")]
internal static int Distance(this byte[] location1, byte[] location2)
{
  throw new NotSupportedException();
}

Sure, when calling this function for a big resultsets it may be a performance bottleneck (I recommend doing there as much work as possible, even at the price of more functions doing similar stuff, to not create a deep function calls.), but with additional filters it works well.

protected IQueryable<Something> SomethingInDistance(LatLong point, int distance)
{
  byte[] spatialData = point.ToSqlBytes();
  return this.Somethings.Where(o => o.Location_bin.Distance(spatialData) < distance);
}

Another way could be to create a view (if you know the parameters in advance) and do explicit join (table/entityset <> view/entityset) in EF. Or do the filtering on client (if reasonably small). And I’m sure I’ll find other ways how to get the data you want back, simply choose what fits your needs best.

Although it may look like a lot of work, it isn’t. In fact you’re pretty fast (there’s a high level of reusability) if you know what to do (and after one implementation you will).