I'm writing a C# application that uses NHibernate to access the database. One of my data sources is a view (not a table) named content_profile. I have created the following sample class using NHibernate Attributes:
[Class(Table = "content_profile")]
public class ContentProfile
{
[Id(0, TypeType = typeof(int), Name = #"Id"), Generator(2, Class = #"identity"), Column(1, Name = #"Id")]
public virtual int Id { get; set; }
[NotEmpty]
[MinLength(1)]
public virtual string Name { get; set; }
[Property]
public virtual DateTime? CreationDate { get; set; }
[ManyToOne(Lazy = Laziness.False, Column = #"author_id")]
public virtual User Owner { get; set; }
}
When trying to update schema I get the following error:
NHibernate.Tool.hbm2ddl.SchemaUpdate [(null)] - Unsuccessful: alter table public.content_profile add constraint FK280FFEFD6A68A1F9 foreign key (author_id) references public.users
Npgsql.NpgsqlException:
"content_profile" - is not a table
How do I tell NHibernate that it is indeed a view, not a table, and indexes must not be created on schema update?
You can specify that there should not be done any action on the schema of a certain class map by adding SchemaAction.None, never really used attributes because it lacks features but it should have something like [Class(SchemaAction="None")]
Related
I'm working to create a c# application, and in a portion of the application; I'm looking to bring in a .csv to a data table; and then basically loop through each row and query a database to see if the data exists.
I'm testing a LINQ query; but I can't seem to get it to run and display anything. I have the following code setup to run below:
I have the database added and the connection tests succesfully; I have the classes setup. I've been following some courses on pluralsight to test; and I'm not sure what exactly I am doing wrong or missing.
Also as a note; the table name is actually ERP.PartTran, and not PartTran, but I wasn't succesful in setting that up for the db context; could that be why?
EDIT: Code added; images removed
public class EpiDB : DbContext
{
public DbSet<Tran> PartTran { get; set; }
}
public class Tran
{
public int TranNum { get; set; }
public string TranReference { get; set; }
public string PartNum { get; set; }
}
private static void QueryPartTran()
{
var db = new EpiDB();
int tranref = 4650374; //lookup number
var query = from Tran in db.PartTran
where Tran.TranNum == tranref
orderby Tran.TranNum
select Tran;
foreach (var Tran in query)
{
Debug.Print(Tran.PartNum);
}
}
If you have an existing database schema, the first thing to avoid soft exceptions is to disable schema creation/migration in EF. By default when EF connects to a database and goes to resolve the schema, if it comes across a table that it cannot resolve, it will create it. The clue I see that might be happening in this case is when you say the table is called [ERP].PartTran. I suspect you may find that your database has a new empty table called [dbo].Tran. (assuming SQL Server)
To disable schema creation:
In your Db Context constructor
public EpiDB()
{
Database.SetInitializer<EpiDB>(null);
}
This may go a long ways to identifying any bad schema assumptions that EF is making by convention. Jim's answer would be along the lines of where I would believe your problem will lie.
Entities should map relatively closely, if not identically to your table. Renaming an entity or properties to differ from the table to clarify it in code is fine, but you need to be sure that when you do this, you give EF enough information about your schema so that it can resolve the table correctly. If your table is named "PartTran" and your DbSet instance is named "PartTran", why would you want to name the entity "Tran" rather than "PartTran"?
If your application schema is "ERP" then you can avoid needing to specify the schema name on each entity by adding the following to your DbContext.OnModelCreating():
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("ERP");
// ...
}
Otherwise, if you are using multiple schemas then you will need to explicitly map the schema to use with a [Table] attribute or ToTable("{tableName}", "{SchemaName}") in EntityTypeConfig / modelBuilder config.
Next, ensure that your entity fields match the appropriate fields in the table. You don't need to map every field if you don't need them, but at a minimum you do need to map the Primary Key. On a guess from the PartTran entity, I'm guessing you're either missing something like a PartTranId column, or the PK is a composite key using the PartNum and TranNum columns. If you have a PartTranId or similar PK, add it to the entity along with a [Key] attribute. If the PK is a composite:
public class PartTran
{
[Key, Column(Order = 1)]
public int TranNum { get; set; }
public string TranReference { get; set; }
[Key, Column(Order = 2)]
public string PartNum { get; set; }
}
This should give you a few ideas to check out against your code base... To go further it would help to amend your question to include the related tables and any entities you have tried creating so far. Something like "PartTran" looks like a joining table for a many-to-many relationship between a "Part" table and a "Tran"(saction?) table. If that is the case there are a number of options how you can efficiently wire this up in EF to get the data out the way you want.
Try this:
public class EpiDB : DbContext
{
public DbSet<Tran> PartTran { get; set; }
}
[Table("PartTran", Schema = "ERP")]
public class Tran
{
public int TranNum { get; set; }
public string TranReference { get; set; }
public string PartNum { get; set; }
}
And maybe even:
public class EpiDB : DbContext
{
public DbSet<Tran> PartTran { get; set; }
}
[Table("PartTran", Schema = "ERP")]
public class Tran
{
[Key] // Is this your primary key field?
public int TranNum { get; set; }
public string TranReference { get; set; }
public string PartNum { get; set; }
}
This is my diagram in my database:
but when I use Entity Framework it was like that:
It hasn't table name ListSuiteQuestion but It has 2 new property in class Question and Suite:
enter image description here
Table ListSuiteQuestion is automatically created by sql because in sql we don't have something called many to many relationship (m:n) and sql creates another table to implement m:n relationship with keys containing the primary key of two relationship tables is also the name of the combination of the names of the two relation tables.
Within your entity framework by accessing each table through another table you have access to that table so there is no need to define it by entity framework. However, if you intend to customize or add a field to a third table you can manually build it into the code and then display it entity framework, though you don't need to.
if you want create it manually in code do like this :
public class Suite
{
//another property
public int IdSuite { get; set; }
public virtual ICollection<SuitQuestions> Questions { get; set; }
}
public class Question
{
//another property
public int IdQuestion { get; set; }
public virtual ICollection<SuitQuestions> Suites { get; set; }
}
public class SuiteQuestions
{
public Suite Suite { get; set; }
public int IdSuite { get; set; }
public Question Question { get; set; }
public int IdQuestion { get; set; }
//add custome property if you need
}
and config it.
It's correct. A question has a list of suites and a suite has a list of questions.
If you do:
var suite = context.Suites.Find(5);
var question = context.Questions.Find(30);
suite.Questions.Add(question);
// And update this suite object here;
You will see a new record in ListSuiteQuestion Tables with IdSuite = 5 and IdQuestion = 30. The class ListSuiteQuestion doesn't need to be created.
However, if you really want to create the class you have to add Id to the Table ListSuiteQuestion as primary key.
I have two model
1)
public class Indicator
{
public long ID { get; set; }
public string Name { get; set; }
public int MaxPoint { get; set; }
public string Comment { get; set; }
public DateTime DateChanged { get; set; }
public DateTime DateCreated { get; set; }
public virtual IList<CalculationType> CalculationTypes { get; set; }
public virtual IList<TestEntity> TestEntitys { get; set; }
public virtual IndicatorGroup IndicatorGroup { get; set; }
}
2)
public class CalculationType
{
public long ID { get; set; }
public string UnitName { get; set; }
public int Point { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
public virtual Indicator Indicator { get; set; }
public virtual IList<Сalculation> Calculations { get; set; }
}
I executing this code
var indicator = DataContext.Indicators.FirstOrDefault(i => i.ID == indicatorID);
var test = DataContext.CalculationTypes.FirstOrDefault();
first line return null on navigation property CalculationTypes
Second line return empty collection. Why?
UPDATE
snapshot database
project link https://github.com/wkololo4ever/Stankin
added Calculation
public class Сalculation
{
public long ID { get; set; }
public virtual CalculationType CalculationType { get; set; }
public virtual ApplicationUser Creator { get; set; }
}
1) Is Lazy Loading enabled? If not, you need to explicitly load your navigation properties with the '.Include' syntax.
2) Are you sure EF should be able to detect that relation? Did you use Code First or Database First?
Edit: 3) Are you sure there is data in your database and that the foreign key from Indicator to IndicatorGroup has a value for that specific record? I am saying this because the value "null" is valid if there is simply no data.
P.S. If you do not see a foreign key on Indicator called "IndicatorGroupId", there might be an "IndicatorId" on the table "IndicatorGroup", in which case - going from the names you provided - your database is misconfigured and you will need to use fluent syntax or data attributes to instruct EF on how to make the foreign keys.
Try this:
DbContext.Configuration.ProxyCreationEnabled = true;
DbContext.Configuration.LazyLoadingEnabled = true;
If DbContext.Configuration.ProxyCreationEnabled is set to false, DbContext will not load child objects for some parent object unless Include method is called on parent object. Setting DbContext.Configuration.LazyLoadingEnabled to true or false will have no impact on its behaviours.
If DbContext.Configuration.ProxyCreationEnabled is set to true, child objects will be loaded automatically, and DbContext.Configuration.LazyLoadingEnabled value will control when child objects are loaded.
I think this is problem:
Edit: 3) Are you sure there is data in your database and that the
foreign key from Indicator to IndicatorGroup has a value for that
specific record? I am saying this because the value "null" is valid if
there is simply no data.
P.S. If you do not see a foreign key on Indicator called
"IndicatorGroupId", there might be an "IndicatorId" on the table
"IndicatorGroup", in which case - going from the names you provided -
your database is misconfigured and you will need to use fluent syntax
or data attributes to instruct EF on how to make the foreign keys.
Try to this and make sure foreign key is corrected.
public class CalculationType
{
public long ID { get; set; }
public string UnitName { get; set; }
public int Point { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateChanged { get; set; }
[ForeignKey("IndicatorID")]
public string IndicatorId { get; set; } //this is the foreign key, i saw in your database is: Indicator_ID, avoid this, rename it to IndicatorID or IndicatorId
public virtual Indicator Indicator { get; set; }
public virtual IList<Сalculation> Calculations { get; set; }
}
Same behavior, but different root cause than selected answer:
Navigation property can also be null if you turned off myContext.Configuration.AutoDetectChangesEnabled
Very obvious, but this got me when I was implementing some performance improvments.
Check this out: Navigation Property With Code First . It mentions about why navigation property is null and the solutions of it.
By default, navigation properties are null, they are not loaded by
default. For loading navigation property, we use “include” method of
IQuearable and this type of loading is called Eager loading.
Eager loading: It is a process by which a query for one type of entity
loads the related entities as a part of query and it is achieved by
“include” method of IQueryable.
I experienced this issue, where navigation properites were not loaded, even when the Include statement was present.
The problem was caused by string-comparison differences between SQL Server and EF6 using .NET. I was using a VARCHAR(50) field as the primary key in my customers table and also, as a foreign key field in my audit_issues table. What I did not realize was that my keys in the customers table had two additional white space characters on the end; these characters were not present in my audit_issues table.
However, SQL Server will automatically pad whitespace for string comparisons. This applies for WHERE and JOIN clauses, as well as for checks on FOREIGN KEY constraints. I.e. the database was telling me string were equivalent and the constraint passed. Therefore I assumed that they actually were exactly equal. But that was false. DATALENGTH of one field = 10, while the DATALENGTH of the other = 8.
EF6 would correctly compose the SQL query to pull the foreign key related fields and I would see them both in the generated Sql query and in the results. However, EF6 would silently fail when loading the Navigation Properties because .NET does not consider those strings equal. Watch out for whitespace in string-type foreign key fields!.
This article helped me.
In sum :
Install-Package Microsoft.EntityFrameworkCore.Proxies
In Startup.cs
using Microsoft.EntityFrameworkCore;
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<MyDbContext>(builder =>
{
builder.UseLazyLoadingProxies(); // <-- add this
}, ServiceLifetime.Singleton);
This is a variant of Keytrap's answer. Using .NET 6 and EF Core 6, I created a ContextPartials.cs for any custom configurations that I don't want EF's Scaffold command to overwrite:
Required Package:
Install-Package Microsoft.EntityFrameworkCore.Proxies
Code (ContextPartials.cs):
// NOTE: I am not using the new file-scoped namespace on purpose
namespace DataAccess.Models.MyDatabase
{
// NOTE: This is a partial outside of the generated file from Scaffold-DbContext
public partial class MyDatabaseContext
{
// NOTE: This enables foreign key tables to become accessible
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseLazyLoadingProxies();
}
}
We need to change one of our tables to use a Guid as the primary key rather than an int. This system is in use in production and it appears that this change requires some additional work rather than the usual add-migration stuff.
There are two FK references to this table meaning that there are 3 total tables needing to be modified.
How should this be handled when using Code First?
CURRENT MODEL CODE
public class TableA
{
public int Id { get; set; }
public String Name { get; set; }
public bool IsActive { get; set; }
}
DESIRED MODEL CODE
public class TableA
{
public Guid Id { get; set; }
public String Name { get; set; }
public bool IsActive { get; set; }
}
First do the change on the code and then install or host your application and create the fresh database out of the new changes. Now you have the database with the new changes. So make a diff between the one in production ( after backing it up and import it in your test server) and the fresh one ( with the new change), now you can create your upgrade script from the diff for the production. Then first upgrade the database of production and then publish your app
I'm coding in C# using entity framework 5 and I have a model Voucher that is something like this:
public class Voucher
{
public int Id { get; set; }
public int AppId { get; set; }
public virtual App {get; set;}
public int? TradeMemberId { get; set; }
public int FiscalPeriodId { get; set; }
}
I have configured this model as:
ToTable("Voucher", "acc");
So that it is mapped to:
[acc].[voucher]
my App property is from the same database but in another schema:
[prf].[App]
Now when ef tries to query and fill App navigation property it cannot find it in acc schema.how can i mark this property as prf schema as we do for models?any help is appreciated.
If you proper define schema using data annotation. EF should take care of this, I have done this before and never ran into any issues.
[Table("Voucher", Schema = "acc")]
public class Voucher {...}
and
[Table("App", Schema = "prf")]
public class App{...}