When running my program I get this exception: 'Microsoft.Data.Sqlite.SqliteException: 'SQLite Error 1: 'no such table: Tasks'.'
I followed the steps on the Microsoft docs to install EFcore, create the model and create the database. This did the trick on one of my other Maui projects but not on this one since I can't get it to to create the table 'Tasks'.
using Microsoft.EntityFrameworkCore;
using Task = Todo.Models.Task;
namespace Todo.Data
{
class TaskContext : DbContext
{
public DbSet<Task> Tasks { get; set; }
public string DbPath { get; }
public TaskContext()
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = System.IO.Path.Join(path, "ToDo.db");
}
// The following configures EF to create a Sqlite database file in the
// special "local" folder for your platform.
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
}
}
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Todo.Models
{
public class Task
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public bool IsCompleted { get; set; }
}
}
Edit: Adding Database.EnsureCreated() in the TaskContext constructor fixed it
Related
I'm using EF Core and have the following project structure
AppName.Entity
Alert
Other classes
AppName.Repository
AlertRepository
Other Repository classes
The alert entity is as follows:
using System;
using System.ComponentModel.DataAnnotations;
namespace AppName.Entity
{
public class Alert
{
[Key]
public int AlertId { get; set; }
public string RuleId { get; set; }
public string DeviceId { get; set; }
public string VehicleVin { get; set; }
public string AlertText { get; set; }
public DateTime DateTimeUtc { get; set; }
public AlertCategory AlertCategory { get; set; }
}
public enum AlertCategory
{
VehicleHealth = 1,
FleetHealth = 2,
EmissionsHealth = 3,
Fuel = 4,
AssetUtilization = 5,
Safety = 6,
DutyCycle = 7
}
}
AlertRepository.cs is as follows:
using AppName.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AppName.Repository
{
public class AlertRepository// : GenericRepository<Alert>, IAlertRepository
{
public async Task<int> Save(Alert alert)
{
using (AFIdbContext context = new AFIdbContext())
{
context.Alerts.Add(alert);
await context.SaveChangesAsync();
return alert.AlertId;
}
}
public async Task<bool> Save(IEnumerable<Alert> alerts)
{
using (AFIdbContext context = new AFIdbContext())
{
foreach (Alert alert in alerts)
{
context.Alerts.Add(alert);
}
await context.SaveChangesAsync();
return true;
}
}
public IQueryable<Alert> GetList(DateTime fromDate, DateTime toDate, int pageSize, int pageNum)
{
using (AFIdbContext context = new AFIdbContext())
return context.Alerts.Where(x => x.DateTimeUtc >= fromDate && x.DateTimeUtc <= toDate).Skip(pageSize * pageNum).Take(pageSize);
}
}
}
AFIdbContext.cs
using AppName.Entity;
using Microsoft.EntityFrameworkCore;
using System.Configuration;
namespace AppName.Repository
{
public class AFIdbContext : DbContext
{
private string _afiConnstring;
public string AFIConnstring
{
get
{
if (_afiConnstring != null)
return _afiConnstring;
else
return "";
}
}
public AFIdbContext() : base()
{
}
public AFIdbContext(DbContextOptions<AFIdbContext> options) : base(options)
{
}
public DbSet<Alert> Alerts { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserAlertPreference> UserAlertPreferences { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AlertRepository>().ToTable("Alert");
modelBuilder.Entity<User>().ToTable("User");
modelBuilder.Entity<UserAlertPreference>().ToTable("UserAlertPreference");
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(AppExtensions.ConnString);
}
}
}
When I attempt to save a collection of Alerts, I get the following exception:
System.InvalidOperationException: 'The entity type 'AlertRepository' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.'
The Alert entity does have a primary key in the DB and does have the [Key] attribute. Why is it telling me that the AlertRepository object doesn't have the key set when it's an Alert Entity I am trying to save? Can I not have the Entity in a separate project? I'm trying to follow a DDD pattern
The exception is being thrown before it even tries to save the records
(I have a breakpoint on await context.SaveChangesAsync(); in AlertRepository
The solution here doesn't help because I already have those set
AlertRepository is not right class to be an entity, you just have to map the Alert entity in OnModelCreating
Replace
modelBuilder.Entity<AlertRepository>().ToTable("Alert");
with
modelBuilder.Entity<Alert>().ToTable("Alert")
or you can just remove this line because entity framework will create a table for you with same name as your entity.
You have mapped the wrong entity in your OnModelCreating for table Alert.
Try with
modelBuilder.Entity<Alert>().ToTable("Alert")
I'm rewriting a current working POS system that was created in C# using the .NET Framework. When trying to build the project the compiler returns error CS0311 saying that the type of the parameter given can't be used.
I've researched the used classes and everything it needs according to the .NET documentation is defined in the class so I have no clue why it doesn't work.
The code used in EfDbContext.cs:
using NLog;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Data.Entity.Validation;
using System.Linq.Expressions;
using Dal.Migrations;
using Dal.Model;
namespace Dal
{
public class EfDbContext : DbContext
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public EfDbContext()
: base(Settings.ConnectionString ?? "")
{
Database.SetInitializer<EfDbContext>((IDatabaseInitializer<EfDbContext>) new MigrateDatabaseToLatestVersion<EfDbContext, Configuration>());
EfDbContext.logger.Debug("Database entity context initialized");
}
public DbSet<Member> Members { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<TaxCategory> TaxCategories { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<TicketLine> TicketLines { get; set; }
public DbSet<MemberCard> MemberCards { get; set; }
public DbSet<CheckoutSheet> CheckoutSheets { get; set; }
public DbSet<Transaction> Transactions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Ticket>().HasMany<TicketLine>((Expression<Func<Ticket, ICollection<TicketLine>>>)(p => p.TicketLines)).WithRequired().WillCascadeOnDelete(true);
modelBuilder.Entity<Ticket>().HasMany<Transaction>((Expression<Func<Ticket, ICollection<Transaction>>>)(p => p.Transactions)).WithOptional((Expression<Func<Transaction, Ticket>>)(m => m.Ticket)).WillCascadeOnDelete(true);
modelBuilder.Entity<Member>().HasMany<MemberCard>((Expression<Func<Member, ICollection<MemberCard>>>)(p => p.MemberCards)).WithRequired((Expression<Func<MemberCard, Member>>)(m => m.Member));
modelBuilder.Entity<CheckoutSheet>().HasMany<Ticket>((Expression<Func<CheckoutSheet, ICollection<Ticket>>>)(p => p.Tickets)).WithOptional((Expression<Func<Ticket, CheckoutSheet>>)(m => m.CheckoutSheet)).WillCascadeOnDelete(true);
}
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
throw new FormattedDbEntityValidationException(ex);
}
}
~EfDbContext()
{
EfDbContext.logger.Debug("Database entity context destroyed");
}
}
}
Code used in Configuration.cs:
using NLog;
using System.Data.Entity;
using System.Data.Entity.Migrations;
namespace Dal.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration<DbContext>
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public Configuration()
{
this.AutomaticMigrationsEnabled = true;
this.AutomaticMigrationDataLossAllowed = true;
this.ContextKey = "POS.Dal.EfDbContext";
}
protected override void Seed(DbContext context)
{
Configuration.logger.Info("Database migration completed");
}
}
}
[CS0311 error returned by compiler][1]
Error CS0311
The type 'Dal.Migrations.Configuration' cannot be used as type parameter 'TMigrationsConfiguration' in the generic type or method 'MigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>'. There is no implicit reference conversion from 'Dal.Migrations.Configuration' to 'System.Data.Entity.Migrations.DbMigrationsConfiguration<Dal.EfDbContext>'.
Dal 2
D:\lagae\Documents\Point Of Sale\POS\Dal
D:\lagae\Documents\Point Of Sale\POS\Dal\EfDbContext.cs
Line 20
Column 134
Does somebody see what I did wrong?
Kind Regards,
Jerlag_01
My entity is derived from Entity class, so it should by default have an id of type int, but for some reason EntityFramework does not recognize it. I even tried to manually make my primary key(commented out) but it still won't work.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text;
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using Abp.Timing;
namespace Test.Models
{
[Table("AppTasks")]
public class Task : Entity, IHasCreationTime
{
//[Key]
//public int Id { get; set; }
public const int MaxTitleLength = 256;
public const int MaxDescriptionLength = 64 * 1024;
[Required]
[StringLength(MaxTitleLength)]
public string Title { get; set; }
[StringLength(MaxDescriptionLength)]
public string Description { get; set; }
public TaskState State { get; set; }
public DateTime CreationTime { get; set; }
public Task()
{
CreationTime = Clock.Now;
State = TaskState.Open;
}
public Task(string title, string description = null) : this()
{
Title = title;
Description = description;
}
}
public enum TaskState : byte
{
Open = 0,
Completed = 1
}
}
Try using a concrete IEntityTypeConfiguration<T>:
public class TaskConfiguration : IEntityTypeConfiguration<Task>
{
public void Configure(EntityTypeBuilder<Task> builder)
{
builder.HasKey(tsk => tsk.Id);
//builder.HasMany(...) relationships et al.
}
}
And in your DbContext definition (or whatever you have called it):
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfiguration(new TaskConfiguration());
}
This should definitely configure EFCore in a way, that Id will be the primary key for this entity. As an added benefit, you get a bit more flexibility and cleaner code.
Scenario: Intranet app. Windows authentication. EF 6.1.3. Databases: SQL Server Compact Edition and MS Access. VS Studio 2013.
The solution has 3 projects:
EnqueteWeb.UI - ASP.NET web application;
EnqueteWeb.Dominio - class library for the application domain;
ControleDeAcessoGeral - class library to get data of the user logged from Active Directory, and include/update/delete/list some users that perform some special actions on the app.
As the access control to the app is based on a SQL Server Compact Edition database, I have EntityFramework installed in ControleDeAcessoGeral. I want to have all the methods regarding to users in a class in this project. And so I did it.
This ControleDeAcessoGeral project is defined like this:
Aplicacao
- Corp.cs (methods to deal with Active Directory stuff)
- UsuariosApp.cs (methods to deal with the SQL Server CE database)
Contexto
- DBControleDeAcesso.cs (defines the context)
- InicializaControleDeAcesso.cs (fill in initial data to the
DBControleDeAcesso database)
Entidades
- Perfil.cs (profiles that a user can have on the app)
- Usuarios.cs (users that may perform some actions on the app)
- UsuarioAD.cs (Active Directory user and its data)
The DBControleDeAcesso.cs class has the following code:
using ControleDeAcessoGeral.Models.Entidades;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
namespace ControleDeAcessoGeral.Models.Contexto
{
public class DBControleDeAcesso : DbContext
{
public DBControleDeAcesso() : base("ControleDeAcessoContext") { }
public DbSet<Perfil> Perfis { get; set; }
public DbSet<Usuario> Usuarios { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
The entities classes are the following:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ControleDeAcessoGeral.Models.Entidades
{
public class Usuario
{
[Key]
public string Logon { get; set; }
public string Nome { get; set; }
[Display(Name="Órgão")]
public string Orgao { get; set; }
public string Email { get; set; }
[StringLength(maximumLength: 4)]
public string Depto { get; set; }
[Display(Name = "Perfis")]
public virtual List<Perfil> Perfis { get; set; }
public Usuario()
{
this.Perfis = new List<Perfil>();
}
}
}
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ControleDeAcessoGeral.Models.Entidades
{
public class Perfil
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Por favor, informe o NOME DO perfil.")]
[StringLength(maximumLength: 25)]
public string Nome { get; set; }
[StringLength(maximumLength: 255)]
[Display(Name = "Descrição")]
public string Descricao { get; set; }
public virtual List<Usuario> Usuarios { get; set; }
public Perfil()
{
this.Usuarios = new List<Usuario>();
}
}
}
And the UsuariosApp.cs class is as bellow (for the sake of brevity, I'll show only the methods that concerns to the issue):
using ControleDeAcessoGeral.Models.Contexto;
using ControleDeAcessoGeral.Models.Entidades;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace ControleDeAcessoGeral.Models.Aplicacao
{
public class UsuariosApp
{
private DBControleDeAcesso db { get; set; }
public UsuariosApp()
{
db = new DBControleDeAcesso();
}
public void SalvarUsuario(Usuario usuario)
{
db.Usuarios.Add(usuario);
db.SaveChanges();
}
public Perfil LocalizarPerfil(int id)
{
return db.Perfis.Find(id);
}
}
}
The action that tries to save a user (Usuarios.cs) in the SQL Server CE database is in AdministracaoController and has the following code:
using ControleDeAcessoGeral.Models.Aplicacao;
using ControleDeAcessoGeral.Models.Entidades;
using EnqueteWeb.UI.Models;
using EnqueteWeb.UI.ViewModels;
using System.Linq;
using System.Web.Mvc;
namespace EnqueteWeb.UI.Controllers
{
public class AdministracaoController : Controller
{
[HttpPost]
public ActionResult CriarUsuarioNaApp(UsuarioViewModel model)
{
foreach (var item in model.PerfisSelecionados)
{
Perfil perfil = new UsuariosApp().LocalizarPerfil(item);
model.Usuario.Perfis.Add(perfil);
}
if (ModelState.IsValid)
{
new UsuariosApp().SalvarUsuario(model.Usuario);
return RedirectToAction("Usuarios");
}
return View(model);
}
}
}
So, when this action CriarUsuarioNaApp is invoked and the method SalvarUsuario(model.Usuario) runs, the following error occurs:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
I've read a few about this on web but, unfortunately, I still couldn't make it works.
Hope a wise and good soul will show me the way.
Thanks for your attention.
Paulo Ricardo Ferreira
The problem arises from the fact that you do not dispose of the first DbContext instance (from which you load the profile entities) prior to attaching said entities to the second DbContext instance.
To fix (and some additional suggestions):
have UsuariosApp implement IDisposable and dispose your instance of the DbContext db when disposing UsuariosApp
wrap your newly-disposable UsuariosApp instance in a using statement and use this single instance for both your Perfil loading and Usuario saving logic
optimize Perfil loading by loading all values with single call
validate ModelState.IsValid immediately
Something like this:
public class UsuariosApp : IDisposable
{
private DBControleDeAcesso db { get; set; }
public UsuariosApp()
{
db = new DBControleDeAcesso();
}
public void SalvarUsuario(Usuario usuario)
{
db.Usuarios.Add(usuario);
db.SaveChanges();
}
public Perfil LocalizarPerfil(int id)
{
return db.Perfis.Find(id);
}
public IEnumerable<Perfil> LocalizarPerfiles( IEnumerable<int> ids )
{
return db.Perfils.Where( p => ids.Contains( p.Id ) )
.ToArray();
}
private bool _disposed = false;
protected virtual void Dispose( bool disposing )
{
if( _disposed )
{
return;
}
if( disposing )
{
db.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}
}
public ActionResult CriarUsuarioNaApp( UsuarioViewModel model )
{
// validate model state first
if( ModelState.IsValid )
{
// use single, disposable repo/uow instance
using( var uapp = new UsuariosApp() )
{
// get all profiles in a single call, no loop required
var perfils = uapp.LocalizarPerfiles( model.PerfisSelecionados );
model.Usuario.Perfis.AddRange( perfils );
uapp.SalvarUsuario( model.Usuario );
}
return RedirectToAction( "Usuarios" );
}
return View( model );
}
Let me know if that doesn't solve your problem.
Hey Guys I hava a question. I know questions like this are asked often, but I worked on a solution for several hours and read many answers but I couldnt find the right one. I am doing an application using ASP.NET MVC 4 Razor. I´m rather new to this system. I created a .edmx Data Model using Entity Framework 5 (Database-First Approach). This is how my auto-generated Context class looks like:
namespace KSM3.Models
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
public partial class kontrollsystemEntities : DbContext
{
public kontrollsystemEntities()
: base("name=kontrollsystemEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
[EdmFunction("kontrollsystemEntities", "udf_GetReportsByController")]
public virtual IQueryable<udf_GetReportsByController_Result> udf_GetReportsByController(string controller_account)
{
var controller_accountParameter = controller_account != null ?
new ObjectParameter("controller_account", controller_account) :
new ObjectParameter("controller_account", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<udf_GetReportsByController_Result>("[kontrollsystemEntities].[udf_GetReportsByController](#controller_account)", controller_accountParameter);
}
}
}
and my Model class looks like this:
namespace KSM3.Models
{
using System;
public partial class udf_GetReportsByController_Result
{
public int ID { get; set; }
public string ProviderID { get; set; }
public int VertragID { get; set; }
public System.DateTime Leistungszeitraum_von { get; set; }
public System.DateTime Leistungszeitraum_bis { get; set; }
public string ReportklasseID { get; set; }
public int Version { get; set; }
public string Status { get; set; }
}
}
When I now click on "Add Controller" and select my classes, I get the error message:
"Unable to retrieve Metadata for KSM3.Models.udf_GetReportsByController_Result.cs"
Note: I am using Entity Framework to retrieve information from a user-defined function, not from a table! If I try the same procedure with a table, it works!
What do I have to prepare or change in order to make this work?
Thank you for all answers!
I have solved my problem, thanks!
I had to call the udf_GetReportsByController(string controller_account) method in the controller and hand the IQueryable-Result to my view.
My Controller looks like these (Note: Beginner´s mistake)
public class ReportController : Controller
{
private kontrollsystemEntities db = new kontrollsystemEntities();
//
// GET: /Report/
public ActionResult Index()
{
IQueryable<udf_GetReportsByController_Result> result = db.udf_GetReportsByController(User.Identity.Name);
return View(result.ToList());
}
}
}