EF 6 database first: How to update stored procedures? - entity-framework

We are using Entity Framework 6.0.0 and use database first (like this) to generate code from tables and stored procedures. This seems to work great, except that changes in stored procedures are not reflected when updating or refreshing the model. Adding a column to a table is reflected, but not adding a field to a stored procedure.
It is interesting that if I go to the Model Browser, right click the stored procedure, select Add Function Import and click the button Get Column Information we can see the correct columns. This means that the model knows of the columns, but does not manage to update the generated code.
There is one workaround, and that is to delete the generated stored procedure before updating the model. This works as long as you have not made any edits on the stored procedure. Does anyone know of a way to avoid this workaround?
I am using Visual Studio 2013 with all the latest updates as of early December 2013.
Thanks in advance!
Update 1:
andersr's answer helped in one case, where the stored procedure used a temporary table, so i gave him +1, but it still does not solve the main problem of updating simple stored procedures.
Update 2:
shimron's comment below links to a question about the same issues in EF 3.5. It seems the same is still true for EF 6.0. Read it for an alternative way of doing it, but my conclusion as of now is that the simplest way of doing it is to delete the generated stored procedure before updating the model. Use partial classes if you want to do something fancy.

Based on this answer by DaveD, these steps address the issue:
In your .edmx, rt-click and select Model Browser.
Within the Model Browser (in VS 2015 default configuration, it is a tab within the Solution Explorer), expand Function Imports under the model.
Double-click your stored procedure.
Click the Update button next to Returns a Collection Of - Complex (if not returning a scalar or entity)
Click okay then save your .edmx to reflect field changes to your stored procedure throughout your project.

Does your stored procedures return data from temporary tables by any chance ? EF does not seem to support this, see EF4 - The selected stored procedure returns no columns for more information.
However, the stored procedure will as you observed, be available in the Model Browser. I did a quick test featuring the scenario described above. The stored procedure was generated in my context class, but the return type was an int rather than a complex type. See the link above for potential workarounds.

I just encountered this and my workaround (it is really nasty) was to create an if statement with a condition that will never be true at the top of the stored procedure which selects the same list of outputs as the query with explicit casting to the datatypes I want to return. This will assume nullability of your types, so to resolve that you wrap the cast in an ISNULL
For example, if your output has the columns:
UserId (int, not null)
RoleId (int, nullable)
FirstName (varchar(255), nullable)
Created (datetime, not null)
You would expect this to create a POCO like:
SomeClass {
public int UserId { get; set; }
public int? RoleId { get; set; }
public string FirstName { get; set; }
public DateTime Created { get; set; }
}
...But it doesn't and that's why we're here today. To get around this not working as expected, I put the following at the top of my SP (right after the 'AS'):
if(1=0)
begin
select
UserId = isnull((cast(0 as int)),0),
RoleId = cast(0 as int),
FirstName = cast(0 as varchar),
DateTime = isnull((cast(0 as datetime)),'')
end
It is horrible and ugly but it works for me every time. Hopefully we get a tooling update that resolves this soon...happened to me today with no temp tables in SQL Server 2016 w/VS2015...
Hope this helps somebody

Related

EF code-first migration: SqlCeException altering NTEXT column

My application uses Entity Framework 5.0 code-first on top of a Sql CE database. Until now, we have used Automatic Migrations to manage entity mapping changes. However, I now have a change for which I need to create a custom migration to ensure no data is lost during the update. I made my changes to the entities, and used the Add-Migration command which generated Up() and Down() methods for me. I customized the Up() method to insert my custom sql to preserve the data, and tested my application.
When I run the application, I received the error:
Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration.
Ok, I don't understand this because all of my changes are detailed in the Up() method that got executed.
So I turn Automatic Migrations back on just to see what happens. Now I receive this error:
"Cannot alter column of type NTEXT or IMAGE [ Column Name = LastName ]"
This error comes from a table/entity that hasn't even been touched with my changes. The existing database has this string mapped to nvarchar(4000). If I examine the DB after I receive this exception, I observe that the columns have been changed to ntext. What is EF doing? Why is it touching tables that haven't been changed? How can I get more information on what is going on here?
Update:
As a workaround, I attempted to mark each and every string type in my entities with a data annotation as such:
[Column(TypeName = "ntext")]
public virtual string LastName
{
get;
set;
}
Now all of my strings are using ntext in the database. This leads to further exceptions when queries are performed:
The ntext and image data types cannot be used in WHERE, HAVING, GROUP BY, ON, or IN clauses, except when these data types are used with the LIKE or IS NULL predicates.
So, to summarize:
Turning off automatic migrations causes EF to detect phantom changes and throw exceptions
Turning on automatic migration in conjunction with a custom migration causes all existing strings to be mapped to ntext
strings mapped to ntext cannot be queried, effectively making them useless in my application
For me, a modification of an Up method worked out.
SerialNumber = c.String(maxLength: 99)
was applied instead of
SerialNumber = c.String()
i had the same issue and i fixed by editing the table column data type manually by opening SQl Server Compact/SQlite Toolbox explorer windows, and then expend the database name, then expend the table you want to edit and right click the column you want to edit, and click drop script, then run the scrip and the column will be dropped from the table, then right click the table and click add column and from here you can choose what data type you want and add the new column that way. I hope this helps some one.

Change Table and Column Name Mappings Entity Framework v4.3

I've got an application with a working Entity model generated from an existing database. I have to point my application at a new database, with the same schema, except that the table and column names are different.
For example, my current schema has tables named like "Answer". My new schema that I need to point to has the exact same table, except it is named "tblAnswer".
My columns have also changed. Where as a column used to be called "AnswerId", it's now "zAnswerId". Don't ask about the "z" prefix, it's a long story, but it's on every column.
So, what options do I have to point this existing Entity Model (generated from the database) to a new database and adjust the mappings? I've been experimenting with some of the techniques that are used for "Code First" mappings, as outlined in this guide, but haven't had any luck. I simply don't know if this is the right approach, or if there is something that makes more sense.
Suggestions? Thanks in advance.
You can change the database in the web.config file.
Use data annotations to use the different table and column names.
For example:
[Table("tblAnswer")]
class Answer
{
[Column("zAnswerId")]
public int AnswerId { get; set; }
}

Entity Framework 4: Mapped Stored Procedure on Model with Additional Parameters

We've started using Entity Framework 4 for data access and have come across an issue or perhaps lack of understanding.
Our current system is heavily reliant on Stored Procedures, these procedure contain some necessary business logic so we need to continue to use these when doing Select/Insert/Update/Delete.
The issue we are having is the following:
We've mapped a table to an entity, let's say for example this is a User entity and has the following properties - UserId, FirstName, LastName
Now in our sproc to insert a user we accept FirstName, LastName, CreatedById as parameters.
As our User Entity has no CreatedById we get an error indicating that no property of our Entity can be mapped to the "CreatedById" parameter.
Now one thing we've tried is to manually add a CreatedById scalar property to our Entity, but this results in the issue that there is no Field in our User table in the data source that maps to CreatedById. In general the additional property that we'd like to pass in is not something that is stored.
Now there is potential solution to this in that we can just map the procedures to Function Imports and not bother with using the .AddObject, .DeleteObject, .SaveChanges way of manipulating our objects but that doesn't feel like the way to go about it.
that's a good question. There are few options i can tell u.
instead of mapping the entity to the table, map it a view and have the view return CreatedById and then your problem would be solved.
Second option is to create overloaded stored procedure that takes only FirstName, LastName and calls the actual stored procedure with a default value for CreatedById. You can create overloads at the database layer or create it in the model in the ssdl layer which supports inline stored procedure.
exec myproc #firstName,#LastName,null

How to manage GetDate() with Entity Framework

I have a column like this in 1 of my database tables
DateCreated, datetime, default(GetDate()), not null
I am trying to use the Entity Framework to do an insert on this table like this...
PlaygroundEntities context = new PlaygroundEntities();
Person p = new Person
{
Status = PersonStatus.Alive,
BirthDate = new DateTime(1982,3,18),
Name = "Joe Smith"
};
context.AddToPeople(p);
context.SaveChanges();
When i run this code i get the following error
The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.\r\nThe statement has been terminated.
So i tried setting the StoreGeneratedPattern to computed... same thing, then identity... same thing. Any ideas?
You have to manually edit the edmx xml and set your SSDL StoreGeneratedPattern attributes to identity or computed. But whenever you update your edmx via the designer your changes will get overwritten.
This is a known issue. Please see the following links for more details:
Microsoft Connect Ticket
Using a GUID as an EntityKey in Entity Framework 4
I had the same problem! For me it works like this:
Database MS SQL Server express:
[RowTime] [datetime2](7) NOT NULL
ALTER TABLE [dbo].[Table_1] ADD CONSTRAINT [DF_Table_1_RowTime] DEFAULT (getdate()) FOR [RowTime]
GO
Then I import the table from database to my Entities model.
Entities will not realise the default value!
So, you have to set the StoreGeneratedPattern of the column to Computed.
Then Entities will not put there any default value any more.
Combination of:
datetime2,
NOT NULL,
StoreGeneratedPattern=Computed
Works for me!
Changing type of DateCreated to datetime2 might solve the problem.
datetime 2007-05-08 12:35:29.123
datetime2 2007-05-08 12:35:29. 12345
Ref: http://technet.microsoft.com/en-us/library/bb677335.aspx67
Here is a working workaround:
1) Change the column to datetime2 as mentioned elsewhere. This fixes the conversion error.
2) Add a trigger that sets DateCreated to getdate();
CREATE TRIGGER [TR_AS_ChangeTime] ON [AS_ApplicationSession]
AFTER INSERT,UPDATE AS
BEGIN
SET NOCOUNT ON;
UPDATE AS_ApplicationSession
SET AS_ChangeTime = getdate()
WHERE AS_Id IN(SELECT AS_ID FROM INSERTED)
END
3) If neccessary, set
p.DateCreated = DateTime.MinValue;
just to initialize it.
4) If you need the DateCreated from the database, add
context.Refresh(System.Data.Objects.RefreshMode.StoreWins, p);
just after
context.SaveChanges();
Focusing on the fact that I do not want change the database, since it is an application problem and I expect them to solve this problem one day, my solution (that is totally possible in my case) is to create a partial class of the model to correct the problem on constructor:
public partial class Log
{
public Log()
{
this.Date = DateTime.Now;
}
}
This works for me because:
I create the model on the moment I send it to database.
I use CLOUD for these services, the datetime must be the same in both Application and Database servers!
Don't forget that the namespace needs to match the Model namespace or partial should not list properties (and should no be partial as well ;])!

GUID or int entity key with SQL Compact/EF4?

This is a follow-up to an earlier question I posted on EF4 entity keys with SQL Compact. SQL Compact doesn't allow server-generated identity keys, so I am left with creating my own keys as objects are added to the ObjectContext. My first choice would be an integer key, and the previous answer linked to a blog post that shows an extension method that uses the Max operator with a selector expression to find the next available key:
public static TResult NextId<TSource, TResult>(this ObjectSet<TSource> table, Expression<Func<TSource, TResult>> selector)
where TSource : class
{
TResult lastId = table.Any() ? table.Max(selector) : default(TResult);
if (lastId is int)
{
lastId = (TResult)(object)(((int)(object)lastId) + 1);
}
return lastId;
}
Here's my take on the extension method: It will work fine if the ObjectContext that I am working with has an unfiltered entity set. In that case, the ObjectContext will contain all rows from the data table, and I will get an accurate result. But if the entity set is the result of a query filter, the method will return the last entity key in the filtered entity set, which will not necessarily be the last key in the data table. So I think the extension method won't really work.
At this point, the obvious solution seems to be to simply use a GUID as the entity key. That way, I only need to call Guid.NewGuid() method to set the ID property before I add a new entity to my ObjectContext.
Here is my question: Is there a simple way of getting the last primary key in the data store from EF4 (without having to create a second ObjectContext for that purpose)? Any other reason not to take the easy way out and simply use a GUID? Thanks for your help.
I ended up going with a GUID.
The size/performance issues aren't
critical (or even noticeable) with SQL Compact, since
it is a local, single-user system.
It's not like the app will be
managing an airline reservation
system.
And at least at this point, there
seems to be no way around the "no
server-generated keys" limitation of
the SQL Compact/EF4 stack. If someone has a clever hack, I'm still open to it.
That doesn't mean I would take the same approach in SQL Server or SQL Express. I still have a definite preference for integer keys, and SQL Compact's bigger siblings allow them in conjunction with EF4.
Use a Guid. AutoIncrement is not supported on Compact Framework with Entity Framework.
Also, if you ever want to create a application which uses multiple data sources, int PK's are going to fall apart on you very, very quickly.
With Guid's, you can juse call Guid.NewGuid() to get a new key.
With int's, you have to hit the database to get a valid key.
If you store data in multiple databases, int PK's will cause conflicts.
What I've done for SQL CE before, and I assume we have a single application accessing the database, is to calculate the MAX value on startup and put it in a static variable. You can now hand out sequential values easily and you can make the code to generate them thread safe very easily.
One reason to avoid Guids would be size = memory and storage space consumption.
You could also query SQL Compact metadata like so:
SELECT AUTOINC_NEXT FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Categories' AND AUTOINC_NEXT IS NOT NULL