NHibernate 3.x deletes child entities when combining LINQ paging, many-to-many, and subselect fetch - linq

Our application has the concept of Stories and the concept of Tags. A story can have many tags applied to it, and a tag can be applied to many stories, making the relationship many-to-many. The two tables, Stories and Tags are bridged with a third, StoriesToTags.
The relevant pieces of the mapping files are as follows:
Here's the mapping from Story to Tag:
<class name="Story" table="Stories">
...
<set fetch="subselect" name="Tags" table="StoriesToTags">
<key>
<column name="StoryId" />
</key>
<many-to-many class="Tag">
<column name="TagId" />
</many-to-many>
</set>
</class>
And the inverse relationship from Tag to Story:
<class name="Tag" table="Tags">
...
<set fetch="subselect" inverse="true" name="Stories" table="StoriesToTags">
<key>
<column name="TagId" />
</key>
<many-to-many class="Story">
<column name="StoryId" />
</many-to-many>
</set>
</class>
As you can see, we're using the subselect fetch strategy to avoid the N+1 query problem. Everything works great, until you attempt to page a result using LINQ:
IQueryable<Story> stories = GetStories(...).TakePage(pageNumber, pageSize);
After running this query, NHibernate deletes the relationships (records in StoriesToTags) for all stories that were not loaded in the query. It only seems to occur when the tags are specifically loaded (that is, the subselect is triggered). The relationships are not deleted if we switch to a join or select fetch strategy, but that causes N+1 queries to be executed.
My best guess is that NHibernate thinks the tags have been orphaned, but we haven't set any cascades on the collections. Also, as far as I can tell, setting a cascade has no effect.
This process worked great under NHibernate 2.x and NHibernate.Linq. We didn't see the issue with deletion occur until we moved to NHibernate 3.x, which has LINQ support built-in. I'm not sure it makes a difference, but for what it's worth, we're using SQL Server with identity keys.
Any thoughts? I initially thought I was doing something insanely stupid, but I've tried basically every permutation of mapping and we can't seem to eliminate the issue.
Edit: Another interesting piece of information. If you call session.IsDirty() before closing the session, the issue doesn't occur. I suspect that this is because collection changes aren't persisted between flushes, but I can't decipher NHibernate's source well enough to know for certain.

have you set up in mapping of the entity : Cascade.None() this will stop deleting anything else except the entity.
This might help: http://ayende.com/blog/1890/nhibernate-cascades-the-different-between-all-all-delete-orphans-and-save-update

Can you give us some clue at what you are trying to achieve here ? I never experimented with a specified fetch on a many-to-many, but me thinks it got something to do with some sort of explicit cascade= all for a many to many.

Related

Using both basic AND association mapping for one field with Doctrine?

I have a Variable object which may belong to a group
I'm mapping the Variable field containing the FK to the Group's id and his relation association in my domain object. (using XMl).
In other words, my domain object has both a (string) field with the id and a "relation" field with the object.
The intended goal here is to retrieve a ready Group object when querying, while simply setting an id when writing
So when I create/update a Variable, I set the ID (not the relation Object).
My issue is : the value is (always) properly saved when I UPDATE, but (always) lost on INSERT.
Here's the part of my mapping :
<field name="parentId" type="string" length="36" column="parent" nullable="true"/>
<many-to-one field="parent" target-entity="App\Domain\VariablesGroup\VariablesGroup" fetch="EAGER">
<join-column name="parent" referenced-column-name="id" nullable="true"/>
</many-to-one>
Docs didn't help me much here, since those 2 mappings are described separetely, and might not be designed to be mixed this way.
--
But I'd prefer to avoid querying the Group from DB and populate the Object field when not necessary.
Since I didn't find how Doctrine handles this under the hood, I guess it might not be something supported :(
Technical precisions about this mappings-interraction (or should I say conflict ?) would be welcomed.
Well, some more testing, using inconsistent data entries in my entity seem to clearly demonstrate that such usage isn't intended.
Whild my code wouldn't allow this, if I do save my entity with groupId = 1 and Group = (group Object with ID 2), I get an alternance of values for the saved value in the "parent" column of my database.
I guess this is due to Doctrine skipping the "useless" update, and proceeding to the one which effectively changes the data value.
Since both values are different, the skipped update alternantes.
If my assumptions are right, Doctrine would better pop an error in such a mapping case. (as it does if you map one column to 2 fields).
Maybe I'll file a suggestion/issue, if this seem relevant.

Retrieve the fetchxml in grid via javascript

This might seem a bit of an odd request so I'll try to offer some background. I have a feature on my CRM which requires that user should be able to filter a view and then save the resulting records such that a separate process can pick them up and process them periodically e.g. daily.
Now here's the catch, they want this process to requery the data before it processes it, so basically what should be saved is the query or filters rather than the data in the view.
Having previously written some javascript code which dynamically sets the fetchxml on a subgrid like so
Subgrid.getGrid().setParameter("fetchXml", fetchxml);
I though it should be fairly straight forward to potentially retrieve the fetchxml in the grid
Subgrid.getGrid().getParameter("fetchXml");
However that doesn't work and I can't seem to find any documentation or anything that can point me in the right direction. I have used Developer tools to inspect the properties of Mscrm.XrmControlGridWrapper but I can't find anything useful..
If anyone knows how I can retrieve the fetchxml that powers a subgrid using javascript, it would be massively helpful?
EDIT
I have just found that I can do this
Subgrid.getGrid().getFilter().$3_1.GetParameter("fetchXml")
and that returns exactly what I want, however this just screams of hacky and unsupported.
$3_1 has a type of [object (Mscrm.TurboGridControl)]
Is there a way I can access this object in a supported way?
A few thoughts on this:
You can retrieve the SystemForm record, then parse the FormXml to get the ViewId. Then you can retrieve the view from the SavedQuery entity, and get the FetchXML. Here's an example of the ViewId in the FormXml:
You could add a boolean field to the entity and when the user saves the set they want to process you can flag those records for the later batch process to retrieve.
When the user identifies the set they want to process you could temporarily create a view (SystemQuery or UserQuery) with the the FetchXML using the "in" operator with the list of Guid's to identify the exact records to process. After using the view to retrieve and process the records, the batch process could delete the view. I would probably be comfortable using this approach up to a few dozen records.
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
<entity name="account">
<filter type="and">
<condition attribute="accountid" operator="in">
<value>{A1CC84F2-BE0D-E711-8104-00155D6FD705}</value>
<value>{A3CC84F2-BE0D-E711-8104-00155D6FD705}</value>
<value>{A5CC84F2-BE0D-E711-8104-00155D6FD705}</value>
</condition>
</filter>
</entity>
</fetch>
If you want to avoid changing the Modified information by setting a boolean flag on the records to be processed, you could create an N:N linking entity and associate the selected records to an instance of that entity.
You can do:
Since you have the fetchxml that previously set as filter for SubGrid, why don't you store this variable directly. You can save this variable in localStorage or maybe even in a dummy field created for this purpose. And use it in the desired process.
Btw, the supported way of getting fetchXml: Xrm.Page.getControl(gridControlName).getFetchXml()
Hope this helps...

How can i add sample data to an edmx file and display it in Visual-Studio

I am using Visual Studio 2010 and ADO.Net Entity Data Model to create my database schema. Since i am still drafting and discussing the schema i would like to be able to add example data to the edmx-file and be able to show it in the designer like in the following screenshot:
Source code of EDMX file with added sample data
The underlying xml-code would contain the normal edmx-output and on top the sample data like below:
<?xml version="1.0" encoding="utf-8"?>
....
<EntityType Name="Title">
<Documentation>
<Summary>Table titles contains all the dvd-title we have in our store</Summary>
</Documentation>
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32"
Name="Id" Nullable="false"
annotation:StoreGeneratedPattern="Identity" />
<Property Type="String" Name="Title" Nullable="false" >
<Documentation>
<Summary>The title of the movie</Summary>
<LongDescription>
The translated title of the movie and the original title
</LongDescription>
</Documentation>
</Property>
</EntityType>
<Titles>
<Title>
<id>810</id>
<title>Pulp Fiction</title>
<year>1994</year>
</Title>
<Title>
<id>940</id>
<title>Lock, Stock and Two Smoking Barrels</title>
<year>1998</year>
</Title>
</Titles>
The sample data should be displayed and should be editable in design view.
Does anyone know a tool or a way to combine the edmx-file and to display and add sample data?
Bonus Question: When i generate the sql-statement the documentation is not added to the sql statement. Is there a way to include the documentation of the fields and the tables so that they would be added to Microsoft SQL Server?
I don't think it is possible. One of the reasons is that what you see is a model and not the database schema. What you don't see is how entity is mapped to the database. In the basic scenario there is 1:1 mapping indeed but once you start customizing your model you may end up having columns in the database with names different from the property names in the model, you may have complex properties you would not even have corresponding properties for on the diagram, you may put multiple entities to one table or one entity to multiple tables. How are you going to show this? And since it is just entity model and not table model data does not seem to be a good fit. I don't know of any tool that could do what you need. Edmx is an Xml file and the designer itself is extensible and based on DSL. You can try cooking something yourself but it will be a lot of work.

NHibernate query mapped collection

In my ASP.NET web-application I use NHibernate to persist my "User"-Instances, where each of them has a "Entries" - collection. It is a typical one-to-many mapping and it works just fine. The mapping-code for the entries looks like this:
<bag name="Entries" cascade="all-delete-orphan">
<key column="UserID" />
<one-to-many class="MyApp.Entities.Entry, MyApp.Entities" />
</bag>
Now I have a page, where I want to display a grid with all the entries of the logged-in user. To do so, I could simply bind the "Entries" property of the current user to the Grids "DataSource" - property. This also works just fine, but this also means, that the grids built-in paging-functionality (Telerik RadGrid) doesn't have any effect on database-performance, because all the entries will be loaded each time when displaying the grid.
Therefore I could apply my custom-paging, where I only fetch the rows which I need to display the grids current page. A typical Linq2NHibernate query looks like this:
var query = from entry in Session.Linq<Entry>()
where entry.User == currentUser
select entry;
query.Skip(pageNum * pageSize).Take(pageSize).ToList();
Using this approach I need to extend my repository altough NHibernate has already done the mapping between User and Entry...
My question is: If I use LINQ to directly query the "Entries"-collection of my "User"-object - does this mean, that all the Entries will be loaded from the database and then filtered in memory or would this be translated to a real "database"-query, so that I could use this much more comfortable approach to implement paging?
Example:
myGrid.DataSource = currentUser.Entries.Skip(pageNum * pageSize).Take(pageSize).ToList();
J4I: Of course I use lazy-loading in the mapping files...
Thank you in advance!
LINQ on the collection will always be LINQ-to-objects, as they don't implement IQueryable, so you'd be loading everything in memory.
A query is the only possible approach at this moment.

nhibernate query with child entities and lazy="false"

I have a problem with seperate sql queries being generated for every item in a child collection when selecting the Parent.
I have a Parent object with an IList collection of Child objects.
If I run the following code using linq to nhibernate:
IList parents = _repository.ToList();
I get the sql statements like the following:
SELECT * FROM Parent
SELECT * FROM Child WHERE ParentId = 1
SELECT * FROM Child WHERE ParentId = 2
SELECT * FROM Child WHERE ParentId = 3
SELECT * FROM Child WHERE ParentId = 4
etc etc
This is obviously extremely inefficient and assume it must be something to do with the mapping files?
Below is the mapping for the Child collection:
<bag name="Children" lazy="false" table="Child" cascade="all-delete-orphan">
<key column="ParentId"/>
<one-to-many class="Child" />
</bag>
Trust this is a newbie mistake somewhere.
Any help greatly appreciate.
S
It all depends on what _repository.ToList() is doing under the covers. You can force "eager" fetching of your collections via HQL syntax similar to the following:
"from Parent inner join fetch Children"
HQL statements are meant to be flexible, and so they tend to ignore your collection mapping and join strategies defined either fluently or in the hbm xml files.
You should now see only a single query executed against the database.
Look at this Linq for NHibernate and fetch mode of eager loading. You can manage your fetch strategy in the mapping file if your subcollection always needs to be in context. However, NH best practice recommends against this. Instead we always keep collections lazy, and set fetching strategies on the queries where appropriate.

Resources