Posts Tagged ‘LINQ’
Few days ago I posted an extension method to run projection on a collection in parallel. The method has one problem. It’s not dealing with exceptions. And because the ordering wasn’t (and isn’t) implicitly preserved, I did this small improvement.
Right now the method returns simple structure with original item, the result (if no exception occured, sure) and exception (if any). I didn’t went to AggregateException (although you can modify the code yourself to use it). Now you can decide while consuming what to do when exception occurred. Adding some kind of cancellation shouldn’t be difficult.
The idea behind is the same as in previous version.
#region ParallelProjection
internal struct ParallelProjectionResult<TSource, TResult>
{
public TSource Item { get; set; }
public TResult Result { get; set; }
public Exception Exception { get; set; }
}
internal static IEnumerable<ParallelProjectionResult<TSource, TResult>> ParallelProjection<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> projection, int maxParallelism)
{
BlockingCollection<ParallelProjectionResult<TSource, TResult>> results = new BlockingCollection<ParallelProjectionResult<TSource, TResult>>();
ThreadPool.QueueUserWorkItem((o) =>
{
Semaphore semaphore = new Semaphore(maxParallelism, maxParallelism);
CountdownEvent countdown = new CountdownEvent(1);
try
{
foreach (var item in source)
{
countdown.AddCount();
semaphore.WaitOne();
ThreadPool.QueueUserWorkItem(
(element) =>
{
TSource e = (TSource)element;
ParallelProjectionResult<TSource, TResult> result = new ParallelProjectionResult<TSource, TResult>();
result.Item = e;
try
{
result.Result = projection(e);
}
catch (Exception ex)
{
result.Exception = ex;
}
results.Add(result);
semaphore.Release();
countdown.Signal();
},
item);
}
countdown.Signal();
countdown.Wait();
results.CompleteAdding();
}
finally
{
if (countdown != null)
countdown.Dispose();
if (semaphore != null)
semaphore.Dispose();
}
}, null);
return results.GetConsumingEnumerable();
}
#endregion
Here’s updated version of the method.
I have here another not-general-purpose-parallel/multihtreaded-method.
To make a long story short I needed do some transformation on collection’s elements, aka projection. Unfortunately the method I was plugging in was doing some network requests, in fact couple of requests. Sequentially, blocking processing until the response came back. I know a proper way will be to turn these requests into asynchronous, unluckily this was part of bigger architecture I could not change. Because it’s not CPU bound the AsParallel method would not help much. So I solved it abusing ThreadPool threads. Bad for scheduler and memory, as I’ll be wasting threads and resources, blocking, until reply is sent by server, but very easy for me. I told you, abusing.
So I came with this method. It’s utilizing new .NET Framework 4 concurrent collections, BlockingCollection in particular as it’s great for producer-consumer scenario and I want the method to return results whenever one is done (that also implies the ordering isn’t preserved).
internal static IEnumerable<TResult> ParallelProjection<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> projection, int maxParallelism)
{
BlockingCollection<TResult> results = new BlockingCollection<TResult>();
ThreadPool.QueueUserWorkItem((o) =>
{
Semaphore semaphore = new Semaphore(maxParallelism, maxParallelism);
CountdownEvent countdown = new CountdownEvent(1);
try
{
foreach (var item in source)
{
countdown.AddCount();
semaphore.WaitOne();
ThreadPool.QueueUserWorkItem(
(element) =>
{
results.Add(projection((TSource)element));
semaphore.Release();
countdown.Signal();
},
item);
}
countdown.Signal();
countdown.Wait();
results.CompleteAdding();
}
finally
{
if (countdown != null)
countdown.Dispose();
if (semaphore != null)
semaphore.Dispose();
}
}, null);
return results.GetConsumingEnumerable();
}
The method is straightforward, a lot of work was saved using the smart blocking collection. I’m simply reading items from the collection and applying the function to them. To not overload the system with huge number of threads I also added maxParallelism parameter. When this number of threads is processing items, I’ll stop scheduling more, using Semaphore, until some are done and again available. When there’s no item in source collection available and all item were processed I call CompleteAdding method to say I’m done and there will be no other items. Here I’m using CountdownEvent class initialized to 1 as you can’t, of course, add items if it reaches 0. Before final Wait I’m subtracting one to compensate this.
And that’s it. Again, it’s not general purpose method. Use with care, it may bring you even worse performance if wrongly used.
Scott Guthrie recently posted article about New Embedded Database Support with ASP.NET. This made me think about other options, Firebird in particular, and advantages and disadvantages. What I’m going to do is to very shortly introduce Firebird Embedded here and then compare it with features Scott wrote in his article.
Firebird Embedded, shortly, is Firebird database server in one DLL. No need to install etc., just load this DLL and use it. To be precise, there are some other DLLs, i.e. to support national charsets, but it’s still in under 10MB all. The database itself is built from same sources as “full” server and it’s not limited in any way.
Works with Existing Data APIs – as I said, Firebird Embedded is based on same codebase as “full” server, thus the SQL and API is same. And not only this, the ADO.NET provider for Firebird works with it and you’re programming using same thinking.
No Database Installation Required and Database Files are Stored on Disk – databases created by Firebird Embedded are stored wherever you want, with any extension. Firebird itself doesn’t have any master database, thus even the “full” server works with any (valid database) file.
Shared Web Hosting Scenarios Are Now Supported with SQL CE 4 – sure you don’t need to install anything with Firebird Embedded, that’s why it’s called Embedded. And not only this, from version 2.5, you can open database from different processes, i.e. IIS worker process and Apache workers or some console application doing something in background.
Visual Studio 2010 and Visual Web Developer 2010 Express Support – as the tool set for Firebird Embedded is exactly same as for “full” Firebird, you can use DDEX (aka Server Explorer support), Entity Framework (LINQ), …
Supports Both Development and Production – this is something I’m silently expecting. But yes, you can do the same with Firebird Embedded too.
Easy Migration to SQL Server – ahh, my favorite point. The Firebird Project has of course whopping number of tools to support migration from Embedded to “full”. The most used is … nothing. The databases are fully compatible and you can switch servers without any other tools, migration, conversion, … Just place it where you want it and connect to it, either with “full” or Embedded Firebird. And to switch your application? Again nothing. Same ADO.NET provider, just change connection string, if needed. Mostly you’ll add server IP address and maybe different path and manually switch server type, if you want. Really the migration is so simple. It is one minute task and thanks to same tool set, no matter what you version your targeting during development, your application will work with the other as well without any additional effort.
You like Firebird Embedded? I do, a lot. SQL Server Compact Edition 4 looks promising, but Firebird has something to offer as well. And recall, it’s based on same sources as “full” Firebird, very mature codebase, examined with tons of installations.
Yesterday I needed to put one element at beginning of the collection I already had. Some kind of Concat upside down.
As you can use the Concat method, it looks weird when you see the code, because two items are actually swapped. So I created a simple extension method to do it for me.
I started with:
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> source, T item)
{
yield return item;
foreach (var x in source)
yield return x;
}
It’s classic imperative approach, you’re expressing how you’ll do it. Then I thought: “Hey, why not to use LINQ methods already available.”. As you guess, I abused Concat method as I wrote above:
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> source, T item)
{
return new[] { item }.Concat(source);
}
You can easily extend both methods to accept also collection as a second parameter.
If you’re more about declarative programming you’ll probably like the other one. But choose whatever fits your brain better.
The ADO.NET provider for Firebird now fully supports all the new features in Entity Framework 4. Simple. If you’re eager to test it, grab the weekly build and enjoy.
We support the internal improvements as well as the visible changes like i.e. Model First or CreateDatabaseScript method. The template for creating SQL script is now part of sources and sure will be included in final package as well. I expect to improve it on your feedback and also the model generation from designer is pluggable so you can create custom one and use it there.
To support some new features in SQL generation I had to tweak it little bit and as with every change, there’s a change that something goes wrong. Thus I would be more than happy to get some feedback either that it works OK or any queries where it fails.
I’m so happy to cross this milestone about two months after final Visual Studio 2010, .NET Framework 4 (incl. Entity Framework 4) were released. You can expect the official release after some testing, it’s your turn
.
Though Entity Framework 4 doesn’t support bulk action, you can do it. As Matthieu Mezil shows, it’s possible. Nice piece of code, Matthieu.
If you don’t wanna play with it and go straight, you can create a stored procedure for action you need to call it. That’s how I’m doing it. Maybe in next version of EF somebody will think about bulk actions and we’ll see it.
And the v3/v4 is pretty awesome. It’s really going through a lot of options and dealing with it. Worth at least looking at it and quickly thinking about it.
As it may look like nothing is going on, it’s not true. Next to Firebird 2.5 new protocol features, I’m also working on Entity Framework v4 support. Before I go further, be sure, that all providers written for Entity Framework v1 are also working with v4.
In fact right now all the major improvements in Entity Framework v4 are supported. You can benefit from features available, like the LIKE translation support or plenty of new functions. My personal favorite is TruncateTime (so I can get rid of workaround). The Model First approach is next in a row. At least basic T4 template for start is my aim. The rest could be done by you, simply modifying the template. And also wiring the template into code so you can use it programmatically too. Under the cover, while working on new stuff I’m also finding ways to optimize the code. Luckily the changes will be noticeable.
The DDEX for Firebird supports Visual Studio 2010 and the full Entity Framework v4 support will be here soon – now you can try a weekly build. Feel free to ask about anything related.
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.
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).
Today I seen a code with good chance of hard to find mysterious bugs. Let’s start with database table structure we’re going to use for demonstration.
create table NullTest(id int primary key, b bit);
insert into NullTest values (0, 1);
insert into NullTest values (1, 0);
insert into NullTest values (2, null);
If you now try to query this table via LINQ (i.e. LINQ to Entities) you may get surprising results.
foreach (var item in ent.NullTests.Where(x => x.b != true))
{
Console.WriteLine(string.Format("ID: {0} t B: {1}", item.id, item.b));
}
Console.WriteLine("===");
foreach (var item in ent.NullTests.AsEnumerable().Where(x => x.b != true))
{
Console.WriteLine(string.Format("ID: {0} t B: {1}", item.id, item.b));
}
If you run this code, you’ll get different results.
ID: 1 B: False
===
ID: 1 B: False
ID: 2 B:
What happened? Is the database engine doing something wrong? Or is there a bug in LINQ? Neither of those. In fact both results are correct. The second one (evaluated in .NET on client) is obvious why it’s as is. But what happened in processing of first one (evaluated on database side)? The devil is in NULL logic. Every operation with NULL results in NULL or false if it’s a boolean operation. And this exactly explains the inconsistent result. In .NET null != true is true but in databases it’s false (because of the NULL rules described above).
Thus if you’re writing LINQ query for database, although the impedance mismatch should be hidden from you when using LINQ, you need to take into account different NULL handling in database engines and in .NET (or any common programming language).
Remember the DBNull.Value? That was explicit solution for this “problem”.
From time to time you may need to compare only date part of datetime/timestamp field in your database i.e. for filtering. In EFv1 this is a little bit problem, in EFv4 (now beta 2) better.
Let’s start with EFv1. If you try to write something like this:
.Where(x => x.DateTimeColumn.Date == DateTime.Today)
You’ll end up with nice exception. Simply EF is not able to translate it. To solve this problem I created handy (for me) method, that will do simply expansion comparing Day, Month, Year, so you don’t have to write it over and over again.
public static Expression<Func<TElement, bool>> DateEqual<TElement>(Expression<Func<TElement, DateTime>> valueSelector, DateTime dt)
{
if (valueSelector == null)
throw new ArgumentException("valueSelector");
ParameterExpression p = valueSelector.Parameters.Single();
Expression ex = Expression.And(
Expression.Equal(
Expression.MakeMemberAccess(valueSelector.Body, typeof(DateTime).GetMember("Day").Single()),
Expression.Constant(dt.Day)
),
Expression.And(
Expression.Equal(
Expression.MakeMemberAccess(valueSelector.Body, typeof(DateTime).GetMember("Month").Single()),
Expression.Constant(dt.Month)
),
Expression.Equal(
Expression.MakeMemberAccess(valueSelector.Body, typeof(DateTime).GetMember("Year").Single()),
Expression.Constant(dt.Year)
)
)
);
return Expression.Lambda<Func<TElement, bool>>(ex, p);
}
So you can write i.e.:
.Where(Ext.DateEqual<DateTimeEntity>(x => x.DateTimeColumn, DateTime.Today.AddDays(-20)))
One modification I have in my head, is to extend it to support comparing two columns in database. But that shouldn’t be hard.
On the other hand, EFv4 contains couple of new canonical functions, for datetime/timestamp as well. One that’s useful for this case is TruncateTime, also exported for LINQ usage with EdmFunctionAttribute. With it, you can write queries little bit easier. Still not directly .Date, but i.e.:
.Where(x => EntityFunctions.TruncateTime(x.DateTimeColumn) == DateTime.Today)
Comparing two columns is available too. Sure, your provider needs to support this new function, but that should be no problem for all provider writers.
Maybe the future EF improvement could be to support .Date for translation too.
Nová verze Entity Frameworku už pomalu klepe na dveře a proto není od věci podívat se, co nového nabídne. V rámci přednášky na MS Festu projdu největší změny a vylepšení. Rozhodně je na co se těšit – pokud již EF používáte, tak na ulehčení života a pokud ještě ne, tak na ještě větší lákadla jej použít. A vzhledem k plánované večerní akci bude prostor i pro kuloární diskuze, nejen o EF.
Next Firebird conference is coming. This year in München, Germany. I’ve never been in München though it’s pretty close to Czech Rep. thus I’m looking forward.
You can find info about conference at firebird-conference.com. I’ll be speaking there too, hence if you’re interested in Firebird and .NET you should definitely come. Every day I have one session. I’ll be covering new (2.5+) protocol implementation in .NET client for Firebird 2.1, Entity Framework support (also new in 2.5) and finally you’ll see how to create Windows phone (formely PocketPC, …) application and accessing Firebird database.
And the conference will be also great place to meet people you know from list etc. and talk face to face about your Firebird related problems, challenges and solutions. If you have any specific need to show (related to my three sessions) feel free to drop line in comments.
Pokud jste nestihli první pražskou nebo brněnskou přednášku, máte v Praze další šanci. 3.11.2009 (úterý) od 17:20 v rámci programátorských večerů na MFF UK.
Více info zde nebo zde, registrace na http://akce.altairis.cz.
Prezentace ke stažení.
Model Defined functions are new feature in EFv4. You simply define you function using EDM functions etc. in your model and then you can use it in your queries. With EdmFunction attribute you can also create stub function to use it in LINQ queries. That’s all great, and even itself makes life with Entity Framework easier.
But if you call it from LINQ (my favourite way of querying), it’s kind of odd. You’re writing it as:
context.Persons2.Where(p => GetAge2(p) < 100);
And while I was preparing some demos for my presentation, there was a flash of idea in my head. “What if I define the function stub as extension method?”, I thought. Yes like:
[EdmFunction("testovaciModel", "GetAge2")]
static int GetAge2(this Persons2 p)
{
throw new NotSupportedException();
}
This should work, right? It’s just sugar and the translation should work without complaining. And it really does. You can now write:
context.Persons2.Where(p => p.GetAge2() < 100)
Sweet! You can still keep these MDF method stubs in one place but use it in more natural syntax.
And by the way, it works for store functions as well (you’re just limited on types).
[EdmFunction("testovaciModel.Store", "GetAge")]
static int GetAge(this DateTime born)
{
throw new NotSupportedException();
}
context.Persons2.Where(p => p.Born.GetAge() < 100)
I’m especially happy for store function exposed to LINQ. I’m using these in a reasonable amount in my databases and being able to filter using the function without wrapping the query into i.e. stored procedure or view is neat.
20.10.2009 (úterý) od 17:00 budu přednášet o novince (je to ještě novinka, když .NET 4 je za dveřmi?) v .NET 3.5 SP1 – Entity Framework. Tentokrát v Brně. Stejně jako v Praze se podíváme na nové vlastností, které přinese Entity Framework v4 v .NET 4, stejně tak, pokud zbyde čas, přijde na řadu rychlé info o ADO.NET Data Services (Astoria).
Registrace na http://wug.cz/Aktuality/tabid/36/ctl/Detail/mid/492/ItemId/303/language/cs-CZ/Default.aspx.
Prezentace ke stažení.
Firebird doesn’t have identity/autoincrement columns like i.e. MS SQL or MySQL. Firebird has concept of generators/sequences (as know i.e. in Oracle). This is more powerful concept, but comes also with drawbacks, because you can say for sure, whether the column values is generated or not. This is causing problems, if you set StoreGeneratedPattern in your model manually and then updated model from database – it’s lost. Because this can be big pain in the ass, FirebirdClient can now report the column as “Identity” if you give it little help.
If you put into comment of column #PK_GEN#, then FirebirdClient will report it as generated primary key, resulting in StoreGeneratedPattern to be set to “Identity”. Hence you don’t have to every time manually change the model and easily use automatic fetching of the value from database when saving changes. If you wanna test it, grab build from weekly builds.
13.10.2009 (úterý) od 17:30 budu přednášet o novince (je to ještě novinka, když .NET 4 je za dveřmi?) v .NET 3.5 SP1 – Entity Framework. A nejen to. Zabředneme také do nových vlastností, které přinese Entity Framework v4 v .NET 4. Pokud zbyde čas, na řadu přijde rychlé info o ADO.NET Data Services (Astoria).
Více info, včetně registrace na http://akce.altairis.cz/Events/298.aspx.
Prezentace ke stažení.
Most of the time you’re using properties in objects that are also in database. But sometimes you may need to create property in object that’s not in database – it’s used only in this application or it’s there custom logic. Then, if you wanna use it in queries, you’re out. You can use only those properties declared in table, obviously.
But there’s a solution. First, the property has to be created from some other properties, so you’re able to do the same in database. And of course, functions used there needs to be translatable to store language too. This may limit you, but there’s nothing you can do about it, except evaluating query on client side.
Let’s have a table like this:
create table Foo (
ID int primary key,
IsAccepted bit,
IsPaid bit not null,
IsPacked bit not null,
);
And you wanna have property IsReadyToShip, in application. So you create:
public bool IsReadyToShip
{
get { return this.IsAccepted.HasValue && this.IsAccepted && this.IsPaid && this.IsPacked; }
}
But with this property you’re not able to query for all Foos ready to ship. Luckily the solution is pretty easy. First you’ll create expression for this:
public static Expression<Func<Foo, bool>> IsReadyToShipExpression = f => f.IsAccepted.HasValue && f.IsAccepted && f.IsPaid && f.IsPacked;
Then you’l prepare static compiled version of this expression, just for performance reasons, you can compile it in getter everytime too:
protected static Func<Foo, bool> IsReadyToShipFunc = IsReadyToShipExpression.Compile();
And finally the property:
public bool IsReadyToShip
{
get { return IsReadyToShipFunc(this); }
}
Right now we have the same result as before – working property. But because we also have the expressions of the property (and incidentally it’s translatable to store language
), so we can use it for querying. You can use it in an easy way (this is in this case ObjectContext used in Entity Framework):
public IQueryable<Foo> FoosReadyToShip
{
get { return this.Foos.Where(Foo.IsReadyToShipExpression); }
}
Not the shortest way to do it. But if you don’t wanna to write the condition again and again (and lower the maintainability) this is a way to do it.