I'm trying to use the entity framework to communicate with my existing DB. This is thus DB first. Using sql server and c#. The problem is that I would like to use a sequential GUID, generated by the database.
Each time I run the code, my controller pastes the GUID assigned to lutRoughagePhysicalSiloId to the DB (most of the time lutRoughagePhysicalSiloId is just an empty GUID (000..) since I don't fill it). As far as I can see, this should not be happening since I use [DatabaseGenerated(DatabaseGeneratedOption.Identity)].
Any Ideas?
SQL code
CREATE TABLE [dbo].[lutRoughagePhysicalSiloType]
(
[RoughagePhysicalSiloTypeId] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY UNIQUE DEFAULT NEWSEQUENTIALID(),
[PhysicalSiloTypeName] NVARCHAR(50) NOT NULL UNIQUE
)
(Notice the Default value newsequentialId() that gets generated as default by sql server)
Automatic created modelview (DB first):
namespace guiIlvoDairyCowBarn.Models
{
using System;
using System.Collections.Generic;
public partial class lutConcentratePhysicalSiloType
{
public lutConcentratePhysicalSiloType()
{
this.lutConcentratePhysicalSiloes = new HashSet<lutConcentratePhysicalSilo>();
}
public System.Guid ConcentratePhysicalSiloTypeId { get; set; }
public string PhysicalSiloTypeName { get; set; }
public virtual ICollection<lutConcentratePhysicalSilo> lutConcentratePhysicalSiloes { get; set; }
}
}
Buddy class
namespace guiIlvoDairyCowBarn.Models
{
[MetadataType(typeof(lutRoughagePhysicalSiloMD))]
public partial class lutRoughagePhysicalSiloType
{
public class lutRoughagePhysicalSiloMD
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public System.Guid RoughagePhysicalSiloTypeId { get; set; }
[Required, DisplayName("Physical silo type")]
public string PhysicalSiloTypeName { get; set; }
public virtual ICollection<lutRoughagePhysicalSilo> lutRoughagePhysicalSiloes { get; set; }
}
}
}
what is a Buddy class suppose to mean?
Using both [Key] and the [DatabaseGenerated(DatabaseGeneratedOption.Identity)] should work and ignore any user provided value. After saving to the DB the property should have the DB generated Guid.
Your class lutConcentratePhysicalSiloType has no EF data annotations and if saved would take the values you provided. NewSequentialId() is a default and without any EF data annotations the DB row will get whatever the value you specify in the C# class.
Related
Assume I've read and googled, and I still don't know what I'm doing incorrectly. Whenever I try to execute
_dbContext.Set<T>().Add(aMediaObjectWithAssociatedProvider);
_dbContext.SaveChanges();
I get the dreaded efcore violation of primary key constraint reference table
I have a class as such:
public class Media : BaseModel
{
public virtual string Title { get; set; }
public virtual string? Description { get; set; }
public virtual string Source { get; set; }
public virtual Guid? MediaTypeId { get; set; }
public virtual Guid? ProviderId { get; set; }
public virtual DateTime? StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual Provider? Provider { get; set; }
}
The BaseModel class is
public abstract class BaseModel : IBaseModel
{
public virtual Guid Id { get; set; }
}
The Provider class is as such:
public class Provider : BaseModel
{
public virtual string Name { get; set; }
public virtual string? ApiUsername { get; set; }
public virtual string? ConfigurationSection{ get; set; }
}
My DBContext has the following:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Media>().HasKey(x => x.Id);
mb.Entity<Media>().HasOne(p => p.Provider).WithOne().HasForeignKey<Media>(x => x.ProviderId);
}
The code for inserting a new object is as follows:
public T Insert(T oneObject)
{
try
{
// Ensure the entity has an ID
if (oneObject.Id == Guid.Empty)
{
oneObject.Id = Guid.NewGuid();
}
_dbContext.Set<T>().Add(oneObject);
_dbContext.SaveChanges();
}
catch (Exception error)
{
_logger.LogError(error.Message, error);
}
return oneObject;
}
Assume that providers are static, in a sense that they already exist in their table, and I don't want to add new providers when I save media... Media just needs to have a provider.
I know exactly what is happening (the model, after travelling through json back through the api to the server is losing context, but I'm also trying to build a repository type of system where I don't have to build complex save logic for every object. (hence why i'm hand wringing over adding code that loads existing providers).
This problem specifically began rearing its head when I was saving new Media objects into the database with existing Providers. I am still mulling over how to look up children dynamically, but i'm not quite there yet.
I've been at this for so long, i'm about ready to give up on efcore relations and just rebuild the models as single objects, and handle all of the manipulation in javascript. And I don't like this idea.
I know for a fact that there will be questions for more code, but please let me know what. Again, I'm just stepping into .net core / ef core so this code-first is a little confusing for me. Thanks
You may have 2 options to try. Do backup your whole project and database beforehand. Clone your database to another database name. Try these either one option using new cloned database for testing.
No.1
Set "newid()" without quotes in your ID's default value in sql server. So you don't need to use Guid.NewGuid() in code every insert. newid() will auto generate GUID.
No. 2
How about removing primary key from ID (GUID) and then creating new column "UID" (running number) and set UID (running number) as primary key and enable its identity? You need to change all other tables too. And re-link UID each other if you use relationship. This way, your UID will not have existing number when insert.
I have annotated some of my model classes' keys with [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
and added a migration.
When running Update-Database I keep getting following error message:
To change the IDENTITY property of a column, the column needs to be dropped and recreated.
I also tried to Update-Database with a complete new database, to no avail as the error is the same.
When changing the identity property in SQL Server Management Studio, I do not get such error but I would like to avoid a mix of code and DB first approach.
Is there a way to either
force the update to drop and recreate the column
or drop and recreate the table ?
EDIT1:
To answer a question from the comments. My model class before looked like:
public partial class MyModel
{
[Key]
public int Id { get; set; }
[MaxLength(70)]
public string Name { get; set; }
public string Description { get; set; }
...
}
This is my model class after adding the annotation:
public partial class MyModel
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(70)]
public string Name { get; set; }
public string Description { get; set; }
...
}
Is it possible to use arrays in Entity Framework with PostgreSql?
Suppose, for instance, we had the POCO class
public class MyTable
{
[Key]
[Column("gid")]
public int Gid { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("email")]
public string Email { get; set; }
[Column("somedata")]
public int[] SomeData { get; set; }
}
At this point Entity Framework simply does not create the column "somedata" and skips it. Is there a way to do this anyway? And by that I mean not having to use a separate table. Postgres arrays come in handy at times where you want to store a small or limited number of values into a single column.
It's possible to do this if you use Entity Framework Core with the Npgsql EF Core provider.
The code-first approach would be:
[Column("somedata", TypeName = "integer[]")]
public int[] SomeData { get; set; }
Is there a way to convert data in an existing database into objects that can easily be put into a seed method?
Essentially I'd like to let some people add lines to my DB via a form and then convert that into an object that I can re-seed the DB anytime I need to make changes.
The database itself is created via code-first using the following model:
public class Combo
{
public int Id { get; set; }
public string MainPrefix { get; set; }
public string MainDescriptor { get; set; }
public string MainDish { get; set; }
public string Connector { get; set; }
public string SecondaryDescriptor { get; set; }
public string SecondaryDish { get; set; }
}
High level untested idea:
Create a new T4 template which will use either your context or direct SQL to query database and generate either C# static class with method (which will return collection of object created from database data) or SQL insert commands. Depending on what you choose you will either call static method in Seed to get all entities you want to insert or simply load the created SQL script and execute it.
You can also do the same without T4 by using Code Dom (if you want to generate C# code) and creating a new custom tool for visual studio.
These features can be part of your project or the result (C# code) can be compiled in separate external assembly.
I'm writing an ASP.net MVC3 application using Entity Framework Code First and SqlCe4.
I have been designing several models, and found one that is turning out to be interesting. This is what I have so far:
public class Preference
{
public int PreferenceId { get; set; }
[Required]
public int StudentId { get; set; }
public virtual Student Student { get; set; }
[Required]
public int PresentationId { get; set; }
public virtual Presentation Presentation { get; set; }
[Required]
public int Rank { get; set; }
}
I however, need a unique constraint or index, because for a particular student, I want them to have to have a list of preferences, but the PresentationId needs to be unique for each student. Is there a way to do this via Code First or some validation attribute?
This sounds like I'm going down the branch of a many to many relationship with the Preference object being an intermediary, to add the Rank property. However, I can't seem to find how to make sure that the values are unique. Is the best way really to manually just add a unique index to the database outside of EF?
Currently(ie EF 4.1) EF does not have an Attribute or configuration mechanism to create unique indexes.
However if you are using Database Initializer you can create it manually
public class MyInitializer : IDatabaseInitializer<MyContext>
{
public void InitializeDatabase(MyContext context)
{
if (!context.Database.Exists() || !context.Database.ModelMatchesDatabase())
{
context.Database.DeleteIfExists();
context.Database.Create();
context.ObjectContext.ExecuteStoreCommand("CREATE INDEX IX_Preference_PresentationId ON Preference ( PresentationId )");
}
}
}
Or execute it outside the Initializer.
I'm not sure how long the feature has existed, but in EF5, you can use the CreateIndex and DropIndex method in your Up() and Down() methods. This allows you to create unique indexes, which function almost identically to unique constraints, in EF's own language.
public partial class UniqueIndexMigration
{
public override void Up()
{
// Create new unique index
this.CreateIndex("dbo.TableName", new[] { "Col1", "Col2", "Col3" }, true, "IX_TableName_Col1_Col2_Col3");
}
public override void Down()
{
// Drop unique index
this.DropIndex("dbo.TableName", "IX_TableName_Col1_Col2_Col3");
}
}
The true, seen above, is the parameter that specifies that it's a unique constraint. Sadly, these constraints are not honored when making further migrations later on (EF won't drop them for you if you change the underlying schema), but my hope is that by using the EF API, your code will get an upgrade for free when this feature is finally added.
Going with what Eranga was saying, I ended up making it work similarly, but like this:
I used the following code in my DataContext class:
public class StudentRegistrationContext : DbContext
{
public StudentRegistrationContext()
{
Database.SetInitializer(new StudentRegistrationDatabaseInitializer());
}
public DbSet<Student> Students { get; set; }
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Presenter> Presenters { get; set; }
public DbSet<Presentation> Presentations { get; set; }
}
In the Database Initializer class, I used the following:
public class StudentRegistrationDatabaseInitializer : DropCreateDatabaseIfModelChanges<StudentRegistrationContext>
{
protected override void Seed(StudentRegistrationContext context)
{
base.Seed(context);
context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX UX_Preferences_StudentId_PresentationId ON Preferences (StudentId, PresentationId)");
}
}
See answear that implements the Unique constraint with a Custom ValidationAttribute:
https://stackoverflow.com/a/10566485/1133338