SQLite DB built from fluent nhibernate description produces columns that are not nullable - nhibernate

I have the following code to create an in-memory SQLite DB for testing:
Configuration config = null;
FluentConfiguration fluentConfiguration = Fluently.Configure().Database(SQLiteConfiguration.Standard.InMemory().ShowSql()
).Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<ReturnSourceMap>();
m.HbmMappings.AddFromAssemblyOf<ReturnSourceMap>();
m.FluentMappings.AddFromAssemblyOf<EchoTransaction>();
}).ExposeConfiguration(c => config = c);
ISessionFactory sessionFactory = fluentConfiguration.BuildSessionFactory();
_session = sessionFactory.OpenSession();
new SchemaExport(config).Execute(true, true, false, _session.Connection, Console.Out);
which seems to work fine for most things but unfortunately it is creating all the columns as not nullable.
For instance I have two classes:
InternalFund and ExternalFund. Both inherit from Fund and both persist to the same table.
ExternalFund has a column Manager_ID, InternalFund doesn't. Unfortunately this means that I can't persist an InternalFund as it throws a SQL Exception.
I don't need the referential integrity for my tests so would be happy if I could just make all columns nullable.
Does anyone know how to do this?
Thanks
Stu

You should try using the FluentNhibernate feature of Conventions. If you check out this link you will see the first example is of setting nullability as a default.

Oops, terribly sorry. It seems that the SQLite DB assumes nullability unless otherwise stated - unlike TSQL which requires it in the create statement.
In fact, I found hidden away in a sub-method that the particular property had a not null set on it in the mapping. Removing this sorted the problem.
Thanks for posting about conventions though, I'll have a look as I have another issue that they might sort.
Cheers
Stu

Related

Nhibernate Evers, how to change AuditJoinTable name

i'am using fluent Nhibernate and Envers with this setup
var enversConf = new NHibernate.Envers.Configuration.Fluent.FluentConfiguration();
enversConf.Audit<Segnalazione>()
IRevisionListener revListner = services.GetService<IRevisionListener>();
enversConf.SetRevisionEntity<RevisionEntity>(e => e.Id, e => e.RevisionDate, revListner);
cfg.SetEnversProperty(ConfigurationKey.AuditTableSuffix, "_LOG");
cfg.SetEnversProperty(ConfigurationKey.AuditStrategy, typeof(CustomValidityAuditStrategy));
cfg.IntegrateWithEnvers(enversConf);
i need to change AuditJoinTable naming adding a prefix XXX_
All others table have same prefix and so standard logging table inherits it, only JoinTable hasn't it
i found settings for java version but not for .net one
EDIT:
Now i have table with this naming convention:
XXX_Table1
XXX_Table2
main log table are create with _LOG suffix, so i get
XXX_Table1_LOG
XXX_Table2_LOG
while
AuditJoinTable are created as
Table1Table2_LOG
and i need
XXX_Table1Table2_LOG
I am solving this by adding the name to each join table. Could be more generic but it works.
enversConf.Audit<Segnalazione>()
.SetTableInfo(ug => ug.Foo, t => t.TableName = "XXX_Segnalazione_Foo")
Do you mean that cfg.SetEnversProperty(ConfigurationKey.AuditTablePrefix, "XXX_") doesn't work? It should.
IEnversNamingStrategy is used to decide names for tables, default this one is used where ConfigurationKey.AuditTablePrefix is used for "default prefix". You can also inject your own impl of this interface if you want to.
Using SetTableInfo overrides this and surely works but if I understand you correctly you don't need to do that in this case.

Entity Framework 6: Store update, insert, or delete statement affected an unexpected number of rows (0)

I am hoping someone can help me identify the cause of the following error when doing on update in Entity Framework.
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
From what I have read, it means that data has changed between the fetch and save, however this is not the case in my situation. (Single developer running the application locally. I also have viewed the data in the database before I attempt to save, and it is the exact same as when I did the fetch. Also able to reproduce this on command.)
I am not sure if this is relevant, but it seems to be the only factor that is different than other entities that are working. I have an entity that represents a table with a composite key. The value that I am updating is one of the values that makes up the composite key. There is only one record in the table at the moment, so I know there is no primary key violation stuff going on.
Does anyone know what steps I can take to find out what the actual problem is?
Thanks
If you have instead of trigger on table you are inserting to, it causes this error. You have to rewrite trigger to after insert trigger, or at the end of trigger select new generated id. Maybe for update there is similar problem. Look at query entity framework generates - it can help you to see what's going on.
EDIT:
To see generated queries set logging:
public class CustomContext : DbContext
{
public CustomContext()
: base("name=CustomString")
{
// configure writing queries to console
Database.Log = Console.Write;
}
// other context stuf ...
}
Or use some profiler (for sql server express you can use http://expressprofiler.codeplex.com/).
The problem resides in the fact that you call the method _dataContext.SaveChanges();, but nothing changed in the data. To avoid this error try this:
public void EditCustomer(Customer customer)
{
_dataContext.Customer.Attach(customer);
var entry = _dataContext.Entry(customer);
if(entry.Property(e => e.DeviceId).CurrentValue != entry.Property(e => e.DeviceId).OriginalValue)
{
entry.Property(e => e.DeviceId).IsModified = true;
}
if(entry.Property(e => e.Name).CurrentValue != entry.Property(e => e.Name).OriginalValue)
{
entry.Property(e => e.Name).IsModified = true;
}
if(entry.Property(e => e.DeviceId).IsModified || entry.Property(e => e.Name).IsModified)
{
_dataContext.SaveChanges();
}
}
I hope this helps you.
#DonPablone
I've encountered with such error message, my environment is as follows, SQL server 2016 along with ef6 database first and the issue was that the database developer did not define an identity seed column in the Id column of the table I'm inserting data into, and of course the issue solved when I updated the table design, so I'm sharing this experience in case if anyone got the same issue.
If by any chance, we have the same problem when trying to update record using Attach() and then SaveChanges() combination? This may help...
I am using SQLite DB and its EF provider (the same code works in SQLServer DB without problem).
I found out, when your DB column has GUID (or UniqueIdentity) in SQLite and your model is nvarchar, SQLIte EF treats it as Binary(i.e., byte[]) by default. So when SQLite EF provider tries to convert GUID into the model (string in my case) it will fail as it will convert to byte[]. The fix is to tell the SQLite EF to treat GUID as TEXT (and therefore conversion is into strings, not byte[]) by defining "BinaryGUID=false;" in the connectionstring (or metadata, if you're using database first) like so:
<connectionStrings>
<add name="Entities" connectionString="metadata=res://savetyping...=System.Data.SQLite.EF6;provider connection string="data source=C:\...\db.sqlite3;Version=3;BinaryGUID=false;App=EntityFramework"" providerName="System.Data.EntityClient" />
</connectionStrings>
Link to the solution that worked for me:
How does the SQLite Entity Framework 6 provider handle Guids?

How do I wrap an EF 4.1 DbContext in a repository?

All,
I have a requirement to hide my EF implementation behind a Repository. My simple question: Is there a way to execute a 'find' across both a DbSet AND the DbSet.Local without having to deal with them both.
For example - I have standard repository implementation with Add/Update/Remove/FindById. I break the generic pattern by adding a FindByName method (for demo purposes only :). This gives me the following code:
Client App:
ProductCategoryRepository categoryRepository = new ProductCategoryRepository();
categoryRepository.Add(new ProductCategory { Name = "N" });
var category1 = categoryRepository.FindByName("N");
Implementation
public ProductCategory FindByName(string s)
{
// Assume name is unique for demo
return _legoContext.Categories.Where(c => c.Name == s).SingleOrDefault();
}
In this example, category1 is null.
However, if I implement the FindByName method as:
public ProductCategory FindByName(string s)
{
var t = _legoContext.Categories.Local.Where(c => c.Name == s).SingleOrDefault();
if (t == null)
{
t = _legoContext.Categories.Where(c => c.Name == s).SingleOrDefault();
}
return t;
}
In this case, I get what I expect when querying against both a new entry and one that is only in the database. But this presents a few issues that I am confused over:
1) I would assume (as a user of the repository) that cat2 below is not found. But it is found, and the great part is that cat2.Name is "Goober".
ProductCategoryRepository categoryRepository = new ProductCategoryRepository();
var cat = categoryRepository.FindByName("Technic");
cat.Name = "Goober";
var cat2 = categoryRepository.FindByName("Technic");
2) I would like to return a generic IQueryable from my repository.
It just seems like a lot of work to wrap the calls to the DbSet in a repository. Typically, this means that I've screwed something up. I'd appreciate any insight.
With older versions of EF you had very complicated situations that could arise quite fast due to the required references. In this version I would recomend not exposing IQueryable but ICollections or ILists. This will contain EF in your repository and create a good seperation.
Edit: furthermore, by sending back ICollection IEnumerable or IList you are restraining and controlling the queries being sent to the database. This will also allow you to fine tune and maintain the system with greater ease. By exposing IQueriable, you are exposing yourself to side affects which occur when people add more to the query, .Take() or .Where ... .SelectMany, EF will see these additions and will generate sql to reflect these uncontrolled queries. Not confining the queries can result in queries getting executed from the UI and is more complicated tests and maintenance issues in the long run.
since the point of the repository pattern is to be able to swap them out at will. the details of DbSets should be completly hidden.
I think that you're on a good path. The only thing I probaly ask my self is :
Is the context long lived? if not then do not worry about querying Local. An object that has been Inserted / Deleted should only be accessible once it has been comitted.
if this is a long lived context and you need access to deleted and inserted objects then querying the Local is a good idea, but as you've pointed out, you may run into difficulties at some point.

JPA & Ebean ORM: Empty collection is not empty

I've started switching over a project from hand-written JDBC ORM code to Ebeans. So far it's been great; Ebeans is light and easy to use.
However, I have run into a crippling issue: when retrieving a one-to-many list which should be empty there is actually one element in it. This element looks to be some kind of proxy object which has all null fields, so it breaks code which loops through the collection.
I've included abbreviated definitions here:
#Entity
class Store {
...
#OneToMany(mappedBy="store",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
List<StoreAlbum> storeAlbums = new LinkedList<StoreAlbum>();
}
#Entity
class StoreAlbum {
...
#ManyToOne(optional=false,fetch=FetchType.EAGER)
#JoinColumn(name="store_id",nullable=false)
Store store;
}
The ... are where all the standard getters and setters are. The retrieval code looks like this:
Store s = server.find(Store.class)
.where()
.eq("store_id",4)
.findUnique();
Assert.assertEquals("Sprint",s.getStoreName());
Assert.assertEquals(0, s.getStoreAlbums().size());
The database is known to contain a 'store' row for "Sprint", and the 'store_album' table does not contain any rows for that store.
The JUnit test fails on the second assertion. It finds a list with 1 element in it, which is some kind of broken StoreAlbum object. The debugger shows the object as being of the type "com.lwm.catalogfeed.domain.StoreAlbum$$EntityBean$test#1a5e68a" with null values for all the fields which are declared as nullable=false (and optional=false).
Am I missing something here?
Thought I'd post an update on this... I ended up giving up on EBeans and instead switched the implementation over to use MyBatis. MyBatis is fantastic; the manual is easy to read and thorough. MyBatis does what you expect it to do. I got it up and running in no time.
EBeans didn't appear to detect that the join for the associated collection resulted in a bunch of null ids, but MyBatis handled this scenario cleanly.
I ran into the same issue and was able to solve it by adding an identity column to the secondary table (StoreAlbum). I did not investigate the cause but I suppose Ebean needs a primary key on the table in these kind of situations.

NHibernate Session.SetReadOnly

I'm facing the same issue others already posted on SO: On reading objects from the database, NHibernate would update all the objects because the value of one field is not proper in the DB.
(In detail: A newly added date column contains "1/1/0001" in all rows, so on mapping, NHibernate replaces the date and, on tx.Commit(), updates every single row.)
[Edit: It turned out that this was wrong. Instead, these date fields were null but would be updated to 1/1/0001 by NHibernate. See Diego's answer for details.]
To prevent that, I found this post with an answer by Ben Scheirman as well as the blog comment referred to by the OP.
The commenter Christian says:
You can also disable automatic dirty checking and updating by disabling the snapshots in Hibernate with session.setReadOnly(o, true) or for all queried objects with query.setReadOnly(true).
(Note that this blog post is about Java Hibernate.)
query.SetReadOnly(true) was successful where I used queries. However, I also have code like this:
ISession session = this.NHibernateHelper.SessionFactory.OpenSession();
ITransaction tx = session.BeginTransaction();
try
{
BO resultBO = session.Get<BO>(id);
tx.Commit();
return resultBO;
}
catch (Exception ex)
{
tx.Rollback();
throw ex;
}
finally
{
session.Close();
}
In this case I don't have a query, and the said Session.SetReadOnly(resultBO, true) does not exist in NHibernate. Where has it gone?
I guess "evict" is basically not a good idea because it makes the object transient so I can't use it to update values in another session (at least it gets more complicated. I would also need to make sure all objects are always evicted so my generic update methods wouldn't need to differentiate between persistent and transient objects - or am I completely wrong?
Thanks and cheers,
chiccodoro
You are attacking the symptom instead of the disease.
What you have there is a ghost (see http://jfromaniello.blogspot.com/2010/02/nhibernate-ghostbuster-version-11.html)
Make the property nullable and you'll be fine.