How to create an ICriterion in NHibernate that filters based on the content of a collection - nhibernate

I have several ICriterion instances that I combine in various ways (conjunction, disjunction, etc.) based on user input. I'm having trouble creating an ICriterion that is based on matching a value in an associated collection.
For example, given a one-to-many relationship between Orders and OrderItems, I want to be able to create an ICriterion that selects all Orders that have an OrderItem with a Quantity > 100.
I've tried several things and haven't yet found anything that works.

i had a the same problem in a project. you need a pair of alias and criteria for each filtered collection.
KeyValuePair<string, ICriterion[]> collectionfilters = GetFromSomeWhere();
foreach (var association in collectionfilters)
{
criteria.CreateAlias(association.Key, association.Key);
foreach(var crit in association.Value)
{
criteria.Add(crit);
}
}
// example
KeyValuePair<string, ICriterion[]> GetFromSomeWhere()
{
return new KeyValuePair<string, ICriterion[]>("OrderItems", new []{ Restrictions.Gt("OrderItems.Quantity", 100) });
}

Related

CRM365 - What is the easiest way to get list 1:M entity relations

Is there way (plugin to XrmToolBox, code fragment) to get the list of lookup attributes pointing to my entity? I need exactly the same list as in FetchXML Builder plugin, link-entity Relationship 1:M (see the picture) and be able to copy this list.
(My entity is opportunity)
Thanks for any suggestion.
If you just want the list to copy and paste, you can get this in the browser using CRM's WebAPI
/api/data/v9.0/RelationshipDefinitions/Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata?$select=SchemaName&$filter=ReferencedEntity eq 'account'
This query is doing the following:
Retrieves all 1:N relationships from CRM
Limiting the number of attributes returned to only include Schemaname
Filtering the result to only show attributes targeting the account entity
You just open your CRM organization in the browser and paste the above line after your CRM's address
e.g.
https://myorg.crm.dynamics.com/api/data/v9.0/RelationshipDefinitions/Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata?$select=SchemaName&$filter=ReferencedEntity eq 'account'
As Filburt show me the right way, possible C# solution is in the bottom
public List<KeyValuePair<string, string>> GetRelatedEntities (string entityName)
{
List<KeyValuePair<string, string>> retval = new List<KeyValuePair<string, string>>();
RetrieveEntityRequest retrieveBankAccountEntityRequest = new RetrieveEntityRequest
{
EntityFilters = EntityFilters.All,
LogicalName = entityName
};
RetrieveEntityResponse retrieveBankAccountEntityResponse = (RetrieveEntityResponse)service.Execute(retrieveBankAccountEntityRequest);
OneToManyRelationshipMetadata[] relations = retrieveBankAccountEntityResponse.EntityMetadata.OneToManyRelationships;
foreach(OneToManyRelationshipMetadata m in relations)
{
retval.Add(new KeyValuePair<string, string>(m.ReferencingEntity, m.ReferencingAttribute));
}
return retval;
}

Get newly created Podio-PHP item id from PodioItem:Create

I am trying to create new items in four apps apps via one form -- some the items will become app references. For example, I first create an Entity item, then a Person item and relate the new Entity to the Person via an app reference field in the Person App. Then create a Project and relate both the Person and the Entity to the Project. This was successful until I upgraded to the latest Podio-PHP. In the past, when newly created items were returning item_id as integer then I could pass that into the field as an app reference before the next item was created.
I've been trying to get the item_id as integer so I can do the same, but I keep getting null. Anyway, given the way Podio now returns newly created items as objects, should I be trying this a different way?
$app_id = 1234567;
$fields = new PodioItemFieldCollection(array(
...I am defining external_id and values...
));
$item = new PodioItem(array(
'app' => new PodioApp($app_id),
'fields' => $fields,
));
if( $files ){
$item->files = new PodioCollection($files);
};
$item->save();
$new_id = $item->item_id;
return $new_id;
$new_id is where I'm trying to store the integer so I can pass it along to the next item creation as an app reference.
It's not really handled well in podio-php (it's been handled equally un-well since forever though). If you look at the save method you can see what's going on: https://github.com/podio/podio-php/blob/master/models/PodioItem.php#L58-L72
public function save($options = array()) {
$json_attributes = $this->as_json_without_readonly_fields();
if ($this->id) {
return self::update($this->id, $json_attributes, $options);
}
else {
if ($this->app && $this->app->id) {
return self::create($this->app->id, $json_attributes, $options);
}
else {
throw new PodioMissingRelationshipError('{"error_description":"Item is missing relationship to app"}', null, null);
}
}
}
save() doesn't assign the new item_id to the object itself. This is a bug and it would be good if you created an issue at https://github.com/podio/podio-php/issues so it can be fixed.
For now you can see that save() returns the same as the static create method. So your last three lines needs to be replaced with:
$new_item_placeholder = $item->save();
$item->item_id = $new_item_placeholder->item_id;
return $item->item_id;

Adding key value to RavenDB document based on another document's values (For all documents in large collection)

I am new to RavenDB and could really use some help.
I have a collection of ~20M documents, and I need to add a key to each document. The challenge is that the value of the key needs to be derived from another document.
For instance, given the following document:
{
"Name" : "001A"
"Date" : "09-09-2013T00:00:00.0000000"
"Related" : [
"002B",
"003B"
]
}
The goal is to add a key that holds the dates for the related documents, i.e. 002B and 003B, by looking up the related documents in the collection and returning their date. E.g.:
{
"Name" : "001A"
"Date" : "09-09-2013T00:00:00.0000000"
"Related" : [
"002B",
"003B"
]
"RelatedDates" : [
"08-10-2013T00:00:00.0000000",
"08-15-2013T00:00:00.0000000"
]
}
I realize that I'm trying to treat the collection somewhat like a relational database, but this is the form that my data is in to begin with. I would prefer not to put everything into a relational dataset first in order to structure the data for RavenDB.
I first tried doing this on the client side, by paging through the collection and updating the records. However, I quickly reach the maximum number of request for the session.
I then tried patching on the server side with JavaScript, but I'm not sure if this is possible.
At this point I would greatly appreciate some strategic guidance on the right way to approach this problem, as well as, more tactical guidance on how to implement it.
The recommended way of doing this is via a Console application that loops thru all your records, similar to what you have already done but in a way that pages the data so you dont hit the maximum number of requests per session.
See this example from the ravendb source code example application:
you need to do something like this:
using (var store = new DocumentStore { ConnectionStringName = "RavenDB" }.Initialize())
{
int start = 0;
while (true)
{
using (var session = store.OpenSession())
{
var posts = session.Query<Post>()
.OrderBy(x => x.CreatedAt)
.Include(x => x.CommentsId)
.Skip(start)
.Take(128)
.ToList();
if (posts.Count == 0)
break;
foreach (var post in posts)
{
session.Load<PostComments>(post.CommentsId).Post = new PostComments.PostReference
{
Id = post.Id,
PublishAt = post.PublishAt
};
}
session.SaveChanges();
start += posts.Count;
Console.WriteLine("Migrated {0}", start);
}
}
}
I've done this sort of thing with about ~1.5M records and it wasnt exactly quick to do the migration. If your records are small then you can just Load<> and SaveChanges on each one as from experience programmatically patching the documents did not speed things up materially
As a side note, the ravendb google groups is very active if you want to ask specifically about doing this from the studio

How to Create ViewModel with multiple related tables and Save Form

I am trying to figure out the best way to accomplish this given the modern versions. I have am using VS2012 MVC4 EF5 and have built a edmx file from my database. I built a form that will allow submission of vendor information. The main table is Vendor table that contains mainly contact information and there are additional tables that store their multiple category choices (checkbox list) and another that stores their minority info (collection of radio buttons). So my ViewModel is the vendor table and I populate the checkboxes and radio buttons with view bags that query the lookup tables for their values.
So I assume I should either build the categories and minority parts into the ViewModel and somehow wire up the magic so that the database knows how to save the returned values or should I just use viewbags and then somehow on post read those values and loop through them to store them to the database? Either way I am stuck and don't know how to do this.
I have serached numerous examples online but none of them fit this situation. The is not a complex data model but should be rather common real world situation. I am new to MVC so forgive me if I am missing something obvious.
Any guidance is appreciated.
UPDATE: Here is the baseic code to save the ViewModel to the db but how do you save the checkbox list and radio buttons. I think there are two approaches 1) to somehow include them in the ViewModel or 2) perform a separate function to save the form checkbox and radio button values.
[HttpPost]
public ActionResult Form(VendorProfile newProfile)
{
if (ModelState.IsValid)
{
newProfile.ProfileID = Guid.NewGuid();
newProfile.DateCreated = DateTime.Now;
_db.VendorProfiles.Add(newProfile);
_db.SaveChanges();
return RedirectToAction("ThankYou", "Home");
}
else
{
PopuplateViewBags();
return View(newProfile);
}
}
Perhaps another way of stating my problem is what if you had to build an form to where people would sign up and select all their favorite flavors of ice cream from a list of 31 flavors. You need to save the person's contact information in the primary table and then save a collection of their flavor choices in another table (one-to-many). I have a ViewModel for the contact form and a list of flavors (checkbox list) displayed from a lookup table. How do you write code to save this form?
SOLUTION: There might be a better way, but wanted to post what I discovered. You can pass in the collection of checkboxes and then send them to another method that handles the db inserts.
[HttpPost]
public ActionResult Form(VendorProfile newProfile, int[] categories)
{
if (ModelState.IsValid)
{
newProfile.ProfileID = Guid.NewGuid();
newProfile.DateCreated = DateTime.Now;
_db.VendorProfiles.Add(newProfile);
_db.SaveChanges();
InsertVendorCategories(newProfile.ProfileID, categories);
return RedirectToAction("ThankYou", "Home");
}
else
{
PopuplateViewBags();
return View(newProfile);
}
}
private void InsertVendorCategories(Guid ProfileID, int[] categories)
{
try
{
var PID = new SqlParameter("#ProfileID", ProfileID);
var CID = new SqlParameter("#CatID", "");
foreach (int c in categories)
{
CID = new SqlParameter("#CatID", c);
_db.Database.ExecuteSqlCommand("Exec InsertVendorCategory #ProfileID, #CatID", PID, CID);
}
}
catch { Exception ex; }
}

Caching not working for associated entities unless lazy loading specified in mapping file

I am having a problem getting associated entities to be cached unless
they are specified (Not.LazyLoad() in mapping). If I build up a query
in Criteria or Linq the Main Entity is cached, but not associated
entities.
IList<NewsItem> news;
using (var session = factory.OpenSession())
{
Console.WriteLine("First Query");
news = session.CreateCriteria(typeof(NewsItem))
.SetCacheable(true)
.SetFetchMode("Author", FetchMode.Eager) // associated entity eager loaded
.List<NewsItem>();
}
foreach (var item in news)
{
Console.WriteLine("Author: " + item.Author.Name); //works fine first time
}
using (var session = factory.OpenSession())
{
Console.WriteLine("");
Console.WriteLine("Second Query");
news = session.CreateCriteria(typeof(NewsItem))
.SetCacheable(true)
.SetFetchMode("Author", FetchMode.Eager)
.List<NewsItem>();
}
foreach (var item in news)
{
Console.WriteLine("Author: " + item.Author.Name); //NHibernate.LazyInitializationException
}
I want to avoid having to eager load the associations via the mapping
files. Anyone else having a similar problem.
Any feedback appreciated.
What query caching (SetCacheable(true)) does is store the IDs retrieved by the query; it then hydrates the objects one by one (hopefully, from the entity cache).
IIRC, when caching a query, FetchModes for related entites are not used, so the Author references are not being initialized.
It's worth noting that you shouldn't be using your entities outside a session, but here's a workaround:
news = session.CreateCriteria(typeof(NewsItem))
.SetCacheable(true)
.SetFetchMode("Author", FetchMode.Eager)
.List<NewsItem>();
foreach (var item in news)
{
var name = item.Author.Name;
}
This will force initialization of the proxies inside the sessions; that way you'll be able to use them afterwards.
Of course, you need to make sure both NewsItem and Author are cached, otherwise you'll actually decrease the performance by caching the query.