I am working in a company where a project has been brought in house because the external team tasks with building the system have not done a great job and thus been fired.
An issue that I have is that we have an existing database, where some tables where seed data should have been done through a migrationBuilder looks to have just been inserted via SSMS \ SQL Server Insert scripts.
as a result I get an error like this when adding seeding scripts so that when we spin up an new isntance of the database this works, but on an existing environment such as dev, test and staging it does not.
Violation of PRIMARY KEY constraint 'PK_xxxx'. Cannot insert duplicate key in object 'forms.AnswerTypes'. The duplicate key value is (1)
The only potential way I have found around this is from this link here
https://entityframeworkcore.com/knowledge-base/54553668/add-or-update-data-to-existing-database-with-entity-framework
But hope that there are better ways that this can be acheived as I cannot delete the data as part of the migration because its already used and being referenced by other tables so the ripple effect is wide ranging.
An example of the sort of data that I am trying to seed is like this;
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
schema: "forms",
table: "Rules",
columns: new[] { "RuleId", "Rules" },
values: new object[] { 1, "Any" });
migrationBuilder.InsertData(
schema: "forms",
table: "Rules",
columns: new[] { "RuleId", "Rules" },
values: new object[] { 2, "All" });
}
So, the question is, is it possible with migrationBuilder to check is data exists prior to inserting?
You can write custom SQL and stuff and add it to your migration script;
https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/operations
There's also .Sql():
https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli#customize-migration-code
migrationBuilder.Sql(
#"
UPDATE Customer
SET Name = FirstName + ' ' + LastName;
");
Which you could use. My team and i use EF6 still but we use the same principle. Our migrationscript sometimes have additional SQL statements to move any data around or generate default data when adding a column etc.
Related
I have a customer and sales table
CUSTOMER
--------------
Id (int auto increment)
Name
SALES
---------------
Id (int auto increment)
CustomerId (int)
OrderTotal (decimal)
With Guid i can do this.
dbTransaction = dbContext.Database.BeginTransaction(isolationLevel);
var customer = new Customer()
{
Id = Guid.NewGuid(),
Name = "John Doe"
};
var sales = new Sales()
{
Id = Guid.NewGuid(),
CustomerId = customer.Id,
OrderTotal = 500
};
dbContext.SaveChanges();
dbTransaction.Commit();
How can i do this if my primary key is int (with DatabaseGeneratedOption.Identity)?
You cannot. The ID that goes into a IDENTITY column is generated by the database upon insertion, and all "tricks" to circumvent that and determine the ID yourself are probably flawed.
Short answer: If you want some say in generating an ID before you save, use a GUID (UNIQUEIDENTIFIER), or a SEQUENCE (if you're working with SQL Server 2012 or newer).
Why you should not compute the next free ID yourself:
Don't even consider running a query such as context.Customers.Max(c => c.Id) + 1 as a viable solution, because there's always the possibility that you have concurrent database accesses: another process or thread might persist a new entity to the same table after you've read the next "free" ID but before you store your entity. Computing the next free ID will be prone to collisions, unless your whole operation of getting the ID, doing something with it, and storing the entity with that ID were atomic. This would likely require a table lock in the DB, which might be inefficient.
(The same problem exists even when you use SEQUENCEs, a new feature introduced in SQL Server 2012.) (I was wrong; see end of answer.)
Possible solutions:
If you need to determine the ID of an object before you save it, then don't use the ID that goes in a IDENTITY column. Stay with a GUID, because you're extremely unlikely to get any collision with these.
There's no need to chose between one or the other: you can actually have your cake and eat it! Nothing stops you from having two ID columns, one that you determine externally (the GUID) and one that stays internal to the DB (the IDENTITY column); see the blog article "CQS vs. server generated IDs" by Mark Seemann for a more detailed look at this idea. Here's the general idea by example:
CREATE TABLE Foos
(
FooId INT IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
-- ^^^^^ assigned by the DBMS upon insertion. Mostly for DB-internal use.
Id UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE DEFAULT (NEWID()),
-- ^^ can be dictated and seen by the users of your DB. Mostly for DB-external use.
…
);
CREATE TABLE FooBars
(
FooId INT NOT NULL FOREIGN KEY REFERENCES Foos (FooId),
-- use DB-internal ID in foreign key constraints ^^^^^
…
);
CREATE VIEW PublicFoos AS
SELECT Id, … FROM Foos;
-- ^^ publish the public ID for users of your DB
(Make sure you adhere to some convention for consistently naming internal and public ID field names.)
SEQUENCEs, a feature introduced in SQL Server 2012, are a possible alternative to having an IDENTITY column. They are automatically increased and you are guaranteed a unique number when getting the next free ID using NEXT VALUE FOR SomeSequence. One of the use cases mentioned on MSDN are:
Use sequences instead of identity columns in the following scenarios: […] The application requires a number before the insert into the table is made.
Some caveats:
Getting the next sequence value will require an additional roundtrip to the database.
Like identity columns, sequences can be reset / re-seeded, so there is the theoretical possibility of ID collisions. Best to never re-seed identity columns and sequences if you can help it.
If you fetch the next free sequence value using NEXT VALUE FOR, but then decide not to use it, this will result in a "gap" in your IDs. Gaps obviously cannot happen with regular (non-sequential) GUIDs because there is no inherent ordering to them.
As far as I know you can not get the ID before saving the changes in the database. The database creates the ID after the values are inserted in the database.
To add to it when you call .SaveChanges() then only it will write the changes to the database and only then the identity value will get generated.
You can get that value by a small hack.
Create a function in SQL Server something like this
CREATE FUNCTION fn_getIdentity(#tbl_name varchar(30))
AS
BEGIN
IF #tbl_name = 'Employee_tbl'
RETURN IDENT_CURRENT('Employee_tbl')
ELSE IF #tbl_name = 'Department_tbl'
RETURN IDENT_CURRENT('Department_tbl')
ELSE
RETURN NULL
END
Create an entity in your Entity framework to support this function and use it wherever you want.
Then use
var nextValue = dbContext.fn_getIdentity("Employee_tbl")
IDENT_CURRENT returns you last incremented value for an identity column. This doesn't mean MAX + 1 as if your previous transaction generated an identity value for this column but was rolled back then you will see next value that will be generated.
Please note, I didn't check syntax properly and this syntax is just to present an idea.
However I would go with solution provided by Stakx i.e. SEQUENCE if using SQL Server 2012 or above
else creating a table to implement functionality of SEQUENCE by reserving ID once generated permanently in a table.
We can indeed if your ID is in integer, using SQL. The following example is for PostreSQL, please feel free to adapt it for other servers and to edit this answer.
Create a virtual entity model for the database to wrap our query result and to have a fake DbSet<some virtual model> to use ef core extension method FromSqlRaw.
Define a virtual model:
public class IntReturn
{
public int Value { get; set; }
}
Now fake a DbSet<IntReturn> it will not be really created on server:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Entity<IntReturn>().HasNoKey();
base.OnModelCreating(modelBuilder);
}
Now we can get the currently used Id for Customers table in this example. The calling method is inside a Subclassed : DbContext, you'd what to instantiate your context to use it instead of this:
public int GetNextCustomerId()
{
//gets current id, need do +1 to get the next one
var sql = "Select last_value as Value FROM \"Customers_Id_seq\";";
var i = this.Set<IntReturn>()
.FromSqlRaw(sql)
.AsEnumerable()
.First().Value + 1;
return i;
}
Credits to:
https://erikej.github.io/efcore/2020/05/26/ef-core-fromsql-scalar.html
https://stackoverflow.com/a/18233089/7149454
I am creating an Entity Framework 6 database using the Empty EF Designer Model.
My issue can be duplicated as follows. I have two tables and a many to many association between them.
When I generate the database, it creates a third mapping table, which I expect. However, the names of the columns are unexpected.
CREATE TABLE [dbo].[WordWordType] (
[Words_WordId] int NOT NULL,
[WordTypes_WordTypeId] int NOT NULL
);
Notice that the mapping table has column names that are somewhat redundant and long. The column names include the table name, which is redundant, followed by an underscore, followed by the Key column name.
WordWordType
- Words_WordId
- WordTypes_WordTypeId
I am hoping EF 6 has a way to create the mapping table with column names that don't include the table name and underscore. I need the table to look as follows.
WordWordType
- WordId
- WordTypeId
At the moment you already have the table name in your primary key column name, which is - in my opinion - a redundant information.
If your name your primary key columns simply Id, then EF will automatically name your columns Word_Id and WordType_Id.
In Code First you can use fluent API
modelBuilder.Entity<Word>()
.HasMany(w => w.WordTypes)
.WithMany(wt => wt.Words)
.Map(x =>
{
x.ToTable("WordWordType");
x.MapLeftKey("WordId");
x.MapRightKey("WordTypeId");
});
but since you are using model first I think the only way is to change T4 file which is responsible for generating SQL script. You can find it in Database Script Generation section in your model properties.
Update
see this for debugging T4.
the SSDLToSQL10.tt contains two main section for this, one is Creating all tables on line 150 and another one is Creating all FOREIGN KEY constraints on line 196, they enumerate on Store.GetAllEntitySets() and Store.GetAllAssociationSets() and creating tables and foreign keys in database, debug the T4 file and see where your mapping table is created, I suspect it is in Store.GetAllEntitySets(), then change the generation the way you want.
I'm looking for for a tool or technique that will allow me to point to a table in a SQL Server Database, generate a DTO Class for that table AND then generate DTO Class INSTANCES for each row in that table.
Given a table in SQL Server
CREATE TABLE
[dbo].[Tickets](
[TicketID] [int] IDENTITY(1,1) NOT NULL,
[TicketName] [varchar](50) NOT NULL,
CONSTRAINT [PK_Tickets] PRIMARY KEY CLUSTERED ([TicketID] ASC)
Populated with data:
TicketID TicketName
1 Green
2 Blue
Would produce a class called "Tickets" with properties:
public int TicketID {get;set;}
public string TicketName {get;set;}
AND then something like:
public List<Tickets> LoadDataIntoListOfTickets()
{
List<Tickets> theList = new List<Tickets>;
theList.Add(new Ticket(){TicketID = 1, TicketName = "Green"});
theList.Add(new Ticket(){TicketID = 2, TicketName = "Blue"});
return theList;
}
I don't want to generate INSERT statements to send to SQL Server. I want to create DTO Class and Instances for the Data.
Clarification: I'm looking to do a code generation of the DTO Class AND the source code for the DTO instances for each row of in the data as it exists AT A SPECIFIC POINT IN TIME. I'm familiar with the various ORM's that can create the classes. What I'm looking for is something that will generate the LoadDataIntoListOfTickets() listing with a line of source code for each ROW in the database. (Hopefully that makes it clear.)
Clarification #2:
This is going to be used to replace having SQL INSERT statements in a text file for initializing tables when the user creates a new database for an application. Having a text file with the SQL INSERT statements allowed me to use traditional version control tools to manage our database initialization process. It works really well, but it isn't as human readable (whitespace / alignment) or re-usable as storing the data in DTO instances would be. Serializing to XML would reduce readability. Binary serialization would (by nature) make it NOT human readable and would also make it near impossible to do a meaningful compare/merge with previous versions.
Does anyone know of a tool or library that can do this ?
If what you are really after is a way to get a new database from containing nothing to it containing a database a schema and an initial set of data then I would HIGHLY recommend you looking into database migration tools. they will help you keep things human-readable and in sync on multiple installations / environments.
here you can find a few listed:
https://stackoverflow.com/questions/8033/database-migration-library-for-net
Personally I prefer https://github.com/dradovic/MigSharp
I'm using Entity Framework for the first time and I'm trying to create a object with a collection (and I want all the objects in the collection to be created in database as well) but I'm having some foreign keys violations.
My sample tables:
table APPOINTMENTS: ID, VAR1, DATE_APPOINTMENT
table GUESTS: ID, APPOINTMENT_ID, USER_ID, VAR2, VAR3
My test code:
DomainService aux = new DomainService();
APPOINTMENTS appointment = new APPOINTMENTS();
appointment.VAR1 = "BLA";
appointment.DATE_APPOINTMENT = new DateTime();
//The user with id = 1 is already created in the database
appointment.GUESTS.Add(new GUESTS { USER_ID = 1, VAR2 = 1, VAR3 = "F" });
aux.InsertAppointment(appointment);
At DomainService I have:
public void InsertAppointment(APPOINTMENTS appointment)
{
using (var context = this.ObjectContext)
{
context.AddToAPPOINTMENTS(appointment);
context.SaveChanges();
}
}
But I'm getting this error:
{"ORA-02291: integrity constraint (FK_GUESTS_APPOINTMENTS) violated - parent key not found"}
What am I doing wrong?
UPDATE:
To create the ID's in the database, I am using a sequence for each table and a trigger before insert to get the next value.
When I create a single object, e.g. a appointment without guests, it inserts in the database and it generates the id.
The solution to this problem:
"The ID fields that are generated from sequences won't be handled
correctly. After saving the entities the ID's will be returned as 0.
I'll fix this by manually hacking the SSDL (open your .edmx file in a
text editor) with StoreGeneratedPattern="Identity" attributes on the
ID fields (lines 6 and 16). Note that designer may rip that change out
upon future modification.
While I suppose it's not absolutely necessary it might also be prudent
to modify some type metadata such as changing "number"s to "int"s in
your SSDL and "Decimal"s to "Int32"s in your CSDL where applicable.
Frequently these don't auto-generate with the desired values
especially with XE."
#http://www.chrisumbel.com/article/oracle_entity_framework_ef
As for me, the problem was solved simply by opening diagram .edmx and changing property StoreGeneratedPattern from None to Identity for each Primary Key in each table. After saving everything was fine.
I'm using VS 2012, Entity Framework 5 (6 is not supported yet), Oracle 11.2, last ODP.NET 12, .Net 4.5
In case of EF code first approach, if this error come
(ORA-02291: integrity constraint (FK_GUESTS_APPOINTMENTS) violated -
parent key not found)
In my case there are 2 tables which have Identity columns. So I just added
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
property to my model class just above the the column which is identity column in database and it solved my problem :)
Hope this help :)
I cant see where you are setting your Primary Key (the ID property of the appointment class). Are you using a key generator on the database side? If not this should be the problem.
You're inserting a record with a foreign key value that is not found in the parent table that constraint refers back to.
I'm trying to insert new records into a SQL CE 4 database with LINQPad and having problems with the identity problem of a table. Let's say I have this simple table for instance:
PEOPLE
Id int IDENTITY(1,1) NOT NULL,
Name nvarchar(100) NOT NULL
I may be doing things the wrong way but I tried this in LINQPad
People person = new Person { Name = "Bob" };
People.InsertOnSubmit(person);
SubmitChanges();
But I get a SqlCeException stating
"The colum cannot be modified. [ Column name = Id ]"
I can insert a record with SQL just fine, that works with no errors from SQL CE or its data provider and SQL CE sets the Id column for me which is what I'm wanting
INSERT INTO PEOPLE (Name) VALUES ('Bob');
Is there another step that I'm missing? I'm not even sure if its a LINQPad issue but thought I'd ask anyways since that's what I'm trying this code with right now.
What do you get if you run this in LinqPad
(from dm in this.Mapping.GetTable(typeof(People)).RowType.DataMembers
select new { dm.DbType, dm.Name, dm.IsPrimaryKey , dm.IsDbGenerated }
).Dump();
In particular, as I understand it, IsDbGenerated should be true for the id column.
I have a SQL CE 3.5 data file that I have used previously in LinqPad and looking at the SQL generated for inserts it does not mention id
This is probably the issue with dbml file. Check whether the column is marked as identity(or whatever this is called in L2S). The thing is that Id cannot appear in the insert query.