Posts Tagged MS SQL Server

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).

Tags: , , , , ,

SQL command when inserting M:N association with identity columns in the underlying table

Today I uncovered a magic command from Entity Framework v4 when you create M:N association and the underlying table is defined with both columns as identity (you insert there). I don’t what’s it good for, as this table in fact only stores the two IDs to connect other tables. But somebody may build some logic on identity there, sure.

When I first saw the command, I was completely stunned. I had no idea what’s going on there and whether I see there one or more commands. You can have fun too:

declare @generated_keys table([ID_A] int, [ID_B] int)
insert [dbo].[A_B]
output inserted.[ID_A], inserted.[ID_B] into @generated_keys
default values
select t.[ID_A], t.[ID_B]
from @generated_keys as g join [dbo].[A_B] as t on g.[ID_A] = t.[ID_A] and g.[ID_B] = t.[ID_B]
where @@ROWCOUNT > 0

As an old school guy I was first looking for semicolons and then later tried to decode it by “parsing” the content.

Isn’t it nice… :)

Tags: , ,

Unique constraint on MS SQL?!

I have a strong feeling that MS SQL knows I don’t like it. I don’t say it’s a bad database, but sometimes it’s really “interesting”. ;)

OK, so what I faced this time? Let’s start with basic facts. The NULL value in database is a special value. NULL and NULL is again NULL, NULL and False is again NULL and NULL isn’t equal to NULL. Two days ago I was creating a chain of records in database (which isn’t too relationalish, but …) and I needed unique constraint on one column. No problem you may think. So did I. But not in MS SQL.

Check this example:

1> create table test(id int primary key, foo int);
2> create unique index idx_text on test(foo);
3> go
1> insert into test values (1, null);
2> go

(1 rows affected)
1> insert into test values (2, null);
2> go
Msg 2601, Level 14, State 1, Server X2-001\SQLEXPRESS, Line 1 Cannot insert duplicate key row in object 'dbo.test' with unique index 'idx_text '.
The statement has been terminated.

What a mess! Looks like somebody in MS SQL engine team thinks NULL == NULL. After some railing, testing on Firebird and asking my friend about Oracle, I googled: https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=299229. Looks like I’m not alone; feeling better. ;)
Luckily I’m working on MS SQL 2008 and I can use filtered indices. Created unique index with where clause where <column> is not null and I’m done. But boy that was a bitter finding. Hope I’ll not be punched to the face with these basics again in an at least two months.

Tags: , , ,

First touches on Code Only in EF4

I finally got my hands on the new feature in EF4, Code Only. It’s now available in feature CTP. What’s the code only? Very shortly you write just a code and express your mapping in code as well. There’s no model, no edmx file, no CSDL, MSL, SSDL files.

The current version is pretty limited, it’s more preview than something you can do some work with (which I wasn’t aware of and I was little disappointed). You can read more about what will be available on http://blogs.msdn.com/efdesign/archive/2009/06/10/code-only.aspx, http://blogs.msdn.com/efdesign/archive/2009/08/03/code-only-enhancements.aspx and http://thedatafarm.com/blog/data-access/next-version-of-ef-code-only-design-laid-out-by-ms/.

I mainly wanted to try it on a non standard (read: not “follow this demo”) MS SQL database and on Firebird as well. The first bad news is, that current CTP supports only SqlClient. And probably some following previews will too, as the other providers model will be built on top of it (but I’ll try to push this as much as I can, to see some Firebird demo soon). The other is, that the currently available version is limited, even features in first Code Only blog post are not working. :( Right now you can only create a context and use it for work with all defaults – default names for tables, default mapping etc.

using (SqlConnection conn = new SqlConnection(@"Data Source=.\sqlexpress;Initial Catalog=testovaci;Integrated Security=True;Pooling=False"))
{
	ContextBuilder<COContext> builder = new ContextBuilder<COContext>();
	COContext context = builder.Create(conn);
	string script = context.CreateDatabaseScript();
	context.Masters.ToArray();
}

Nothing special. If you try it with FirebirdClient, you’ll get NotSupportedException immediately. Dammit.

using (FbConnection conn = new FbConnection("database=localhost:rrr.fdb;user=sysdba;password=sysdba"))
{
	ContextBuilder<COContext> builder = new ContextBuilder<COContext>();
	builder.Create(conn);
}

Despite the frustration I found couple of new interesting extension methods (in Microsoft.Data.Objects, also ContextBuilder sits there). One of these is CreateDatabaseScript. This method works not only with Code Only created ObjectContext, but on every ObjectContext. So you can create your database creation script during runtime even if you’re using i.e. EDMX file easily. I’m also seeing great opportunity for this with Code Only setup during runtime. You can have different builds with different setups and create script for users based on selection. And by the way, there’s also CreateDatabase method available. But again right now works only with MS SQL only. But I know other databases will be supported as well, I have tested some pieces already.

I like the Code Only feature, maybe more than Model First (probably because I’m using ERD tools to model my databases). And I’m looking forward to see more progress on it, and also the model for 3rd party providers.

Tags: , , ,

How dumb the instead of triggers on MS SQL are???

Shortly: Very.

I hate the idea of instead of triggers and I made a couple of blog posts several times. And even worse is the implementation behavior on MS SQL. And the cascade constraints are bad too.

Let’s suppose this simple definition.

create table master(id int primary key, foo nvarchar(20));
create table detail(id int primary key, id_master int not null, bar nvarchar(20));
alter table detail add foreign key (id_master) references master(id) on delete cascade;

Nothing special. Works fine, no problems expected. Until you try to define instead of delete trigger on detail table. I.e.

create trigger tr_test on detail
instead of delete as
begin
  select 1;
  -- now the fun begins
end

You get an nice error: Cannot create INSTEAD OF DELETE or INSTEAD OF UPDATE TRIGGER 'tr_test' on table
'detail'. This is because the table has a FOREIGN KEY with cascading DELETE or
UPDATE.
. What the hell??? :o Why?

Again I got a direct proof, that in this area the MS SQL is wrong, very wrong. I would be willing to accept the limitation when I’ll be doing there some master table manipulation (yes, as I said, cascade constraints are bad too). But this? I can hardly believe my eyes. The trigger is almost empty. Or am I missing someting on backround that limits this to work?

Another “feature” that makes me love Firebird more.

Tags:

Subselects in computed columns in MS SQL

I found nice hack on MS SQL. On Firebird I’m using sometimes computed column(s) with select in definition. It can be performance (or concurrency) problem, but if you use it carefully it’s helpful. And I was missing this feature on MS SQL. But accidentally I found solution/workaround.

You can create a function, that will do the select and return the value you need. The only problem is, that you’re creating dependency between the column and the function.

You can create function for instance:

create function FooBar
(
  @ID int
)
returns bit
as
begin
  declare @result bit;

  if /* some code with (sub)select */
  begin
    set @result = 1;
  end
  else
  begin
    set @result = 0;
  end

  return @result;
end

And use it in column for table (it’s just example ;) ):

alter table Foo add Bar as (case when dbo.FooBar(ID) = 1 then 'foo' else 'bar' end);

Not nice as in Firebird where you can just use the select, but works. I haven’t measured any performance hit, but sure there will be some slowdown. But if you really need it … :)

Tags: ,

Instead of triggers in MS SQL and Entity Framework problem

I don’t like the model of instead of and after triggers in MS SQL. The before and after ones are, in my opinion, better. Anyway I needed to do some complex tests, not possible with check constraints, before inserting. Hence I jumped into instead of trigger. No problem in a view. Or not?

The Entity Framework is smart enough to get all server generated columns back when inserting (or updating) – see StoreGeneratedPattern. But the problem is how the identity column, often used for primary keys, is retrieved. The command issued after insert looks like (particular shape depends on other store generated columns and concurrency checks): select <PK column> from <some table> where @@ROWCOUNT > 0 and <PK column> = scope_identity().The problem is, that if you generate new PK value in trigger, it is different scope. Thus this command returns zero rows and EF will throw exception.

Hmm, this isn’t the way. :( So one of the solutions is move away from instead of triggers and use stored procedures (remember that you need all three) and result binding (you might be forced to create SPs (of fake ones in SSDL) for some related objects too). It’s not easiest solution, but works nice and you don’t have to introduce hacks etc.

Tags: ,