PetaPoco \ NPoco - Partial Insert - asp.net

Is there a way to partially insert an object using PetaPoco or NPoco?
For example, I have a table called Users:
UserId | UserName | UserMail | UserCreationDate
Each one of these columns are NON NULLABLE and have a default value when they are left empty.
In ASP.NET I have a User class, and I use the ORM to insert a new record with only the name:
Dim userData As New User()
userData.UserName = "Jimmy Hendrix"
db.Insert(userData)
I expect the database to look as follows:
UserId | UserName | UserMail | UserCreationDate
12 | Jimmy Hendrix | (DB default)| (DB default)
I want the insert command only insert the name, without inserting the other object properties with the object's default values.
Such as there is a partial update, I want a partial insert.
Is that possible in PetaPoco?
Is there another way to do it by myself without any ORM?
Edit:
Using SQL I can get the job done, but I need to use POCO objects, so I don't want to have to remember the database parameters. I want something like
user.UserName = "Michael"
user.Insert(user)
And it will insert only the UserName, ignoring the other variables. The SQL that I want to be generated in the background is:
"INSERT Users(UserName) VALUES(#UserName)"
(while the #UserName parameter holds the userData.FirstName value)
As you can see, it doesn't take in account the other variables in the class.
Today if I use the insert command, even if I give a value to a single property in the class, NPoco still tries to insert ALL the class variables into the db setting the variables I didn't want to set with the class's default values (which are different from the db default values)
Also, all of the properties are insertable/updateable, so there can't be any ResultColumn types in the class. I want to insert these values but only the ones I declare in that particular instance. All of the properties are available to update and insert but for each instance i insert only what i declare.

I would create a PartialUserForInsert class:
[TableName("Users")]
public class PartialUserForInsert
{
public string UserName { get; set; }
}

Your provided schema does not include a FirstName column.
Assuming the column is mapped to UserName, using the following should insert as expected.
dim sql = new Sql("INSERT Users(UserName) VALUES(#0)", userData.FirstName)
db.Execute(sql)

Related

Entity Framework throws DBUpdateConcurrencyException if table has trigger defined

While working on RESTful service in ASP.NET with support of Entity Framework and SQL Server 2014 as DB engine, I met some unexpected obstacle on my way.
I've prepared some dummy case which exactly explains what kind of problem I'm facing right now.
I've got database in SQL Server named "dummy_database". It contains only one table - Person - and one trigger - TR_Person_Insert - which is responsible for adding some extra data (current date and time in this particular case) to every single record before add the record to the "Person" table. Code of database structure is included below:
USE dummy_database;
GO
IF EXISTS (SELECT * FROM sys.tables WHERE name LIKE 'person')
DROP TABLE [person];
GO
IF EXISTS (SELECT * FROM sys.triggers WHERE name LIKE 'TR_Person_Insert')
DROP TRIGGER [TR_Person_Insert];
GO
CREATE TABLE [dbo].[person]
(
[id] INT IDENTITY(1, 1),
[name] NVARCHAR(256) NOT NULL,
[surname] NVARCHAR(256) NOT NULL,
[age] INT NOT NULL,
[valid_from] DATETIME2 (7),
CONSTRAINT PK_Person_ID PRIMARY KEY ([id])
)
GO
CREATE TRIGGER TR_Person_Insert ON [dbo].[person]
INSTEAD OF INSERT
AS
BEGIN
INSERT INTO [dbo].[person] ([name], [surname], [age], [valid_from])
SELECT i.[name], i.[surname], i.[age], GETDATE()
FROM inserted i
END
GO
Then I created dummy RESTful Service using ASP.NET and added Entity Framework with Database First approach to it. The code of one and only existing method of that Service is as below:
[RoutePrefix("api/person")]
public class DummyController : ApiController
{
[HttpPost, Route("")]
public IHttpActionResult AddPerson(PersonDto data)
{
using (var ctx = new DummyDatabaseModel())
{
var person = new person
{
name = data.Name,
surname = data.Surname,
age = data.Age
};
ctx.people.Add(person);
ctx.SaveChanges();
}
return Ok();
}
}
The problem is with saving changes method called on database's context. This method throws DBUpdateConcurrencyException exception with additional information as below:
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateConcurrencyException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded.
I'm aware of the fact that this exception is caused by trigger, which is fired instead of regular 'insert' routine on database's side.
The question I ask is - how to get rid of this kind of exception without abandoning trigger in database. I'm out of ideas and to be frank - I count on your tips or solutions, if you faced same problem in the past.
Thank you for your help.
I had the same problem, in my case, the trigger was the expression "instead of insert"
Ex.
Create trigger TR_AA on TbAA INSTEAD OF INSERT ....
I changed the expression by "AFTER INSERT" It's work for me!
Ex.
Create trigger TR_AA on TbAA AFTER INSERT ....

random EntityValidationErrors (The field is required)

I've been trying to update an Entity using the following code:
var db = new MyContext();
var data = db.Tableau.Find(Id);
if (data != null)
{
data.Name = model.Name;
data.EmbedCode = model.EmbedCode;
db.SaveChanges();
}
The problem is that my Tableaus table has a Parent field (FK not null to a DataTree table). Sometimes when I save the changes to this edited record, I get an error saying that "The Parent field is required". But I am not editing the Parent field. The parent field should be intact and existent, since I am only altering the Name and EmbedCode fields.
How to proceed? Thanks in advance.
That is because you are allowing null values in ParentId column in your Tableaus table, but in your Tableau entity you have ParentId as non-nullable property( which it means the relationship is required), and when you load a Tableau instance from your DB, EF expects that you set that property too. Try changing that property to nullable:
public int? ParentId {get;set;}
If you configure your relationship using Fluent Api it would be:
modelBuilder.Entity<Tableau>()
.HasOptional(t=>t.Parent)
.WithMany(dt=>dt.Tablous)// if you don't have a collection nav. property in your DataTree entity, you can call this method without parameter
.HasForeignKey(t=>t.ParentId);
Update 1
If you want ParentId property as Required in your Tableau entity, you need to make sure that you have a valid value in that column in your DB per each row. With a "valid value" I mean it should be different of the default value and it should exist as PK in your DataTree table.
Update 2:
One way to load a related entity as part of your query is using Include extension method:
var data = db.Tableau.Include(t=>t.Parent).FirstOrDefault(t=>t.Id==Id);

Can't retrieve auto generated primary key using Vici Coolstorage

I'm using Vici Coolstorage in a Windows Forms project to access a SQLite database. In every table in my database there is a field called ID defined as INTEGER PRIMARY KEY, so it is an auto increment field.
I'm trying to retrieve the value of that field after I store the object in the database, but I always get the value 0 instead of the real ID. The Vici Coolstorage documentation states that "if the primary key is defined as an autonumber (identity) field in the database, your can retrieve the generated primary key after the object is saved", but that doesn't seem to be true unless I'm doing something wrong. Please help me. This code will reproduce the problem:
<MapTo("Company")> Public MustInherit Class Company
Inherits CSObject(Of Company, Integer)
Public MustOverride ReadOnly Property ID As Integer
Public MustOverride Property Name As String
End Class
Sub SomeMethod()
Dim C As Company = Company.[New]
C.Name = "Some name"
C.Save()
MessageBox.Show(C.ID) 'This always prints 0!!!
End Sub
Thank you!
Had faced this issue and figured out that setting the identity attribute on the field solved this.
[Identity]
public int Id
{
get { return (int)GetField("Id"); }
}

Entity Framework 5: Using DatabaseGeneratedOption.Computed option

I have an EF5 code first project that uses the [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute.
This option is overriding my settings.
Consider this SQL table:
CREATE TABLE Vehicle (
VehicleId int identity(1,1) not null,
Name varchar(100) not null default ('Not Set')
)
I am using the SQL default construct to set the [Name] is case it is not set.
In code behind, I have a class defined similar to:
public class Vehicle {
...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string ShoulderYN { get; set; }
}
When I update the entity in code, the value set in the default overrides my new setting.
In code, I have (pseudo):
vehicle.Name = 'Update Name of Vehicle';
_dbContext.Update(vehicle);
_dbContext.SaveChanges();
The expected result is Vehicle.Name = 'Update Name of Vehicle'.
The actual result is Vehicle.Name = 'Not Set'.
Is there a way in EF5 to say:
if Vehicle.Name is null/empty, use the value defined in the database? Otherwise, if I set the value in code, I want to use this value.
Apparently, no there isn't. It's not that smart :)
As you may already read, Computed option just tells the EF not to update your column, because you will compute a value on the DB-side yourself. EF will then just return newly computed value from your database (which in your case is "Not Set").
Your basic three options are - as per EF Source code documentation:
None - The database does not generate values.
Identity - The database generates a value when a row is inserted.
Computed - The database generates a value when a row is inserted or updated.
https://github.com/aspnet/EntityFramework6/blob/527ae18fe23f7649712e9461de0c90ed67c3dca9/src/EntityFramework/DataAnnotations/Schema/DatabaseGeneratedOption.cs
Since you expect a little more custom logic to be done, I'm afraid you would have to do it yourself. I would suggest you stop relying on database default constraint and do everything in code first approach. This way you would have a code like that:
public class Vehicle
{
public Vehicle()
{
this.Name = "Not set";
}
// Without 'Generated' attribute
public string Name { get; set; }
}
This way, when your Entity is created, it automatically starts with expected default value. And can be later changed by simply modifying the Name property.
Hope it helps!
Actually there is a simple solution for this:
You need to leave default constraint with value in table creation script as it is now:
CREATE TABLE Vehicle (
VehicleId int identity(1,1) not null,
Name varchar(100) not null default ('Not Set')
)
Just remove DatabaseGenerated attribute from property in class definition:
public class Vehicle {
...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string ShoulderYN { get; set; }
}
And that's it: now database will use default value only if you do not specify some value in code. Hope this helps.
I checked this for hours to get good answer but no:
EF cannot update models by automatic generated-ID.
You have 3 options:
Adding another VehicleId to Vehicle model.
Change automatic generated-ID to be manual generated by you.
Setting unique identifier to be something else then the generated-ID in your
model.
In your Vehicle Class it can be the Name property.
I suggest you option 3:
Setting up unique-id to be Vehicle.Name (and you can add more properties).
Then: if vehicle by unique-id doesn't exists, add new vehicle to db-context:
//if there is no such a Vehicle in system, add it:
if (vehicle.Name !=null && vehicle.Name != String.Empty && _dbContext.Where(v => v.Name == vehicle.Name).FirstOrDefault() == null)
_dbContext.Add(vehicle);
_dbContext.SaveChanges();

Primary key has to be called "Id" using EFCode?

I'm writting an application using ASP MVC 3 and based on the music store tutorial. I installed the EFCode framework and I created a database by right clicking App_Data folder.
The columns of the Table "Client" are
IdClient
Name
IdClient was defined as primary key by right clicking on it, and Identity was set to Yes and autoincrement, etc. Everything seems fine...When I run the application it tells me that this table has no key defined!!
If i rename IdClient to Id there is no problem, my question why doesn't it detect IdClient as primary key?? In the tutorial database the primary keys are not defined with a name "Id"...
Thanks!
I assume you mean Entity Framework Code First.
You can add the [Key] attribute to the column.
public class Product
{
[Key]
public int TestID { get; set; }
}
Entity Framework Code First uses convention over configuration and by default will look for a column named "ID" to make it the primary key. Otherwise you have to it declaratively.
You have to do one of two things
1) Use DataAnnotations, and in your model class give your idClient property the [Key] attribute. That will tell EF that idClient is your primary key.
2) In your data context's class, you can override the OnModelCreating(ModelBuilder modelBuilder) method. In that method you have to call something like modelBuilder.Entity<MyEntity>().HasKey(x => x.idClient); so EF knows that this is an identity and key column.
By default, EF4 Code First only automatically sets a column as a primary key if it is named Id or <entity name>Id.

Resources