This StackOverflow question points out an issue with EntityFramework 4.1, when used with a SQL Server Compact table with image columns. The “image” type is the predecessor to varbinary(MAX), and is used for storing large binary values, sometimes referred to as BLOBs.
I have created a sample Console application with fixes for the related issues.
To re-create the application, install EntityFramwork 4.1, create a C# Windows Console application in Visual Studio 2010, and add .NET references to:
System.Data.Entity
EntityFramework
System.ComponentModel.DataAnnotations
Then add code similar to this:
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
//Also added reference to EntityFramework.dll (EF 4.1)
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace CfTest
{
class Program
{
static void Main(string[] args)
{
using (var db = new StudentContext())
{
var student = new Student { Name = "Erik", Photo = ConvertImageToByteArray(@"C:\Users\Erik\Pictures\erik.jpg"), StudentId = 1 };
student.LongText = new string('x', 6666);
db.Students.Add(student);
int recordsAffected = db.SaveChanges();
Console.WriteLine(
"Saved {0} entities to the database, press any key to exit.",
recordsAffected);
Console.ReadKey();
}
}
private static byte[] ConvertImageToByteArray(string fileName)
{
Bitmap bitMap = new Bitmap(fileName);
ImageFormat bmpFormat = bitMap.RawFormat;
var imageToConvert = Image.FromFile(fileName);
using (MemoryStream ms = new MemoryStream())
{
imageToConvert.Save(ms, bmpFormat);
return ms.ToArray();
}
}
}
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
// Required to force Code First to create a ntext column, not a nvarchar(n)
[MaxLength]
public string LongText { get; set; }
// Required to force Code First to create an image column, not a binary(n)
[Column(TypeName = "image")]
public byte[] Photo { get; set; }
}
public class StudentContext : DbContext
{
protected override bool ShouldValidateEntity(System.Data.Entity.Infrastructure.DbEntityEntry entityEntry)
{
// Required to prevent bug - http://stackoverflow.com/questions/5737733
if (entityEntry.Entity is Student)
{
return false;
}
return base.ShouldValidateEntity(entityEntry);
}
public DbSet<Student> Students { get; set; }
}
}
And add an app.config with contents like this. The connection string name matches the DbContext name and this causes Code First to magically create a database file in the specified location, with a Students table:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="StudentContext"
providerName="System.Data.SqlServerCe.4.0"
connectionString="Data Source=C:\Users\Erik\Documents\visual studio 2010\Projects\CfTest\Students.sdf"/>
</connectionStrings>
</configuration>
This is the resulting Students table:
-- Script Date: 11-07-2011 09:43 - Generated by ExportSqlCe version 3.5.1.3
CREATE TABLE [Students] (
[StudentId] int NOT NULL IDENTITY (1,1)
, [Name] nvarchar(4000) NULL
, [LongText] ntext NULL
, [Photo] image NULL
);
GO
ALTER TABLE [Students] ADD CONSTRAINT [PK__Students__000000000000000A] PRIMARY KEY ([StudentId]);
GO
This code demonstrates how to convert an image to a byte array, and also highlights the fixes required to work with image columns:
// Required to force Code First to create an image column, not a binary(n)
[Column(TypeName = "image")]
public byte[] Photo { get; set; }
The Photo column must be decorated with the Column attribute specifying a typeName of “Image.
// Required to force Code First to create a ntext column, not a nvarchar(n)
[MaxLength]
public string LongText { get; set; }
Likewise the LongText column must specify the MaxLength attribute.
public class StudentContext : DbContext
{
protected override bool ShouldValidateEntity(System.Data.Entity.Infrastructure.DbEntityEntry entityEntry)
{
// Required to prevent bug - http://stackoverflow.com/questions/5737733
if (entityEntry.Entity is Student)
{
return false;
}
return base.ShouldValidateEntity(entityEntry);
}
public DbSet<Student> Students { get; set; }
}
And validation must be disabled to prevent an error message saying:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details. This issue is described by the ADO.NET Team here.
UPDATE: @DamienGuard, of LINQ to SQL and EF Code First fame, pointed out that a better and more provider agnostic solution is to use MaxLength rather than TypeName = “ntext”.
6 kommentarer:
Hi Erik, this post is very interesting to me, but
now I have a question for you: suppose I need to assign student's IDs manually so I change table schema to reflect this; the StudentId column is still primary key but its identity property is set to false.
The code in Program.cs remains the same, I just introduce some logic to generate a new ID each time I need to add a student.
When I run the app I get a DbUpdateException on the db.SaveChanges() call telling me that I'm trying to assign a NULL value to the StudentId column. That's not true as I verified I assigned a new value to that column.
Why is this happening?
Thanks very much in advance
andrea
Hi, how can you introduce logic without code changes? Can you share your project? (Email via contact info on the blog)
Erik I don't want to focus on the logic I use to get the new IDs, just suppose I have one and it wors fine. For instance you could delete all the records in the students table and then try to add a new record with ID = 1. Doing so I get the DbUpdateException I told you.
I'm going to email you my sample code...
I have replied to your email
how to show image on the form from db as your code shows how to save the image but i want to see record saved in database so how it will be possible...??
SB: Just select the image byte array from the database, and convert to an image
Post a Comment