I am using CreateDatabaseIfNotExists when creating a database, but it just doesn't drop the database and then it starts initializing it.
There is my DbContext.
public class AppDbContext : DbContext
{
public AppDbContext() : base("Valtrends")
{
InitializeDbContext();
}
public DbSet<FactorType> FactorTypes { get; set; }
public DbSet<ComplexType> ComplexTypes { get; set; }
public DbSet<Value> Values { get; set; }
public DbSet<DataLoader.Entities.Version> Versions { get; set; }
public DbSet<DefaultPlotData> DefaultPlotData { get; set; }
public DbSet<GraphBucket> GraphBuckets { get; set; }
public DbSet<XfactorFrom> XfactorsFrom { get; set; }
public DbSet<XfactorTo> XfactorsTo { get; set; }
public DbSet<DistributionData> DistributionData { get; set; }
public DbSet<Bin> Bins { get; set; }
public DbSet<DefaultPlotSettings> DefaultPlotSettings { get; set; }
private void InitializeDbContext()
{
Database.SetInitializer(new AppDbInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<FactorType>().HasMany(m => m.CompatibilityListXY).WithMany();
}
}
}
And there is the the first part of my AppDbInitializer.
public class AppDbInitializer : CreateDatabaseIfNotExists<AppDbContext>//DropCreateDatabaseAlways<AppDbContext>
{
public override void InitializeDatabase(AppDbContext context)
{
base.InitializeDatabase(context);
var jsonImporter = new JsonImporter();
List<DataLoader.Entities.Version> versions = (jsonImporter.ImportFromJson<List<DataLoader.Entities.Version>>("Versions.json"));
context.Versions.AddRange(versions);
context.SaveChanges();
After SaveChanges I get a violation of primary keys exception on the entity version.
I am using CreateDatabaseIfNotExists when creating a database, but it just doesn't drop the database and then it starts initializing it
It never drops an existing database. As the name implies, it checks if database exists, and if yes, it does nothing, otherwise it creates and initializes it.
Also note that the InitializeDatabase method of the IDatabaseInitializer is always called, it's the class that implements it responsibility what actual action to perform.
In your case you incorrectly assume that the base method will always create the new database, which is not the case. If you want to add a code that executes only when a new database is created, then you should override the Seed method instead:
public class AppDbInitializer : CreateDatabaseIfNotExists<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
var jsonImporter = new JsonImporter();
List<DataLoader.Entities.Version> versions = (jsonImporter.ImportFromJson<List<DataLoader.Entities.Version>>("Versions.json"));
context.Versions.AddRange(versions);
context.SaveChanges(); // you don't need this, it's automatically called after `Seed` call
}
}
Related
App: Blazor Server .NET 5
Using Entity Framework Core
DB; Azure SQL DB
All Ok except for if I refresh the browser, the Helper properties returned by GetActivitys() are null.
Does the Helper property in Activity need a tag?
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Activity> Activitys { get; set; }
public DbSet<Helper> Helpers { get; set; }
}
public class DataAccessService : IDataAccessService
{
private readonly ApplicationDbContext _context;
public DataAccessService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<Activity>> GetActivitys()
{
var list = await _context.Activitys.ToListAsync<Activity>();
return list;
}
}
public class Activity
{
[Key]
[Column("Id")]
[JsonPropertyName("Id")]
public int Id { get; set; }
[Column("Task")]
[Required]
[JsonPropertyName("Task")]
public string Task { get; set; }
[Column("Helper")]
[JsonPropertyName("Helper")]
public Helper Helper { get; set; }
}
public class Helper
{
[Key]
[Column("Id")]
[JsonPropertyName("Id")]
public int Id { get; set; }
[Column("Name")]
[Required]
[JsonPropertyName("Name")
}
If you want to include navigational properties after materializing your query via .ToListAsync(), you must add a chained method .Include(activity => activity.Helper) before materialization.
I am using EF Core and I tried to create a one-to-one relationship between three tables (Car, ElectricCar and PetrolCar)
public class Car
{
public int Id { get; set; }
public string RegistrationNumber { get; set; }
public ElectricCar Company { get; set; }
public PetrolCar Trust { get; set; }
}
public class ElectricCar
{
public int ElectricCarId { get; set; }
public double BatteryCapacityWattage{ get; set; }
public int CarId { get; set; }
public Car Car { get; set; }
}
public class PetrolCar
{
public int PetrolCarId { get; set; }
public double TankCapacity { get; set; }
public int CarId { get; set; }
public Car Car { get; set; }
}
public partial class CarDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public CarDbContext()
{
}
public CarDbContext(DbContextOptions<CarDbContext> options)
: base(options)
{
}
public DbSet<ElectricCar> ElectricCar { get; set; }
public DbSet<Car> Car { get; set; }
public DbSet<PetrolCar> PetrolCar { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Server=DESKTOP-PC\\SQLLOCAL;Database=OneToOneEFCoreCar;Trusted_Connection=True;");
}
}
}
and the code that inserts the data:
CarDbContext context = new CarDbContext();
context.Car.Add(new Car
{
RegistrationNumber = "EL123",
Company = new ElectricCar() { BatteryCapacityWattage = 2000 }
});
context.Car.Add(new Car
{
RegistrationNumber = "PETR123",
Trust = new PetrolCar() { TankCapacity = 50 }
});
context.SaveChanges();
That works without any issue and creates the following data
When I go to the PetrolCar I insert a new row with CarId = 1 and it accepts it without giving any error although that CarId is used in the ElectricCar table as CarId.
Is there any way to restrict this?
If you're entirely set on keeping your object models / data structure the same as it is above then a unique constraint across the two tables isnt really natively achievable.
One possible in code solution (though its not particularly clean, so I would suggest restructuring your data over this, though that seems to be something you would like to avoid) is to override the SaveChanges method.
something along the lines of:
public override SaveChanges()
{
var petrolCars = ChangeTracker.Entries().Where(e is PetrolCar).ToList();
foreach(var pCar in petrolCars)
{
if(query the database for electric cars to see if car id exists)
{
do some sort of error processing and avoid saving;
}
}
base.SaveChanges();
}
it does mean creating a context class that inherits from the default context, though it adds a lot of flexibility in terms of doing something like this (obviously you would want to handle the other cases too of cars having the same id in the other direction)
When I see at other sample projects, the number of tables created for supporting Identity in the db is great (such ones as AspNetRoles, AspNetUserClaims, etc..), but in my case when I make the migration and the update only the User table has been created. What is the reason?
Here is my code in the startup, in the dbcontext and my class user:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSession();
services.AddMemoryCache();
services.AddDbContext<ApplicationDbContext>( options =>
options.UseSqlServer(Configuration["Data:photoarchiver:ConnString"]));
services.AddIdentity<User, IdentityRole > (
opts => {
opts.Password.RequireDigit = false;
opts.Password.RequiredLength = 7;
opts.Password.RequireLowercase = true;
opts.Password.RequireUppercase = false;
opts.Password.RequireNonAlphanumeric = false;
}).AddEntityFrameworkStores<ApplicationDbContext>();
}
Class DbContext:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<User>().HasMany(u => u.Photos).WithOne(i => i.User);
modelBuilder.Entity<Photo>().ToTable("Photos");
modelBuilder.Entity<Photo>().HasOne(i => i.User).WithMany(u => u.Photos);
modelBuilder.Entity<Category>().ToTable("Categories");
}
public DbSet<Photo> Photos { get; set; }
public DbSet<Category> Categories { get; set; }
}
Class User:
public class User : IdentityUser
{
public virtual List<Photo> Photos { get; set; }
[Required]
public string DisplayName { get; set; }
public string Notes { get; set; }
[Required]
public DateTime CreatedDate { get; set; }
}
To get all the AspNetRoles, etc tables "for free" you need to change your ApplicationDbContext to extend from IdentityDbContext<User> instead of just DbContext. IdentityDbContext<T> is found in the Microsoft.AspNetCore.Identity.EntityFrameworkCore namespace. You can see from the source code https://github.com/aspnet/Identity/blob/master/src/EF/IdentityDbContext.cs, IdentityDbContext will bring in the required DbSetproperties.
As you correctly identified in the comment to your question, you will need to call base.OnModelCreating(builder) and re-make your migration files.
I'm using the following technologies: WinForms, Entity Framework 4.4 (5.0 on .NET 4.0), DBContext
I have (what I think is) a very simple Master/Details form, that actually worked just fine before I upgraded from EF 4.0 to EF 4.4. Now, for some reason, the Details DataGridView simply doesn't populate with any data!
Here's my auto-generated schema code:
public partial class RoadMapping
{
public RoadMapping()
{
this.RoadCharacteristics = new HashSet<RoadCharacteristic>();
}
public int RoadMappingID { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public virtual ICollection<RoadCharacteristic> RoadCharacteristics { get; set; }
}
public partial class RoadCharacteristic
{
public RoadCharacteristic()
{
}
public int RoadCharacteristicID { get; set; }
public int RoadMappingID { get; set; }
public string Value { get; set; }
public string Description { get; set; }
public virtual RoadMapping RoadMapping { get; set; }
}
Here's my code that was working with EF 4.0:
SATContext = new SafetyAssessmentToolEntities();
dataGridViewMappings.DataSource = bindingSourceMappings;
dataGridViewDetails.DataSource = bindingSourceDetails;
bindingSourceMappings.DataSource = SATContext.RoadMappings;
bindingSourceDetails.DataSource = bindingSourceMappings;
bindingSourceDetails.DataMember = "RoadCharacteristics";
Here's the code that isn't working with EF 4.4:
SATContext = new SafetyAssessmentToolEntities();
SATContext.RoadMappings.Load();
SATContext.RoadCharacteristics.Load();
dataGridViewMappings.DataSource = bindingSourceMappings;
dataGridViewDetails.DataSource = bindingSourceDetails;
bindingSourceMappings.DataSource = SATContext.RoadMappings.Local.ToBindingList();
bindingSourceDetails.DataSource = bindingSourceMappings;
bindingSourceDetails.DataMember = "RoadCharacteristics";
Please note that bindingSourceMappings and bindingSourceDetails are declared by the form designer.
I know there are a lot of more advanced and code-intensive ways to make this work, but I can't understand why this very simple way of doing it won't work anymore.
Any suggestions?
public partial class SafetyAssessmentToolEntities : DbContext
{
public SafetyAssessmentToolEntities()
: base("name=SafetyAssessmentToolEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<RoadCharacteristic> RoadCharacteristics { get; set; }
public DbSet<RoadMapping> RoadMappings { get; set; }
}
I query data with NHibernate in the server side, then I create a WCF service which is the one that publishes these NHibernate objects, they are correctly serialized to Silverlight, I modify them in my application but when I send them back to the server they get serlialized again, and Generic Lists get converted to Array so I cannot modify them anymore in the server side...
this is my class definition
public class BIMenu
{
public virtual Guid ID { get; set; }
public virtual String DisplayName { get; set; }
public virtual String ProgramToCall { get; set; }
public virtual IList<BIMenu> Children { get; set; }
public virtual IList<BISecurityProfile> SecurityProfiles { get; set; }
public virtual Boolean IsApplication
{
get
{
if (Children.Count < 1 && ProgramToCall != null)
return true;
return false;
}
}
public virtual Boolean IsFolder
{
get
{
return !IsApplication;
}
}
public BIMenu()
{
Children = new List<BIMenu>();
SecurityProfiles = new List<BISecurityProfile>();
}
}
and this is my contract
[ServiceContract]
public interface IBISecurityService
{
[OperationContract]
BIMenu GetMenu(String Name);
[OperationContract]
void SaveMenu(BIOnline.Model.BIMenu Menu);
[OperationContract]
void DeleteMenu(BIOnline.Model.BIMenu Menu);
}
Is your BIMenu class marked [DataContract]? I would expect it to be:
[DataContract]
public class BIMenu
{
[DataMember]
public virtual Guid ID { get; set; }
[DataMember]
public virtual String DisplayName { get; set; }
[DataMember]
public virtual String ProgramToCall { get; set; }
[DataMember]
public virtual IList<BIMenu> Children { get; set; }
[DataMember]
public virtual IList<BISecurityProfile> SecurityProfiles { get; set; }
Also, if your IList<BIMenu> Children and IList<BISecurityProfile> SecurityProfiles properties are being set to instances of the Array type, then that is perfectly valid, since Array implements IList. If you want to keep them as actual List<> instances, then just define the properties as List<> instead of IList<>, like this:
// Defined as actual Lists, not IList interfaces.
[DataMember]
public virtual List<BIMenu> Children { get; set; }
[DataMember]
public virtual List<BISecurityProfile> SecurityProfiles { get; set; }