What's the best way to test your NHibernate Mapping classes?
Let's assume I want to test the following map:
public QuoteMap()
{
this.Table("QUOTE");
this.Id(x => x.Id).Column("QUOTE_ID").GeneratedBy.Sequence("SEQ_QUOTE_ID");
this.Map(x => x.IsDeleted).Column("IS_DELETED");
this.References(x => x.Proposal).Column("PROPOSAL_ID");
}
where Proposal type is mapped to another table.
QUOTE table looks like this:
CREATE TABLE "QUOTE"
(
"QUOTE_ID" NUMBER(18,0) NOT NULL,
"PROPOSAL_ID" NUMBER(18,0) NOT NULL ENABLE,
"IS_DELETED" NUMBER(1,0) DEFAULT 0 NOT NULL ENABLE,
CONSTRAINT "PK_QUOTE" PRIMARY KEY ("QUOTE_ID"),
CONSTRAINT "FK_QUOTE_PROPOSAL" FOREIGN KEY ("PROPOSAL_ID") REFERENCES
"PROPOSAL" ("PROPOSAL_ID") ENABLE
)
Option1: PersistenceSpecification
new PersistenceSpecification<Quote>(session, new CustomEqualityComparer())
.CheckProperty(c => c.TenantId, 1)
.CheckProperty(c => c.IsDeleted, false)
.CheckReference(
c => c.Proposal,
new Proposal
{
Id = 1,
IsDeleted = false,
TenantId = 1,
VersionNumber = 1,
OutletId = 1,
StatusId = "TST"
})
.VerifyTheMappings();
transaction.Commit();
...this test would fail with the following exception:
NHibernate.Exceptions.GenericADOException: could not insert:
[Quote#18][SQL: INSERT
INTO QUOTE (IS_DELETED, PROPOSAL_ID, QUOTE_ID)
VALUES (?, ?, ?)] ---> Oracle.DataAccess.Client.OracleException:
ORA-02291: integrity constraint (PROPOSALOWN.FK_QUOTE_PROPOSAL)
violated - parent key not found
...because it has a dependency on the PROPOSAL record with Id = 1 being there.
Another problem with this is if you have coincidently mapped PropertyA to ColumnB and PropertyB to ColumnA your test would pass and won't point out your mistake.
Option2: Raw SQL to INSERT, NHibernate to SELECT
Now this would be ideal: you insert by issuing a raw SQL statement e.g.
INSERT INTO QUOTE (QUOTE_ID, PROPOSAL_ID, IS_DELETED)
SELECT SEQ_QUOTE_ID.NextVal, 1, 0 from dual;
Then you read using Nhibernate and check the values.
The problem is, again, dependency on PROPOSAL record being there. Insert PROPOSAL record for this test? Sure! However, Proposal table has another set of FOREIGN KEYS, so you may end up inserting ROWS into dozens of tables just to test your mapping... no likey!
Surely there's a much better, simpler way to test NHibernate Maps. Could you suggest one?
You should be using CheckReference for the proposal:
.CheckReference(
c => c.Proposal,
new Proposal
{
IsDeleted = false,
TenantId = 1,
VersionNumber = 1,
OutletId = 1,
StatusId = "TST"
})
Related
In a DataBase first .net 6 app I get the following error
"Npgsql.PostgresException (0x80004005): 42703: no existe la columna b.CampusId"
Hint: Probablemente quiera hacer referencia a la columna «b.campusid».
Which means that the column b.CampusId does not exist as a column, and as a hit it tells me you are probably looking for b.campusid.
But I have the following schema:
CREATE TABLE "Campus" (
"CampusId" SERIAL PRIMARY KEY,
"Name" varchar(250) NOT NULL,
"UniversityId" int NOT NULL,
"StateId" int NOT NULL,
"CreatedAt" TIMESTAMP NOT NULL DEFAULT NOW(),
"ModifiedAt" TIMESTAMP,
UNIQUE("Name", "UniversityId", "StateId"),
FOREIGN KEY ("UniversityId")
REFERENCES "University" ("UniversityId"),
FOREIGN KEY ("StateId")
REFERENCES "State" ("StateId")
);
And the part where the code breaks is in this one:
var campus_ids = await _context.Set<Campus>().FromSqlRaw<Campus>(
$"SELECT * FROM \"Campus\" c WHERE LOWER(UNACCENT(c.\"Name\")) LIKE '%{search}%' LIMIT {MAX_RESULTS} ")
.AsNoTracking()
.Select(x => x.CampusId) // This part is the one it complains about
.ToListAsync();
I do not get why the error ocurs in the Select part, since the Campus Model has the exact property name as the column in the Campus TABLE, both are named exactly CampusId, and I created them with quotes so it could be case sensitive, but still npgsql hints me about the correct name being in lowercase, notwithstanding the data base shows the column exactly like CampusId.
The generated code by EF is:
SELECT b.campusid
FROM (
SELECT * FROM "Campus" c WHERE LOWER(UNACCENT(c."Name"))
LIKE '%universidad%' LIMIT 20
) AS b
So, my questions are:
Why it yielded that bad translation when the entity name is CampusId?
How can I make EF to translate its queries to the appropiate property model and column names?
It should have been 'SELECT b.CampusId' instead of what it actually yielded.
I want SQL query equivalent in EF Core Lambda, to get an extra column as either true or false, based on some substring(in this case DEALER) if present in other column's data of the same table.
myTable
Id col1
1 I have substring - DEALER
2 I do not have any substring
I need the output as
Id, IsDealer
1, true
2, false
I tried the following SQL query,
SELECT [Id] ,
CASE WHEN [col1] LIKE '%DEALER%' THEN 'true' ELSE 'false' END as IsDealer
FROM [dbo].[myTable]
I get proper output, But what is the above SQL query EF CORE equivalent ?
I tried
_Context.myTable.Where(et => et)
.Select(s => new myTableEFCoreModel
{
Id = s.Id,
**<what goes here for IsDealer>**
});
You're returning the string in col1 so just doing a string compare should be all you need:
_Context.myTable.Where(et => et)
.Select(s => new myTableEFCoreModel
{
/* same as select [Id] in sql query */
Id = s.Id,
/* same as case when [col1] like "%DEALER%" then 'true' else 'false' as IsDealer */
IsDealer = s.col1.Contains("DEALER")
});
You can have something like IsDealer = s.col1.Contains("TextYouWantToSearch")
In my project I've two Interesting database tables:
CREATE TABLE [dbo].[User]
(
[Id] INT NOT NULL IDENTITY,
[Name] NCHAR(60) NOT NULL,
PRIMARY KEY ([Id])
)
CREATE TABLE [dbo].[Event]
(
[Id] INT NOT NULL PRIMARY KEY identity,
...
[User id] INT NULL,
CONSTRAINT [FK_Event_User] FOREIGN KEY ([User id]) REFERENCES [User](Id)
)
I want to have access to the Event's users in my Application which take data from WCF.
To make this possible I've to change Child Property in User_Event Association according to this answer.
I use LINQ SQL and I noticed that event.User property is not null only when I invoke get properties on it.
Now I have to add for loop to force invoking this._User.Entity, but it don't look like this solution...
public List<Event> GetAllEvents()
{
var ev = Database.Instance.Db.Events;
foreach (var v in ev)
{
User u = v.User;
}
return ev.ToList();
}
Probably the problem was caused by DataContext.DeferredLoadingEnabled Property.
According to msdn:
DeferredLoadingEnabled
Gets or sets a value that indicates whether to delay-load one-to-many or one-to-one relationships.
update
finally I've solved this problem with LoadWith function from Linq to SQL.
db = new ModelDataContext();
/* setting lazy evaluation rules */
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith((Session s) => s.Exam);
dlo.LoadWith((Session s) => s.SessionUsers);
db.LoadOptions = dlo;
I am particularly confused by the following test case:
public void TestMapping()
{
var autoPersistenceModel = AutoMap.AssemblyOf<RepositoryEntity>().Where(
x => x.Namespace.EndsWith("Descriptors"));
var configuration = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.ShowSql().InMemory)
.Mappings(x => x.AutoMappings.Add(autoPersistenceModel))
.ExposeConfiguration(x => new NHibernate.Tool.hbm2ddl.SchemaExport(x).Create(true, false));
var sessionFactory = configuration.BuildSessionFactory();
using (var session = sessionFactory.OpenSession())
{
new PersistenceSpecification<IndicatorUnitDescriptor>(session)
.CheckProperty(x => x.Name, "Name1")
.CheckProperty(x => x.Timestamp, new DateTime(2000, 10, 10))
.VerifyTheMappings();
}
}
As you can see, I'm experimenting with automappings, but unfortunately the following test case raises the following SQLite exception (the first contains the actual queries done):
drop table if exists "IndicatorUnitDescriptor"
drop table if exists "StockUnitDescriptor"
create table "IndicatorUnitDescriptor" (
Id integer,
Name TEXT,
Timestamp DATETIME,
primary key (Id)
)
create table "StockUnitDescriptor" (
Id integer,
Name TEXT,
Timestamp DATETIME,
primary key (Id)
)
NHibernate: INSERT INTO "IndicatorUnitDescriptor" (Name, Timestamp) VALUES (#p0, #p1); select last_insert_rowid();#p0 = 'Name1' [Type: String (0)], #p1 = 10.10.2000 0:00:00 [Type: DateTime (0)]
System.Data.SQLite.SQLiteException: SQLite error
no such table: IndicatorUnitDescriptor
And I can't understand why does it happen this way - the SQL commands seem to be working appropriately and the corresponding table should be created by the create table query.
I assume something is wrong in my code (I probably missed something). Could you help me?
I think your using two sessions. One during database creation and in your test proper. Try setting up like this.
Configuration cfg = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
.Mappings(m => m.HbmMappings.AddFromAssembly(_mappingsAssembly))
.BuildConfiguration();
var session = cfg.BuildSessionFactory().OpenSession();
new SchemaExport(cfg).Execute(false, true, false, session.Connection, null);
i want to set a new value to my entity objects FK column, but i cant find the property to set. I dont want to get record from db.
I have a db like that
Db Tables:
Concept ConceptType
-Id (PK) -Id(PK)
-Name -Name
-ConceptTypeId(FK) (ALLOW NULL)
Code:
Concept conceptToUpdate = new Concept() { Id = 1 };
ConceptType conceptType = new ConceptType() { Id = 5 };
db.AttachTo("Concept", conceptToUpdate);
db.AttachTo("ConceptType", conceptType);
conceptToUpdate.ConceptType = conceptType;
db.SaveChanges();
This code is working if ConceptTypeId(FK) column is NULL before. If it is not NULL it gives exception. I trace the sql query, the problem is on sql query because it is checking that old value is NULL :S
SQL QUERY: (from SQL Profiler)
exec sp_executesql N'update [dbo].[Concept]
set [ConceptTypeId] = #0
where (([Id] = #1) and [ConceptTypeId] is null)
',N'#0 int,#1 int',#0=5,#1=1
The reason it fails is that in 3.5 SP1 is that for relationships the old FK value is part of the concurrency check, as you found via SQL profiler.
What you need is something like this:
Concept conceptToUpdate = new Concept() {
Id = 1 ,
ConceptType = new ConceptType {Id = OriginalFKValue}
};
ConceptType conceptType = new ConceptType() { Id = 5 };
db.AttachTo("Concept", conceptToUpdate);
db.AttachTo("ConceptType", conceptType);
conceptToUpdate.ConceptType = conceptType;
db.SaveChanges();
This means you need to know not just the Id of the thing you want to update, but also it's original FK values, which is of course a real pain.
Hence a new feature in EF 4.0 called FK Associations. With FK Associations the original value of the FK is not part of the concurrency check.
Hope this helps
Alex