Invalid LazyInitializationException on collections - c#

I've run into a truly frustrating problem and spent several hours trying every which way to sort out. When a collection is lazy loaded on some objects it throws a LazyInitializationException stating there is no session or the session is closed. After taking out the code into a clean console app for testing - I'm convinced the session simply cannot be closed! Here is the code:
private static ISessionFactory _sessionFactory;
static void Main(string[] args)
{
_sessionFactory = BuildFactory(#"*<ThisIsMyConnectionString>*");
using (var session = _sessionFactory.OpenSession())
{
var allContacts = session.Query<Contact>().Where(x=>x.EmployeeId.HasValue);
foreach (var contact in allContacts)
{
var allServices = contact.EmployeeServices.ToArray();
}
session.Dispose();
}
}
private static ISessionFactory BuildFactory(string connectionString = null)
{
return Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ConnectionString(connectionString))
.Mappings(m =>
{
m.FluentMappings.Conventions.AddFromAssemblyOf<TableNameConvention>();
m.FluentMappings.AddFromAssemblyOf<KioskAdapterConfigMapping>();
})
.BuildConfiguration().BuildSessionFactory();
}
And here is my (fluent) mappings:
public ServiceMapping()
{
Table("tblServices");
Id(x => x.Id, "ServiceId");
Map(x => x.Name);
}
public ContactMapping()
{
Table("tblContacts");
Id(x => x.Id, "ContactId");
Map(x => x.EmployeeId);
HasManyToMany(x => x.EmployeeServices)
.Table("lnkEmployeeService")
.ParentKeyColumn("EmployeeId")
.PropertyRef("EmployeeId")
.ChildKeyColumn("ServiceId")
.NotFound.Ignore();
}
How on earth is the session closed? Some database records do not have any records in the "lnkEmployeeService" table, but all foreign keys are in place and valid. Also the link table does have extra columns and is actually a composite key with other columns, but I don't care about rest of the data.

The problem is hidden in the fact, that the EmployeeServices collection is mapped to the Contact by the not-unique value "EmployeeId".
Other words, let's assume three contacts
ContactId, EmployeeId
1 , 1
2 , 2
3 , 2
Now, we instruct NHibernate be ready, to load 3 collections. They will/should/must be unique, for each of the contacts. So in the ISession, they could be kept by their unique identifier. In case of NOT property-ref mapping they would be like:
CollectionId - Collection
1 - the first collection of all Services related to contact with ID 1
2 - the first collection of all Services related to contact with ID 2
3 - the first collection of all Services related to contact with ID 3
But we do have a problem here, because our unique identifier for our collections is
CollectionId - Collection
1 ...
2 ...
2 - here we go... this (collection) or the previous
must now be removed from a session...
no way how to load/handle it any more
The key, must be unique, that's the point, even if this is the property-ref. On a bit different place of the documentation, we can see 5.1.10. many-to-one:
The property-ref attribute should only be used for mapping legacy data
where a foreign key refers to a unique key of the associated table
other than the primary key.
While this is not explained for <bag> mapping the logic is still the same

I think the problem is that you're calling Dispose on an IDisposable that's wrapped in a using block. The using block is the equivalent of writing
var myDisposable = new SomeIDisposableImplementation();
try { ... }
finally
{
if(myDisposable != null) myDisposable.Dispose();
}
In your code Dispose is called twice. My guess is that's causing the problem but I wasn't able to duplicate the exception in a simple test.

Related

Updating EF entities based on deep JSON data

I have a data structure which looks something like this: foo 1:* bar 1:* baz
It could look something like this when passed to the client:
{
id: 1,
bar: [{
id: 1,
baz: []
},
{
id: 2,
baz: [{
id: 1
}]
}
]
}
In my UI, this is represented by a tree structure, where the user can update/add/remove items on all levels.
My question is, when the user has made modifications and I'm sending the altered data back to the server, how should I perform the EF database update? My initial thought was to implement dirty tracking on the client side, and make use of the dirty flag on the server in order to know what to update. Or maybe EF can be smart enough to do an incremental update itself?
Unfortunately EF provides little if no help for such scenario.
The change tracker works well in connected scenarios, but working with disconnected entities has been totally left out for the develpers using the EF. The provided context methods for manipulating entity state can handle simplified scenarios with primitive data, but does not play well with related data.
The general way to handle it is to load the existing data (icluding related) from the database, then detect and apply the add/updates/deletes yourself. But accounting for all related data (navigation property) types (one-to-many (owned), many-to-one (associated), many-to-many etc), plus the hard way to work with EF6 metadata makes the generic solution extremely hard.
The only attempt to address the issue generically AFAIK is the GraphDiff package. Applying the modifications with that package in your scenario is simple as that:
using RefactorThis.GraphDiff;
IEnumerable<Foo> foos = ...;
using (var db = new YourDbContext())
{
foreach (var foo in foos)
{
db.UpdateGraph(foo, fooMap =>
fooMap.OwnedCollection(f => f.Bars, barMap =>
barMap.OwnedCollection(b => b.Bazs)));
}
db.SaveChanges();
}
See Introducing GraphDiff for Entity Framework Code First - Allowing automated updates of a graph of detached entities for more info about the problem and how the package is addressing the different aspects of it.
The drawback is that the package is no more supported by the author, and also there is no support for EF Core in case you decide to port from EF6 (working with disconnected entities in EF Core has some improvements, but still doesn't offer a general out of the box solution).
But implementing correctly the update manually even for specific model is a real pain. Just for comparison, the most condensed equivalent of the above UpdateGraph method for 3 simple entities having only primitive and collection type navigation properties will look something like this:
db.Configuration.AutoDetectChangesEnabled = false;
var fooIds = foos.Where(f => f.Id != 0).Select(f => f.Id).ToList();
var oldFoos = db.Foos
.Where(f => fooIds.Contains(f.Id))
.Include(f => f.Bars.Select(b => b.Bazs))
.ToDictionary(f => f.Id);
foreach (var foo in foos)
{
Foo dbFoo;
if (!oldFoos.TryGetValue(foo.Id, out dbFoo))
{
dbFoo = db.Foos.Create();
dbFoo.Bars = new List<Bar>();
db.Foos.Add(dbFoo);
}
db.Entry(dbFoo).CurrentValues.SetValues(foo);
var oldBars = dbFoo.Bars.ToDictionary(b => b.Id);
foreach (var bar in foo.Bars)
{
Bar dbBar;
if (!oldBars.TryGetValue(bar.Id, out dbBar))
{
dbBar = db.Bars.Create();
dbBar.Bazs = new List<Baz>();
db.Bars.Add(dbBar);
dbFoo.Bars.Add(dbBar);
}
else
{
oldBars.Remove(bar.Id);
}
db.Entry(dbBar).CurrentValues.SetValues(bar);
var oldBazs = dbBar.Bazs.ToDictionary(b => b.Id);
foreach (var baz in bar.Bazs)
{
Baz dbBaz;
if (!oldBazs.TryGetValue(baz.Id, out dbBaz))
{
dbBaz = db.Bazs.Create();
db.Bazs.Add(dbBaz);
dbBar.Bazs.Add(dbBaz);
}
else
{
oldBazs.Remove(baz.Id);
}
db.Entry(dbBaz).CurrentValues.SetValues(baz);
}
db.Bazs.RemoveRange(oldBazs.Values);
}
db.Bars.RemoveRange(oldBars.Values);
}
db.Configuration.AutoDetectChangesEnabled = true;

How do I get an identity value with Entity Framework(v5) before I save the record

I am new to entity framework and I have been searching a while for an answer to this question and I can't find anything that directally addresses this.
Here is the problem. I have a table in Oracle. In this table there are 2 fields(there are more but not important to this question). Card_Transaction_Id and Parent_Card_Transaction_ID. The Parent_Card_Transaction_Id field is constrained by the Card_Transaction_Id field and I am using a Oracle sequence via a trigger to populate the Card_Transaction_Id field.
In my code, I am using Entity Framework(Version 5) to connect using the Code First Approach.
The issue is when I try to create a new record. I need to know what the next sequence value is in order to populate the Parent_Card_Transaction_Id. My mapping for card transactions:
public class CardTransactionMap : EntityTypeConfiguration<CardTransaction>
{
public CardTransactionMap(string schema)
{
ToTable("CARD_TRANSACTION", schema);
// Mappings & Properties
// Primary Key
HasKey(t => t.CardTransactionId);
Property(t => t.CardTransactionId)
.HasColumnName("CARD_TRANSACTION_ID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(t => t.ParentCardTransactionId)
.HasColumnName("PARENT_CARD_TRANSACTION_ID");
Property(t => t.CardProfileId)
.HasColumnName("CARD_PROFILE_ID");
}
}
The question is - is there any way to get the next sequence number before I save the record?
My current work arround is to use the following method:
public static decimal GetNextCardTransactionSequenceValue()
{
using (var context = new Context(new OracleConnectionFactory().GetConnection()))
{
var sequence = context.Database.SqlQuery<int>("select card_transaction_id from card_transaction").ToList();
return sequence[0];
}
}
Using that method, I get the next value and then just populate my table. This works but I don't like doing it this way. I feel that there must be a better way to do it.
Thanks in advance.
You have to do this by navigation properties.
By fetching the next value from a sequence before actually using it in the same session you create yourself a concurrency issue: another user can increment the index (by an insert) in the time between drawing its next value and assigning it to the child record. Now the child will belong to the other user's record!
If your CardTransaction class has a parent reference like this:
int ParentCardTransaction { get; set; }
[ForeignKey("ParentCardTransaction")]
CardTransaction ParentCardTransaction { get; set; }
you can create a parent and child in one go and call SaveChanges without worrying about setting FK values yourself:
var parent = new CardTransaction { ... };
var child = new CardTransaction { ParentCardTransaction = parent, ... };
SaveChanges();
Now EF wil fetch the new CardTransactionId from the parent and assign it to the FK of the child. So generating and getting the parent Id happens all in one session, so it is guaranteed to be the same value.
Apart from preventing concurrency issues, of course it is much easier anyway to let EF do the heavy lifting of getting and assiging key values.
Create a Stored Procedure or Query that will return you the next Value from the Table here is an Example
SELECT NVL(MAX(card_transaction_id + 1), 0) AS MAX_VAL
FROM card_transaction T;
or Create a Trigger - for OracleDB
Change your table definition to this :
CREATE TABLE t1 (c1 NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
c2 VARCHAR2(10));
as per the information in the link i provided in the comment.
after the update ef will automatically query the value for the id that is inserted, there is no need to fill in the id before the insert. ef will generate an insert sql query without id.

Why become referential constraints inconsistent after updating foreign key?

Sorry for the nebulous title, it's hard to describe this in a single line:
I have 2 entities User and UserAddress, where User has 2 foreign keys DefaultInvoiceAddressId and DefaultDeliveryAddressId and UserAddress has a UserId foreign key.
The user object has navigation properties for the default addresses (DefaultInvoiceAddress and DefaultDeliveryAddress) as well as one for all of his addresses: AllAddresses.
The mapping etc. works, creating and updating users and addresses works too.
What does not work though is setting an existing Address of a User as e.g. DefaultInvoiceAddress. In SQL terms, what I want to happen is UPDATE USER SET DefaultInvoiceAddressId = 5 WHERE Id = 3.
I've tried this the following way:
private void MarkAs(User user, UserAddress address, User.AddressType type) {
if (context.Entry(user).State == EntityState.Detached)
context.Users.Attach(user);
// guess I don't really need this:
if (context.Entry(address).State == EntityState.Detached)
context.UserAddresses.Attach(address);
if (type.HasFlag(User.AddressType.DefaultInvoice)) {
user.DefaultInvoiceAddressId = address.Id;
user.DefaultInvoiceAddress = null;
context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
}
if (type.HasFlag(User.AddressType.DefaultDelivery)) {
user.DefaultDeliveryAddressId = address.Id;
user.DefaultDeliveryAddress = null;
context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
}
}
This method is called both when creating new UserAddresses as well as when updating addresses. The create scenario works as expected, however in the update case I receive the following error:
The changes to the database were committed successfully,
but an error occurred while updating the object context.
The ObjectContext might be in an inconsistent state.
Inner exception message: A referential integrity constraint violation occurred:
The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
I call the method with a User object I retrive from the database and the DefaultDeliveryAddress it contains, which I load alongside it via eager loading.
var user = mainDb.User.Get(UnitTestData.Users.Martin.Id, User.Include.DefaultAddresses);
var existingAddress = user.DefaultDeliveryAddress;
mainDb.User.Addresses.SetAs(user, existingAddress, User.AddressType.DefaultInvoice))
// the SetAs method verfies input parameters, calls MarkAs and then SaveChanges
In a nutshell, I just want to make the DefaultDeliveryAddress of a user also his DefaultInvoiceAddress, which would be easily accomplished with the above SQL Update command, but I'm missing something with my EF code.
I've already checked that:
Only the Id is set, the navigation property (DefaultInvoiceAddress) is re-set to null
UserAddress.UserId = User.Id (obviously since it is already assigned to the user)
The user object will become Modified (checked with debugger), since one of its properties is being marked as modified
I also tried clearing both default address navigation properties, but that didn't help either
I suspect this problem is due to the User entity having 2 references to UserAddress, and both foreign keys are set to refer to the same address - how can I get EF to work with that?
Update:
Here are the mappings of the User entity:
// from UserMap.cs:
...
Property(t => t.DefaultInvoiceAddressId).HasColumnName("DefaultInvoiceAddressId");
Property(t => t.DefaultDeliveryAddressId).HasColumnName("DefaultDeliveryAddressId");
// Relationships
HasOptional(t => t.DefaultInvoiceAddress)
.WithMany()
.HasForeignKey(t => t.DefaultInvoiceAddressId);
HasOptional(t => t.DefaultDeliveryAddress)
.WithMany()
.HasForeignKey(t => t.DefaultDeliveryAddressId);
HasMany(t => t.AllAddresses)
.WithRequired()
.HasForeignKey(t => t.UserId)
.WillCascadeOnDelete();
UserAddress has no navigation properties back to User; it only contanis HasMaxLength and HasColumnName settings (I exclude them to keep the question somewhat readable).
Update 2
Here's the executed command from Intellitrace:
The command text "update [TestSchema].[User]
set [DefaultInvoiceAddressId] = #0
where ([Id] = #1)
" was executed on connection "Server=(localdb)\..."
Looks fine to me; seems only EF state manager gets confused by the key mappings.
Figured out the problem: apparently it makes quite the difference when to set navigational properties to null, as EF might otherwise interpret that as an intended change / update (at least that is what I suspect).
The following version of the MarkAs method works:
private void MarkAs(User user, UserAddress address, User.AddressType type) {
if (context.Entry(user).State == EntityState.Detached) {
// clear navigation properties before attaching the entity
user.DefaultInvoiceAddress = null;
user.DefaultDeliveryAddress = null;
context.Users.Attach(user);
}
// address doesn't have to be attached
if (type.HasFlag(User.AddressType.DefaultInvoice)) {
// previously I tried to clear the navigation property here
user.DefaultInvoiceAddressId = address.Id;
context.Entry(user).Property(u => u.DefaultInvoiceAddressId).IsModified = true;
}
if (type.HasFlag(User.AddressType.DefaultDelivery)) {
user.DefaultDeliveryAddressId = address.Id;
context.Entry(user).Property(u => u.DefaultDeliveryAddressId).IsModified = true;
}
}
To sum up my findings for future readers:
If you intend to update entities via Foreign Key properties, clear navigation properties. EF doesn't need them to figure out the update statement.
Clear navigation properties before you attach an entity to a context, otherwise EF might interpret that as a change (in my case the foreign key is nullable, if that isn't the case EF might be smart enough to ignore the navigation property change).
I will not accept my own answer right away to give other (more qualified) readers a chance to answer; if no answers are posted in the next 2 days, I'll accept this one.

Entity framework: Nesting same context says connection already open

I have a piece of code that needs to return me a delimited string on basis of data from a DB, all runs fine except for the line against which comment is mentioned.
I know the fact that single DbContext can not be used for multiple QUERIES at single instance of time.
private string FetchNewEmployees(DateTime addedAfter)
{
StringBuilder newEmployees = new StringBuilder();
using (MyDbContext dbContext = new MyDbContext())
{
var employees = dbContext.Student.Where(p => p.lastupdatedon > addedAfter);
foreach (var employee in employees)
{
newEmployees.Append(string.Format("{0}|{1}|{2}|{3}|{4}{5}",
employee.Name,
employee.DOB,
employee.department.DepartmentName, // This line throws error saying connection already open | commenting this makes it work like charm
employee.EMailAddress,
employee.SchoolName,
System.Environment.NewLine));
}
}
return newEmployees.ToString();
}
The problem id, "department" is another table, hence a foreign key for "employee"...
In case I am unclear, let me know.
Any help right now will be like winning two worlds for me :)
First workaround:
var employees = dbContext.Student.Where(p => p.lastupdatedon > addedAfter).ToList();
...
This closes the connection to the student table but will generate additional queries to lazy load departments.
Another option:
var employees = dbContext.Student.Include( s => s.department ).Where(p => p.lastupdatedon > addedAfter);
...
This causes a single query to be generated which joins both tables.

Fluent-NHibernate many-to-many cascade does not populate the link table

OK, no matter how I define these mappings, my many-to-many mapping does not want to work with cascade insert. I have tried various combination of Cascade() with Reverse() and removing all unnecessary properties just to understand if they had anything to do with this not working, but no lock.
It is really Simple stuff: I have a Message (like an email) which is sent from a user (I have called the entity BasicUser) to a number of users (through property To). User and Message in terms of recipients have a many-to-many relationship but FromUser has one-to-many. FromUser works fine and it is updated alright but my problem is with many-to-many. I even removed FromUser and relationship just to check if this was the problem, but did not help.
So here is the table design (Have removed the relationship from FromUser to BasicUser for simplicity)
And here are the mappings:
public class MessageMap : ClassMap<Message>
{
public MessageMap()
{
Id(x => x.Id).Column("MessageId");
Map(x => x.Subject);
Map(x => x.SentAt);
Map(x => x.Body);
References(x => x.From).Column("FromUser");
HasManyToMany(x => x.To).Table("BasicUserMessage").ChildKeyColumn("BasicUserId")
.ParentKeyColumn("MessageId").Cascade().All();
}
}
public class BasicUserMap : ClassMap<BasicUser>
{
public BasicUserMap()
{
Id(x => x.Id).Column("BasicUserId");
Map(x => x.DisplayName);
Map(x => x.Username);
HasManyToMany(x => x.Messages).Table("BasicUserMessage").ChildKeyColumn("MessageId")
.ParentKeyColumn("BasicUserId").Inverse();
}
}
And I call this and it does not work (table BasicUserMessage does not get populated):
(Note Users with Id 1, 2 and 3 do exist - I also tried getting them from database and then add to list still did not work)
ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
{
Body = "Please note 2",
Subject = "Secret 2",
From = new BasicUser(){Id = 2},
SentAt = DateTime.Now,
};
m.To.Add(new BasicUser(){Id = 1});
m.To.Add(new BasicUser(){Id=3});
session.SaveOrUpdate(m);
session.Close();
The answer about transactions is correct-by-incidental-occurrence. Also correct-by-incidental occurrence is that this is manifesting itself as such because you are using something like an IDENTITY generator that requires a database trip on save to obtain the identity.
Here is what NHibernate does when you set a save-update cascade (or any cascade which implies that) on a many-to-many association with such:
Save the parent entity. This goes to the database immediately because of the identity strategy. The collection is "modified" (because it's new), so let's look at it's members. This step only occurs if inverse is not set on the parent's mapping of the relationship. Create an entry in the link table for each of them.
But wait, some of these entries are transient. Cascades are set properly, so that's okay - but in order to create the link table entry in the session, we need the id of those children, so let's save them immediately.
Now all relevant entities are persistent, and the session has a pending insert for all of the entries in the link table. Flushing the session will issue the commands and create those entries.
When you wrap your code in a transaction, committing the transaction flushes the session, so that is created when you commit. If you use an identity generator that doesn't require a DB round-trip, then the link table entries and the entities are all inserted at the same time, so you won't see the "disconnect" that you're seeing - if the session is never flushed, nothing is ever inserted, and when it is flushed, everything is inserted. If you have neither of these things, flushing your session explicitly will create the link table entries and all will be well.
You made both references inverse. This means to NH: don't store it from this side, because it is already stored by the other side. If both are inverse, nothing is stored.
Remove Inverse from one of the references.
You need to wrap your code in transaction. Otherwise Nhibernate won't save values in joining table
You need to make the BasicUser objects persistent:
ISessionFactory factory = GetSessionFactory();
ISession session = factory.OpenSession();
Message m = new Message()
{
Body = "Please note 2",
Subject = "Secret 2",
From = new BasicUser(){Id = 2},
SentAt = DateTime.Now,
};
var basicUser1 = new BasicUser(){Id = 1};
session.Save(basicUser1);
m.To.Add(basicUser1);
var basicUser3 = new BasicUser(){Id = 3};
session.Save(basicUser3);
m.To.Add(basicUser3);
session.Save(m);
session.Flush();
This should of course be done in a transaction (as Sly answered) and the session should be wrapped in a using statement.

Categories

Resources