How does one make Zend Db cascade delete multiple levels of the hierarchy? For example:
dealers -> products -> attributes
deleting one dealer should go all the way down to attributes, and now it doesn't :(
Any thoughts?
On row of the Zend_Table_Abstract within the function _cascadeDelete a row is constructed like this:
$rowsAffected += $this->delete($where);
It should instead be constructed as something like this:
$toDelete = $this->fetchAll($where);
foreach($toDelete as $row) {
$rowsAffected += $row->delete();
}
More info here.
It's worked for me in one cause but need to test more.
Related
I've been adding records to a dynamic module via the API and in the process during my experimentation I added a bunch of records that weren't associated correctly with any valid parent record.
I've checked and so far I can see that Sitefinity stores data about these records in a number of tables:
mydynamiccontenttype_table
sf_dynamic_content
sf_dynmc_cntnt_sf_lnguage_data
sf_dynmc_cntent_sf_permissions
I would like to clean up the database by deleting these records but I want to make sure I don't create more problems in the process.
Anyone know if there are more references to these dynamic content type records or a process to safely delete them?
There are probably other tables, so your safest option would be to delete the items using the Sitefinity API.
Just get the masterId of the item and use a code like this:
public static void DeleteDataItemOfType(this DynamicModuleManager manager, string type, Guid Id)
{
Type resolvedType = TypeResolutionService.ResolveType(type);
using (var region = new ElevatedModeRegion(manager))
{
manager.DeleteDataItem(resolvedType, Id);
manager.SaveChanges();
}
}
I will use the standard example of Invoice and invoiceLineItems. So I have a single repository for the invoices because the invoice is the aggregate root. Creating a whole invoice and deleting an invoice is pretty easy. What about updating an invoice?
Maybe I shouldn't be thinking so "low level" but I can't imagine a nice way to handle this. Here is my pseudo code for this operation
public void Update(Invoice inv)
{
var dbVersion = GetInvoice(id);
foreach(var lineItem in inv.LineItems)
{
if (lineItem not in dbVersion)
{
InsertLineItem(lineItem);
}
else
{
UpdateLineItem(lineItem);
}
}
foreach(var lineItem in dbVersion)
{
if (lineItem not in inv)
{
DeleteLineItem(lineItem);
}
}
}
I am imagining the queries this thing needs to produce and it seems very inefficient.
Select statements to re materialize the whole invoice.
Update statement for the whole invoice (regardless if anything on the invoice changed)
Insert statement for each line item that is new
Update statement for each line item that is already there (regardless if anything changed)
Delete statement for each line item that is missing
Of course you wouldn't have to generate an update statement for the whole invoice and for the InvoiceLineItem but that would require you to check all the properties in each.
If you however went the IvoiceLineItemRepo you would only update when a user performed an action that would necessitate the update. So if the user updated just a line then there would only be a single update and for that line item and you wouldn't have to check for changes you could assume that it had changed.
Is there a nicer way of handling the updates?
You can first delete LineItem collection and then insert new collection like all-delete-orphan in hibernate.
deleteAllLineItemsByInvoiceId(Long id);
insertAllLineItems(List<LineItem> lineItems);
Producing software for customers, mostly using MS SQL but some Oracle, a decision was made to plunge into Nhibernate (and C#).
The task is to delete efficiently e.g. 10 000 rows from 100 000 and still stay sticked to ORM.
I've tried named queries - link already,
IQuery sql = s.GetNamedQuery("native-delete-car").SetString(0, "Kirsten");
sql.ExecuteUpdate();
but the best I have ever found seems to be:
using (ITransaction tx = _session.BeginTransaction())
{
try
{
string cmd = "delete from Customer where Id < GetSomeId()";
var count = _session.CreateSQLQuery(cmd).ExecuteUpdate();
...
Since it may not get into dB to get all complete rows before deleting them.
My questions are:
If there is a better way for this kind of delete.
If there is a possibility to get the Where condition for Delete like this:
Having a select statement (using LinQ to NHibernate) => which will generate appropriate SQL for DB => we get that Where condition and use it for Delete.
If there is a better way for this kind of delete.
Yes, you could use HQL instead of SQL.
If there is a possibility to get the Where condition for Delete [using Expressions]:
No, AFAIK that's not implemented. Since NHibernate is an open source project, I encourage you to find out if anyone has proposed this, and/or discuss it on the mailing list.
Thanks for your quick reply. Now I've probably got the difference.
session.CreateSQLQuery(cmd).ExecuteUpdate();
must have cmd with Delete From DbTable. On the contrary the HQL way
session.CreateQuery(cmd).ExecuteUpdate();
needs cmd with Delete From MappedCollectionOfObjects.
In that case it possibly solves my other question as well.
There now is a better way with NHibernate 5.0:
var biggestId = GetSomeId();
session.Query<Customer>()
.Where(c => c.Id < biggestId)
.Delete();
Documentation:
//
// Summary:
// Delete all entities selected by the specified query. The delete operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to delete.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of deleted entities.
public static int Delete<TSource>(this IQueryable<TSource> source);
Starting with a List of entities and needing all dependent entities through an association, is there a way to use the corresponding navigation-propertiy to load all child-entities with one db-round-trip? Ie. generate a single WHERE fkId IN (...) statement via navigation property?
More details
I've found these ways to load the children:
Keep the set of parent-entities as IQueriable<T>
Not good since the db will have to find the main set every time and join to get the requested data.
Put the parent-objects into an array or list, then get related data through navigation properties.
var children = parentArray.Select(p => p.Children).Distinct()
This is slow since it will generate a select for every main-entity.
Creates duplicate objects since each set of children is created independetly.
Put the foreign keys from the main entities into an array then filter the entire dependent-ObjectSet
var foreignKeyIds = parentArray.Select(p => p.Id).ToArray();
var children = Children.Where(d => foreignKeyIds.Contains(d.Id))
Linq then generates the desired "WHERE foreignKeyId IN (...)"-clause.
This is fast but only possible for 1:*-relations since linking-tables are mapped away.
Removes the readablity advantage of EF by using Ids after all
The navigation-properties of type EntityCollection<T> are not populated
Eager loading though the .Include()-methods, included for completeness (asking for lazy-loading)
Alledgedly joins everything included together and returns one giant flat result.
Have to decide up front which data to use
It there some way to get the simplicity of 2 with the performance of 3?
You could attach the parent object to your context and get the children when needed.
foreach (T parent in parents) {
_context.Attach(parent);
}
var children = parents.Select(p => p.Children);
Edit: for attaching multiple, just iterate.
I think finding a good answer is not possible or at least not worth the trouble. Instead a micro ORM like Dapper give the big benefit of removing the need to map between sql-columns and object-properties and does it without the need to create a model first. Also one simply writes the desired sql instead of understanding what linq to write to have it generated. IQueryable<T> will be missed though.
I think I know what the answer to this question is probably going to be, but I thought I'd go ahead and ask it anyway.
It appears that within NHibernate if I do something like this:
IList<Customer> customers = Session.CreateCriteria(typeof(Customer))
.Add(Restrictions.Eq("Name", "Steve")
.List<Customer>();
And I want to then delete that list of customers. From what I can tell the only way to do it is like this:
foreach(var customer in customers)
{
Session.Delete(customer);
}
But what I'm wondering is if there's any way I can just go:
Session.Delete(customers);
And delete the entire collection with a single call?
Not with Criteria, but it's easy to do with HQL:
session.CreateQuery("delete Customer customer where customer in (:customers)")
.SetParameterList("customers", customers.ToArray())
.ExecuteUpdate();
But you don't need to load them. You can also do it in one shot:
session.CreateQuery("delete Customer where Name = 'Steve'")
.ExecuteUpdate();