When the following code runs
var op = context.Operation.SingleOrDefault(x => x.Operation1 == null);
I get an error
System.InvalidCastException was unhandled
HResult=-2147467262
Message=Specified cast is not valid.
Source=System.Data
StackTrace:
at System.Data.SqlClient.SqlBuffer.get_Byte()
InnerException:
I notice that EF 6.1.3 has mapped the column Operation to a name Operation1 because the table is also called Operation
The table structure has
[OperationID] [tinyint] IDENTITY(1,1) NOT NULL,
[Operation] [varchar](255) NULL
[System.ComponentModel.DataAnnotations.Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public byte EntidadeID { get; set; }
Related
I am trying to find better and performance efficient approach for bulk delete in .NET Core EF (3.1.9). (Approx 500K to 1 Mil records to be deleted in one shot.)
Model:
public class Employee
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EmpID { get; set; }
public string EmpName { get; set; }
}
And database table as:
CREATE TABLE [dbo].[Employee]
(
[EmpID] [int] IDENTITY(1,1) NOT NULL,
[EmpName] [nchar] (20) NULL,
CONSTRAINT [PK_dbo].[Employee]
PRIMARY KEY CLUSTERED ([EmpID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Trying to delete records with generic method-1.
public int Delete<TEntity>(Func<TEntity, bool> predicate) where TEntity: class
{
return DbContext.Set<TEntity>
.FromSqlRaw($"Delete from dbo.Employee")
.Where(predicate).Count();
}
And calling this method as
Func<Employee, bool> myPredicate = x => x.EmpID > 10;
int deletedCount = myclass.Delete(myPredicate);
Exception thrown:
InvalidOperationException: The required column 'EmpID' was not present in the results of a 'FromSql' operation.
ASP.NET Core EF generates query:
DELETE FROM [dbo].[Employee]
fail: Microsoft.EntityFramework.Query[10100] .... stack trace with above error.
Already looked into this:
.NET Core Entity Framework InvalidOperationException
The required column 'CustomerId' was not present in the results of a 'FromSql' operation
The required column 'id' was not present in the results of a `FromSql` operation in EFcore
So not sure why it is throwing above error. As database has the correct PK and model also has it. Tried with some other entities too, it always throws the same error with column name 'xxxEntityColID'.
However, if I use following code then it works:
public int Delete<TEntity>(string whereCondition, params object[] parameters) where TEntity: class
{
return DbContext.Database.ExecuteSqlRaw($"Delete from dbo.Employee WHERE {whereCondition}", parameters);
}
// and calling like
string myCondition = "EmpID > 10";
int deletedCount = myclass.Delete<Employee>(myCondition, new object[0]);
.NET Core EF generates following SQL and work without any error.
DELETE FROM [dbo].[Employee] WHERE EmpID > 10;
Questions
Why does this 'xxxEntityColID' error occur?
What is the better approach to do bulk deletes in .NET Core EF?
Looks like FromSqlRaw() is worked with "SELECT.." queries.
For Bulk delete I evaluated following approach and it is better than dbContext.RemoveRange(entities); Because RemoveRange() generated separate query for each entity. Then every single query takes some time, so not suitable for Bulk delete. In contrast ExecuteSqlRaw(with where condition), just generates single query which hardly takes 250ms for 200K records. On high end servers it will take lesser for sure.
public int Delete<TEntity>(string whereCondition, params object[] parameters) where TEntity: class
{
return DbContext.Database.ExecuteSqlRaw($"Delete from dbo.Employee WHERE {whereCondition}", parameters);
}
I'm having an issue with updating a database with a migration that adds a column to a table and assign values to it. I have reduced the issue to a very simple case.
Here's the model:
public class Model
{
public int Id { get; set; }
public int Col2 { get; set; }
}
This is the context:
public class Context : DbContext
{
public DbSet<Model> Models { get; set; }
}
First, I enabled migrations, created an initial migration and create a database.
PM> Enable-Migrations
PM> Add-Migration -Name Initial
PM> Update-Database
Then, I extended my model:
public int Col3 { get; set; }
and created a new migration:
PM> Add-Migration -Name AddedCol3
I modified that migration to update the values in Col3, see the call to Sql():
public override void Up()
{
AddColumn("dbo.Models", "Col3", c => c.Int(nullable: false));
Sql("update dbo.Models set Col3 = Col2");
}
public override void Down()
{
DropColumn("dbo.Models", "Col3");
}
When I update the database with this migration, I get:
Msg 207, Level 16, State 1, Line 2 Invalid column name 'Col3'.
The generated script is:
ALTER TABLE [dbo].[Models] ADD [Col3] [int] NOT NULL DEFAULT 0
update dbo.Models set Col3 = Col2
-- Removed update to migration history.
Apparently, SQL Server cannot handle the alter table and update in one batch.
I tried to add SQL("GO"); in between, but this results in the error:
The argument 'sql' cannot be null, empty or contain only white space.
when trying to update.
How can I use migrations to achieve this. I want my Up() and Down() methods to consistently update the database.
The work-around provided by #scsimon translates to the SQL-statement
Sql("EXEC('update dbo.Models set Col3 = Col2')");
and script
ALTER TABLE [dbo].[Models] ADD [Col3] [int] NOT NULL DEFAULT 0
EXEC('update dbo.Models set Col3 = Col2')
and works fine.
I prefer this method, as I expect it to be less fulnerable to EF modifications.
I found a work-around by adding a dummy SQL-statement:
Sql("select 1\nGO");
The resulting script is:
ALTER TABLE [dbo].[Models] ADD [Col3] [int] NOT NULL DEFAULT 0
GO
select 1
update dbo.Models set Col3 = Col2
Note that the GO statement has automagically been moved to another location!
Anyway, the script is now accepted by SQL Server and the changes are applied the way they are itended.
I have an issue when I am trying to delete an entity that also has a modified enum property. The error i get is Invalid cast from 'System.String' to 'BV.Entities.CarType'. A simple example follows:
public class Car {
public int Id { get; set; }
public CarType Type { get; set; } // CarType is an enum
}
var car = // load a car entity
car.Type('Sedan');
car.entityAspect.setDeleted();
manager.saveChanges();
This can happen, for example, when a user starts editing a record, but then decides to just delete it.
I have also used the DocCode sample to test this. I edited the saveTodoTests.js and used the 'can save add, update, and delete in one batch' test similar to what was suggested here Exception in client breeze.js when using enum property on model. If I alter the enum type of the deleteTodo item it throws the error, if I remove the setDeleted() it will save it correctly.
System.InvalidCastException was unhandled by user code
HResult=-2147467262
Message=Invalid cast from 'System.String' to 'BV.Entities.CarType'.
Source=mscorlib
StackTrace:
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
at System.String.System.IConvertible.ToType(Type type, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at Breeze.ContextProvider.EF6.EFContextProvider`1.ConvertValue(Object val, Type toType)
at Breeze.ContextProvider.EF6.EFContextProvider`1.SetPropertyValue(Object entity, String propertyName, Object value)
at Breeze.ContextProvider.EF6.EFContextProvider`1.<>c__DisplayClass10.<RestoreOriginal>b__f(KeyValuePair`2 kvp)
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at Breeze.ContextProvider.EF6.EFContextProvider`1.RestoreOriginal(EntityInfo entityInfo)
at Breeze.ContextProvider.EF6.EFContextProvider`1.<ProcessAllDeleted>b__9(EFEntityInfo entityInfo)
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at Breeze.ContextProvider.EF6.EFContextProvider`1.ProcessAllDeleted(List`1 deletedEntities)
at Breeze.ContextProvider.EF6.EFContextProvider`1.SaveChangesCore(SaveWorkState saveWorkState)
at Breeze.ContextProvider.ContextProvider.OpenAndSave(SaveWorkState saveWorkState)
at Breeze.ContextProvider.ContextProvider.SaveChanges(JObject saveBundle, TransactionSettings transactionSettings)
at BV.Web.Controllers.DefaultController.SaveChanges(JObject saveBundle) in c:\Work\Code\BV\BV.Web\Controllers\DefaultController.cs:line 59
Ok, this was a bug and is fixed in the combination of the current breeze.server.net and breeze.js repos on GitHub. It will also go out as part of the next release (1.4.14) sometime next week.
How to create a unit test to an Entity Framework object to verify an integer attribute validation annotated with the [required] key like this:
[Required]
public Int32 MyIntProperty { get; set; }
The unit test code should be something like this:
EntityObject entityObject = new EntityObject();
entityObject.MyIntProperty = null;
EntityObjectContext.EntityObject.Attach(entityObject);
EntityObjectContext.ObjectStateManager.ChangeObjectState(entityObject, EntityState.Added);
var dbContext = new DbContext(EntityObjectContext, true);
int errors = dbContext.GetValidationErrors().Count();
Assert.AreEqual(1, errors);
The problem is that I can not indicate the null value to an integer property. Is it possible to tests this validation another way?
Tks.
If the validation fails only on null then there is nothing to test here, as Int32 will never be null. If you want to make it possible to be null change it's type to Int32? (or better: int?). If the validation fails for some other reasons, then don't put null into your property but some other erroneous value.
Int32 cannot be null unless it Int32?.
I don't know if the following suits your requirement but Int32 would be 0 when not initialised.
Assert.AreNotEqual(0, error);
I resolve my problem by testing if the property was annotated by the [Required] key word:
var propertyInfo = typeof(EntityObject).GetProperty("MyIntProperty");
var attribute = (EdmScalarPropertyAttribute)
propertyInfo.GetCustomAttributes(
typeof(EdmScalarPropertyAttribute), true)
.FirstOrDefault();
Assert.IsFalse(attribute.IsNullable);
If anyone have other solution please tell me.
Tks.
I'm trying to allow a user to set an association by looking up by name.
Unfortunately, even though I'm using virtual, ASP-MVC 4 is complaining that this is an invalid column name for this particular virtual field "VesselName". Here's the error:
Invalid column name 'VesselName'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'VesselName'.
Here's my code:
public int VesselID { get; set; }
public virtual Vessel Vessel { get; set; }
public virtual string VesselName
{
get
{
return Vessel.Name;
}
set
{
if (value != null)
{
// make sure we use upper case
VesselID = db.Vessels.Where(v => v.Name.Equals(value.ToUpper())).First().ID;
}
}
}
You can have "virtual" or "unmapped" attributes in Entity framweork by using the [NotMapped] DataAnnotation. Virtual as used in the program refers to something else.
See virtual method tables.
http://en.wikipedia.org/wiki/Virtual_method_table