SqlException: Invalid object name 'Products' - c#

I am trying to create a database-first approach ASP.NET Core 6 MVC web
app.
I decided to use Microsoft's AdventureWorks sample database for this.
In short, I am trying to get some information from a table called Production.Product.
Here is the code:
Product Class:
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string ProductNumber { get; set; }
// More properties.
}
Context:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
public virtual DbSet<Product> Products { get; set; }
}
I add the AppDbContext in the Program class as every developer would, nothing special.
For testing purposes I use the HomeController to get the data.
public class HomeController : Controller
{
private readonly AppDbContext _context;
public HomeController(AppDbContext context)
{
_context = context;
}
// I have the view created.
public IActionResult GetProducts()
{
var model = _context.Products.ToList();
return View(model);
}
}
And when I go to the GetProducts view, I am greeted with this error:
An unhandled exception occurred while processing the request.
SqlException: Invalid object name 'Products'.
Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, bool breakConnection, Action wrapCloseInAction)
I doubled check the connection string. Tried renaming the Product class to Production_Product.

In short, I am trying to get some information from a table called
Production.Product.And when I go to the GetProducts view, I am greeted
with this error:"SqlException: Invalid object name 'Products'". I
doubled check the connection string. Tried renaming the Product class
to Production_Product.
Well, based on your description, I have successfully reproduce your issue. As you can see below:
Why the error for:
Generally the exception telling us, AppDbContext property Products doesn't matched with the AdventureWorks database schema or table name. Let have a look on the blow screenshot:
As you can see in the database schema table name consturction followed format Production.Product. Thus, while your entity executing query its searching by DbSet<Product> Products but Product obviously doesn't exists or matched as a result we ended with with the error.
How to resolve:
We can solve the error following couple of ways. Here I am completely agreed with #marc_s. Therefore, I am adding, complete implementations.
Way: 1 Using Entity framework Data Annotation:
If you prefer using data annotations of entity framework so you have to modify your Product class as following:
[Table("Production.Product")]
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public string ProductNumber { get; set; }
}
Note: Main point is that, database schema name should be same as your model or class. You can get more details in official document here.
Way: 2 Using Fluent API Validation:
In this scenario you should modify your code same as blow:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().ToTable("Production.Product");
}
Full DbContext:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().ToTable("Production.Product");
}
}
Note: As said earlier, database table name must be same with the AppDbContext entity name. You can get more details here.
Way: 3 Rename database schema table name:
We even can rename our database table name from Production.Product to Product or we can create new table with name Product. Either way would resolve the issue. Have a look below:
This would also resolve your issue accordingly.
Output:
Note: The takeaways are, your database table name should be matched with your entity model which defined in AppDbContext. On top of that, you could resolve the issue following either way has been described above.

Related

cannot perform override in class inheritance in ADO .NET Entity Model edmx

I have to build a .net web application accessing tables of an existing db.
The db uses different tables for different companies: customers in company "ACorp" are stored in table "ACorpCustomers", those in company "B" are stored in table "BCorpCustomers".
Using ADO .NET Entity Model, I created a different Db Context for each Company:
public partial class ACorpContext : DbContext
{
public ACorpContext()
: base("name=ACorpContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<ACorpCustomer> ACorpCustomers { get; set; }
}
}
The edmx generates also the class
public partial class ACorpCustomer
{
public string Name { get; set; }
public string Phone { get; set; }
}
I created a parent class Customer to be used in the application, with the same properties:
public class ACorpCustomer
{
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
}
I havent't found a way to let the specific entity ACorpCustomers inherit from the parent Customer; the edmx returns the inheritance error, but there is no way to override the properties.
Update
In order to avoid edmx file usage, this is what I finally tried out:
I disabled the __MigrationHistory sql table creation using the AutomaticMigrationsEnabled parameter:
internal sealed class Configuration : DbMigrationsConfiguration<MyDomain.Models.ACorpContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
I disabled the db initialization in the App.config file setting
disableDatabaseInitialization="true"
Then I added a an ADO .NET Entity Model but chose the "code first from database".
In order to be sure not to change the db from the model, I disabled the DB Initializer:
public ACorpContext()
: base("name=ACorpContext")
{
Database.SetInitializer<ACorpContext>(null);
}
Now I expect to be my responsability to be keep in sync the domain model with the db.
Anyway, I feel sure that in case of misalignment no attempt will be done to modify the db.
Without the edmx, I have no more limitations defining inheritance from an abstract class Customer.
I cannot understand why Visual Studio considers this as "Code First" approach, anyway.
Your definition
public partial class ACorpCustomer
has nothing to do with inheritance. partial is a .NET moderator that signifies that your class definition is a part of the bigger definition. For example if you have your class split between 2 code files. .Net "puts" them together and you endup with one type
Here what you seem need to do is
public abstract class Customer
{
public string Name { get; set; }
public string Phone { get; set; }
}
public class ACorpCustomer : Customer
{
// may be, some unique properties here
}
public class BCorpCustomer : Customer
{
// may be, some unique properties here
}
The properties Name and Phone don't even need to be virtual. Looking back into your title, there is nothing that you need to override. Nothing that I see..
This is trivial in Code-First, which you can (and should) use with an existing database. Just map the single Customer entity to the correct table for each DbContext:
public partial class ACorpContext : MyBaseDbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().ToTable("ACorpContext");
}
public virtual DbSet<Customer> Customers{ get; set; }
}

Models from stored procedures

I am writing an application where I'm using code first design. The models returned from stored procedures do not map directly to an entity from the database.
The issue I am having is while I'm inheriting from an interface on each entity, I'm unable to use these custom models:
System.InvalidOperationException: Cannot create a DbSet for 'CategoryDetailEntity' because this type is not included in the model for the context.
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.CheckState()
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken)
at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable1.GetAsyncEnumerator() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToArrayAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
Here is the code in my context file:
// Shouldn't be in the database, this is pulled from a stored procedure
public DbSet<Entities.CustomEntities.CategoryDetailEntity> CategoryDetailEntities { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Exclude models that are not bound to a table
modelBuilder.Entity<Entities.CustomEntities.CategoryDetailEntity>().ToTable(nameof(CategoryDetailEntities), t => t.ExcludeFromMigrations());
}
The entity code:
[NotMapped]
public partial class CategoryDetailEntity : CategoryEntity
{
public int NumProducts { get; set; }
}
CategoryEntity:
[Table("Category")]
public partial class CategoryEntity : BaseEntity
{
public CategoryEntity Parent { get; set; } = null;
public string Icon { get; set; }
}
The base entity has the ID, Created By / Date, Modified By / Date.
Solution:
#JohnM was able to lead me to the solution. I had two issues.
I tried to create a base service class in which my Database models were using. This was the same in my WebAPI, where in the Startup.cs file, I was only using the Base Service to do dependency injection:
services.AddScoped(typeof(IBaseService<>), typeof(BaseService<>));
This meant that the stored procedure was never being called first, only the base class of simple crud functionality.
Once I added all of the services layers explicitly, I was able to call the GetAll function from the correct service as intended.
public CategoryController(ICategoryService service, IMapper mapper) : base(service, mapper) {}
instead of
public CategoryController(IBaseService<CategoryDetailModel> service, IMapper mapper) : base(service, mapper) {}
The accepted Solution solved the other issue, in which using Code First, I wanted to make sure that the model from the Stored Procedure was not included in the migration.
In the Context.cs file:
// Shouldn't be in the database, this is pulled from a stored procedure
public DbSet<CategoryDetailModel> CategoryDetails { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Exclude models that are not bound to a table
modelBuilder.Entity<CategoryDetailModel>().HasNoKey().ToView(null);
}
Using this, I no longer had to mark the Model as [NotMapped]
public partial class CategoryDetailModel : BaseEntity
{
public string Icon { get; set; }
public int NumProducts { get; set; }
public ImageEntity Image { get; set; }
}
For calling stored procedures I define a regular class to hold the results - so one property for each column in the result set. I then define it as a keyless entity type, so the following goes in the OnModelCreating() method of your database context class:
modelBuilder.Entity<MyModelClass>().HasNoKey().ToView(null);

Querying a view in SQL through Entity Framework Core

I am trying to query an already existing view in SQL Server which I am connected to in Visual Studio 2019.
I've created a class corresponding to the name of my Users database called UsersDbContext.:
class UsersDbContext : DbContext
{
public DbSet<EventView> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Data Source=REDACTED;User ID=REDACTED;Password=REDACTERD;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False");
}
}
In this database I have a view named eventview.fss.
How do I access the data from here from Entity Framework?
In my main class where I want to process the data:
private static readonly UsersDbContext _context = new UsersDbContext();
var myid = _context.Users.FromSqlRaw($"SELECT Id FROM eventview.fss");
In my models class I've added a model for the view. It is very simple:
public class EventView
{
public string Date { get; set; }
public string Id { get; set; }
}
What am I missing? Ideally I'd like to have the the rows from the view be put into a List<EventView> then I can work with the data from there.
Edit: when I try to run _context.Users.FromSqlRaw($"SELECT Id FROM eventview.fss").ToList(); I get the error for SqlException: Invalid object name eventview.fss
You can tell EF Core how to retrieve the data set so you don't need to use raw sql. Add a dbset in the context. In OnModelCreating, something like:
builder.Entity().HasNoKey().ToView("eventview.fss", "YourSchemaName");
My problem was that my connection string was missing the Database property. Once I've added it now I can pull the data.

Entity Framework invalid object name "dbo.EA_EmployeePerformance"

It is weird since I use the simple membership initialization my EF model cannot create a new table. My code was able to create the new table needed if there is a transaction required for the model.
This is my model :
public class EmployeeDBContext : DbContext
{
public EmployeeDBContext()
: base("DefaultConnection")
{
Database.SetInitializer<EmployeeDBContext>(new CreateDatabaseIfNotExists<EmployeeDBContext>());
}
public DbSet<EA_Employee> Employees { get; set; }
public DbSet<EA_EmployeePerformance> EmployeePerformances { get; set; }
public DbSet<EA_EmployeeRank> EmployeeRanks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
[Table("EA_EmployeePerformance")]
public class EA_EmployeePerformance
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
[Display(Name = "Employee Performance Id")]
public int EmployeePerformanceId { get; set; }
[Index("Performance", IsUnique = true)]
[MaxLength(20)]
[Required]
public string Performance { get; set; }
public EA_EmployeePerformance()
{
Performance = "";
}
}
Whenever I SaveChanges(), the DB throws me
"Invalid object name dbo.EmployeePerformance".
I check the database and the database only contains tables required from simple membership.
How can I auto create the tables when a transaction is occurred for that model?
--------- EDITED ---------
Now I know what is my problem. The problem is because I use multiple dbContext in one database. It is gonna be tricky to do the migration because migration only works on one dbContext isn't it?
So in my case the problem is because the original AccountDBContext from the simple membership is taking over my database, that is why my other dbContext such as the EmployeeDBContext cannot create the new table (please correct me if i'm wrong).
Is there a way for me to keep the multiple dbContexts in a single database?
First you have to change your Db Initializer to DropCreateDatabaseIfModelChanges otherwise, you will still have the old database and you will never get the model changes without deleting the database manually.
database. It is gonna be tricky to do the migration because migration only works on one dbContext isn't it?
For migration I recommend you to create one DbContext for migration that called for Example MigrationDbContext which contains the the all DbSets and the POCO objects, also when the POCO objects belong to other assembly then just reference them.
Try using Migrations, this will help you manage your changes in your code and update the database scheme accordingly.
Here is a guide for how to use it Migrations.

How get Entity Framework 6.0.0.0 to use SQL Identity and Seed for ID field

I'm very new to Entity Framework, so I'm sure this is a schoolboy error. I've check a few links here on Stackoverflow such as this thread, which gave me some things to try, but this has not fixed the issue.
I have a SQL database with a "tblPerson". The Primary Key is "PersonID" and it is set as the Identity, with a Seed=1 (int).
I then have a C#\ASP.NET front-end that I'm building.
I have a Class for Person:
public class Person
{
[Key]
public Int64 PersonID { get; set; }
[Required]
[StringLength(10)]
[RegularExpression("^[A-Za-z0-9]{2,10}$")]
public string Name { get; set; }
public bool Active { get; set; }
}
I have a Data Access Layer class that then handles the writing to the database:
public class PersonDAL : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>().ToTable("tblPerson");
modelBuilder.Entity<Person>().Property(x => x.PersonID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
public DbSet<Person> People { get; set; }
}
As you can see, I am telling it that PersonID field should be a database-generated identity.
Here is my code inside the Controller that calls an "Add":
public ActionResult Add(Person obj) //Validation runs here
{
if (ModelState.IsValid)
{
PersonDAL personDAL = new PersonDAL();
personDAL.People.Add(obj);
personDAL.SaveChanges();
personDAL.Dispose();
obj = null;
return View("AddPerson", obj);
}
else
{
return View("AddPerson", obj);
}
}
"obj" is populated via the razor view, but it will have PersonID=null, however, when I instantiate the Data Access Layer, and try to Add and Save, I would expect this to work, however, I get the error:
"Cannot insert the value NULL into column 'PersonID' the column does not allow nulls."
Any tips would be gratefully appreciated.
UPDATE: It seems that in my PersonDAL Data Access Layer, this line never gets triggered: public DbSet<Person> People { get; set; }So, I am not sure why the line on the Controller personDAL.People.Add(obj); is failing to set it? (I can prove all this by pulling back People as a a List to the controller, and it's count is 0). Any ideas anyone?
UPDATE - SILLY ERROR!! - I somehow managed to uncheck "Identity" in the SQL table design for PersonID in tblPerson. NOW FIXED.
SILLY ERROR!! - I somehow managed to uncheck "Identity" in the SQL table design for PersonID in tblPerson. NOW FIXED.

Categories