i want to set a new value to my entity objects FK column, but i cant find the property to set. I dont want to get record from db.
I have a db like that
Db Tables:
Concept ConceptType
-Id (PK) -Id(PK)
-Name -Name
-ConceptTypeId(FK) (ALLOW NULL)
Code:
Concept conceptToUpdate = new Concept() { Id = 1 };
ConceptType conceptType = new ConceptType() { Id = 5 };
db.AttachTo("Concept", conceptToUpdate);
db.AttachTo("ConceptType", conceptType);
conceptToUpdate.ConceptType = conceptType;
db.SaveChanges();
This code is working if ConceptTypeId(FK) column is NULL before. If it is not NULL it gives exception. I trace the sql query, the problem is on sql query because it is checking that old value is NULL :S
SQL QUERY: (from SQL Profiler)
exec sp_executesql N'update [dbo].[Concept]
set [ConceptTypeId] = #0
where (([Id] = #1) and [ConceptTypeId] is null)
',N'#0 int,#1 int',#0=5,#1=1
The reason it fails is that in 3.5 SP1 is that for relationships the old FK value is part of the concurrency check, as you found via SQL profiler.
What you need is something like this:
Concept conceptToUpdate = new Concept() {
Id = 1 ,
ConceptType = new ConceptType {Id = OriginalFKValue}
};
ConceptType conceptType = new ConceptType() { Id = 5 };
db.AttachTo("Concept", conceptToUpdate);
db.AttachTo("ConceptType", conceptType);
conceptToUpdate.ConceptType = conceptType;
db.SaveChanges();
This means you need to know not just the Id of the thing you want to update, but also it's original FK values, which is of course a real pain.
Hence a new feature in EF 4.0 called FK Associations. With FK Associations the original value of the FK is not part of the concurrency check.
Hope this helps
Alex
Related
I have a few table structure look as below:
CREATE TABLE Person
(
PersonID INT PRIMARY KEY,
Name NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
CREATE TABLE Info
(
InfoID INT PRIMARY KEY,
PersonID INT,
Info NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
CREATE TABLE Setting
(
SettingID INT PRIMARY KEY,
PersonID INT,
Setting NVARCHAR(255),
LastUpdatedBy INT,
LastUpdatedDate DATETIME
);
I face a new procedure to follow that if there is any updates on Info or Setting table, I will need to do relevant updates to Person table on columns LastUpdatedBy and LastUpdatedDate.
What first come to my mind is to create a SQL trigger that automatically update Person table when Info or Setting table does. But take a quick glance through for a few articles stating that a SQL trigger should be avoided as it's an very expensive process when creating it,
While some people recommends to change in application code. For an example,
using (var db = new DbContext())
{
var result = db.Info.SingleOrDefault(x => x.InfoID == infoID);
if (result != null)
{
result.Info = "Some new value";
result.LastUpdatedBy = userID;
result.LastUpdatedDate = DateTime.UtcNow;
db.SaveChanges();
}
}
need to change and become like this.
using (var db = new DbContext())
{
var result = db.Info.SingleOrDefault(x => x.InfoID == infoID);
if (result != null)
{
result.Info = "Some new value";
result.LastUpdatedBy = userID;
result.LastUpdatedDate = DateTime.UtcNow;
var person = db.Person.SingleOrDefault(x => x.PersonID == result.PersonID);
if (person != null)
{
person.LastUpdatedBy = result.LastUpdatedBy;
person.LastUpdatedDate = result.LastUpdatedDate;
}
db.SaveChanges();
}
}
in reality, the application code is massive, a lot of code modification need to be made.
Assume there are 30+ tables, and each of them contain at least 100k of records. If creating of triggers are possible, it will be as the following:
CREATE TRIGGER TriggerName ON dbo.Info
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Person
SET LastUpdatedBy = INSERTED.LastUpdatedBy ,
LastUpdatedDate = INSERTED.LastUpdatedDate
FROM INSERTED
WHERE dbo.Person.PersonID = INSERTED.PersonID
END
GO
Is the SQL trigger should really be avoided in this scenario? Please explain based on your answer if can. Any alternative solution is welcome, performance first.
Trigger is optimal (from a performance perspective) here; it's simply like running an update statement on a bunch of rows from the front end code. I don't see why you think there is a performance penalty. Your trigger code should look more like this though:
CREATE TRIGGER TriggerName ON dbo.Info
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Person
SET LastUpdatedBy = INSERTED.LastUpdatedBy ,
LastUpdatedDate = INSERTED.LastUpdatedDate
FROM dbo.Person
INNER JOIN
INSERTED
ON dbo.Person.PersonID = INSERTED.PersonID
END
GO
There are other ways, such as making a Stored procedure that updates all tables in a transaction, or updating front end data access layer (if your front end has a lot to update, it implies it is structured wrong: one place should have responsibility for writing to this table. If your front end code has update statements peppered all through it, well.. that's a bad design) so a dedicated class maintains these two tables properly..
Right now I'd say a trigger is your easiest way out of the problem.. they aren't well liked, though not because of performance, but because they start to add confusing consequences.. imagine you as a c# developer with limited database experience, didn't know what a trigger was, and you're complaining "every time I update just this one table, all these other 27 tables change by magic! What's going on? Am I going crazy or what?" - triggers break rules like "keep all your data updating code in one place" and this is why people who engineer systems where specific parts have specific jobs, don't like them
C#, Entity Framework, MSSQL
Table is defined as the following:
id uniqueidentifier (not null, PK, default (newid()) )
createdAt datetimeoffset(3)
updatedAt datetimeoffset(3)
version timestamp
deleted bit
Image image
I do have a many to one relationship assigned between the two tables. on tblArea.fkImages -> tblImages.id
If I insert a record by using this query the newid() appears to work and it assigns a random unique identifier.
insert into tblimages (image) values (null)
However, if I use the following C# code:
Guid gRestID = Guid.Parse(tbxSelectedLoc.Text);
tblImages newImage;
tblArea newArea = new tblArea
{
Name = tbxAddMenu.Text,
fkLoc = gRestID,
};
if (pbxLoc.Image != null)
{
newImage = new tblImages
{
Image = Helpers.ImageToByte(pbxLoc.Image),
};
db.tblImages.Add(newImage);
newArea.tblImages = newImage;
}
db.tblArea.Add(newArea);
db.SaveChanges();
Both record's ID's are 0's in tblArea and in tblImages which then prevents any subsequent additions to the two tables (constraints on the pk).
Anyone know why this is happening?
I have a bunch of entities that need to be updated to match entities of the same class in a different database. For example:
Database1 TableA:
Id| User | FirstName| LastName
1 | Geekguy123 | Fred | Smith
Database2 TableA:
Id| User | FirstName| LastName
34| Geekguy123 | Frederick| Smith
How can I update database1 for Geekguy123 so the new FirstName is Frederick? The ids are different but the User is unique.
To be clear, there are dozens of properties on the entity, which I don't want to update manually. Maybe SQL would be easier?
Just query in database 1 for the record you want. Get its FirstName property and then query the record in database 2 and then update firstname field with the one you got from database 1. And then submit your change.
Db1Context c1 = new Db1Context();
var userToUpdateWith = c1.Users.Single(u => u.User == "Geekguy123")
Db2Context c2 = new Db2Context();
var userToUpdate = c2.Users.Single(u => u.User == "Geekguy123")
Since you got to set many properties, you can do the following.
string[] properties = new string[]{"FirstName","LastName"};
foreach(var property in properties){
object value = userToUpdateWith.GetType().GetProperty(property).GetValue(userToUpdateWith, null);
userToUpdate.GetProperty(property).SetValue(userToUpdate, value, null);
c2.SaveChanges();
Here you go - the pass-through SQL that you can use.
If the table name is really Database1, then replace Table1, Table2 accordingly.
UPDATE t1
SET t1.FirstName = t2.FirstName
FROM Database1.dbo.Table1 AS t1
INNER JOIN Database2.dbo.Table2 AS t2
ON t1.User = t2.User
And the passthrough would be like this:
using (var context = new YourDataContext())
{
context.Database.ExecuteSqlCommand(sqlString);
}
Turns out, there were actually a couple hundred properties. I ended up deleting and re-adding the entity with the new values.
I want to enter unique index value into my database when insert other table data. In this case my table index column ID is unique but not auto-increment.
I also want to check the maximum of index value in target table. If the table is empty then index value starts from 0, OR if it contains 1 row then the index value starts from 2.
Already I can successfully do this operation using a stored procedure. But I want to do this operation using Entity Framework when saving data.
My stored procedure is:
ALTER PROCEDURE [dbo].[CreatePerson]
#Name Nvarchar(50),
#Code Nvarchar(50)
AS
BEGIN
Declare #Id int
SET #Id = ISNULL(((SELECT MAX([id]) FROM dbo.tbl_Person)+1),'1')
Insert Into dbo.tbl_Person([Id], [Name], [Code])
Values (#id, #Name, #Code)
END
Can anyone tell me what the LINQ Command for this index value save process?
A straight translation of your SQL stored procedure into Linq would basically be something like this:
var newId = ctx.Persons.Any() ? ctx.Persons.Select(p => p.Id).Max() + 1 : 1;
var newPerson = new Person { Id = newId, Name = someName, Code = someCode };
ctx.Persons.Add(newPerson);
ctx.SaveChanges();
Note: this solution is certainly NOT recommended for inserting unique id's in a database, consider carefully the suggestions of Sharped and Magnus in the comments below the question to better use auto-increment or random guids to solve your problem.
With an auto-incremented id column your code would look like this:
var newPerson = new Person { Name = someName, Code = someCode };
ctx.Persons.Add(newPerson);
ctx.SaveChanges();
var newId = newPerson.Id;
Note that EF will update the id column of the newly created entity for you in code automagically.
What's the best way to test your NHibernate Mapping classes?
Let's assume I want to test the following map:
public QuoteMap()
{
this.Table("QUOTE");
this.Id(x => x.Id).Column("QUOTE_ID").GeneratedBy.Sequence("SEQ_QUOTE_ID");
this.Map(x => x.IsDeleted).Column("IS_DELETED");
this.References(x => x.Proposal).Column("PROPOSAL_ID");
}
where Proposal type is mapped to another table.
QUOTE table looks like this:
CREATE TABLE "QUOTE"
(
"QUOTE_ID" NUMBER(18,0) NOT NULL,
"PROPOSAL_ID" NUMBER(18,0) NOT NULL ENABLE,
"IS_DELETED" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE,
CONSTRAINT "PK_QUOTE" PRIMARY KEY ("QUOTE_ID"),
CONSTRAINT "FK_QUOTE_PROPOSAL" FOREIGN KEY ("PROPOSAL_ID") REFERENCES
"PROPOSAL" ("PROPOSAL_ID") ENABLE
)
Option1: PersistenceSpecification
new PersistenceSpecification<Quote>(session, new CustomEqualityComparer())
.CheckProperty(c => c.TenantId, 1)
.CheckProperty(c => c.IsDeleted, false)
.CheckReference(
c => c.Proposal,
new Proposal
{
Id = 1,
IsDeleted = false,
TenantId = 1,
VersionNumber = 1,
OutletId = 1,
StatusId = "TST"
})
.VerifyTheMappings();
transaction.Commit();
...this test would fail with the following exception:
NHibernate.Exceptions.GenericADOException: could not insert:
[Quote#18][SQL: INSERT
INTO QUOTE (IS_DELETED, PROPOSAL_ID, QUOTE_ID)
VALUES (?, ?, ?)] ---> Oracle.DataAccess.Client.OracleException:
ORA-02291: integrity constraint (PROPOSALOWN.FK_QUOTE_PROPOSAL)
violated - parent key not found
...because it has a dependency on the PROPOSAL record with Id = 1 being there.
Another problem with this is if you have coincidently mapped PropertyA to ColumnB and PropertyB to ColumnA your test would pass and won't point out your mistake.
Option2: Raw SQL to INSERT, NHibernate to SELECT
Now this would be ideal: you insert by issuing a raw SQL statement e.g.
INSERT INTO QUOTE (QUOTE_ID, PROPOSAL_ID, IS_DELETED)
SELECT SEQ_QUOTE_ID.NextVal, 1, 0 from dual;
Then you read using Nhibernate and check the values.
The problem is, again, dependency on PROPOSAL record being there. Insert PROPOSAL record for this test? Sure! However, Proposal table has another set of FOREIGN KEYS, so you may end up inserting ROWS into dozens of tables just to test your mapping... no likey!
Surely there's a much better, simpler way to test NHibernate Maps. Could you suggest one?
You should be using CheckReference for the proposal:
.CheckReference(
c => c.Proposal,
new Proposal
{
IsDeleted = false,
TenantId = 1,
VersionNumber = 1,
OutletId = 1,
StatusId = "TST"
})