Building Many-to-many query with EF - sql-server

I'm using EF5 with .net 4.0 and I have 3 entities - User, Project and Interest. I connected User Many2Many Interest and Project Many2Many Interest using FluentAPI (it created additional 2 tables for each relation). Everything works great.
What I want to do is to load all projects per user who has same interests. I tried
Project ...
.Where(p => p.Interests
.Any(t => user.Interests.All(i => i.Url == t.Url)));
(Interest has PK Url). When I perform this query, I get error
Unable to create a constant value of type 'DAL.Models.Interest'. Only primitive types or enumeration types are supported in this context.
What is the right query?

The problem is you can't translate user.Interests.All(...) into a SQL procedure.
You can however, create a list of the primitive type (Url) and then compare against that.
var userInterests = user.Interests.Select(u => u.Url);
var sharedProjects = m.Projects.Where( p => p.Interests.Select(i => i.Url)
.Any(pi => userInterests.Any(ui => ui == pi)));

Related

EF Core 6 HasDefaultValueSql with multiple sequences

I'm currently re-writing an old service, and ran into an issue.
In the SQL Server we have a sequence for each clients product number. The old version used stored procedures for creating products. But I was looking into if it was possible to do something with HasDefaultValueSql.
The MS docs said to use .HasDefaultValueSql("NEXT VALUE FOR sequence"), but is there some way to differentiate which sequence to use based on the client_id column?
My product entity type configuration:
builder.ToTable("products");
builder.HasKey(e => e.Id);
builder.Property(e => e.Id).HasColumnName("product_id")
.ValueGeneratedOnAdd();
builder.Property(e => e.ClientId).HasColumnName("client_id");
builder.Property(e => e.Number).HasColumnName("number")
.HasDefaultValueSql("NEXT VALUE FOR client_idProductNumberSequence");
builder.Property(e => e.Name).HasColumnName("name");
builder.Property(e => e.Description).HasColumnName("description");
...other props
builder.HasIndex(e => new { e.ClientId, e.Number })
.IsUnique(true);
If thats not possible, what would be the best way to solve this?

Cakephp 3 - How to integrate external sources in table?

I working on an application that has its own database and gets user information from another serivce (an LDAP is this case, through an API package).
Say I have a tables called Articles, with a column user_id. There is no Users table, instead a user or set of users is retrieved through the external API:
$user = LDAPConnector::getUser($user_id);
$users = LDAPConnector::getUsers([1, 2, 5, 6]);
Of course I want retrieving data from inside a controller to be as simple as possible, ideally still with something like:
$articles = $this->Articles->find()->contain('Users');
foreach ($articles as $article) {
echo $article->user->getFullname();
}
I'm not sure how to approach this.
Where should I place the code in the table object to allow integration with the external API?
And as a bonus question: How to minimise the number of LDAP queries when filling the Entities?
i.e. it seems to be a lot faster by first retrieving the relevant users with a single ->getUsers() and placing them later, even though iterating over the articles and using multiple ->getUser() might be simpler.
The most simple solution would be to use a result formatter to fetch and inject the external data.
The more sophisticated solution would a custom association, and a custom association loader, but given how database-centric associations are, you'd probably also have to come up with a table and possibly a query implementation that handles your LDAP datasource. While it would be rather simple to move this into a custom association, containing the association will look up a matching table, cause the schema to be inspected, etc.
So I'll stick with providing an example for the first option. A result formatter would be pretty simple, something like this:
$this->Articles
->find()
->formatResults(function (\Cake\Collection\CollectionInterface $results) {
$userIds = array_unique($results->extract('user_id')->toArray());
$users = LDAPConnector::getUsers($userIds);
$usersMap = collection($users)->indexBy('id')->toArray();
return $results
->map(function ($article) use ($usersMap) {
if (isset($usersMap[$article['user_id']])) {
$article['user'] = $usersMap[$article['user_id']];
}
return $article;
});
});
The example makes the assumption that the data returned from LDAPConnector::getUsers() is a collection of associative arrays, with an id key that matches the user id. You'd have to adapt this accordingly, depending on what exactly LDAPConnector::getUsers() returns.
That aside, the example should be rather self-explanatory, first obtain a unique list of users IDs found in the queried articles, obtain the LDAP users using those IDs, then inject the users into the articles.
If you wanted to have entities in your results, then create entities from the user data, for example like this:
$userData = $usersMap[$article['user_id']];
$article['user'] = new \App\Model\Entity\User($userData);
For better reusability, put the formatter in a custom finder. In your ArticlesTable class:
public function findWithUsers(\Cake\ORM\Query $query, array $options)
{
return $query->formatResults(/* ... */);
}
Then you can just do $this->Articles->find('withUsers'), just as simple as containing.
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Database Access & ORM > Retrieving Data & Results Sets > Custom Finder Methods

How do I build this complex database query in CakePHP 3.0?

This is my database design, or at least the tables that are relevant to this question.
I want to build a query that returns a single page (find will be based on the path attribute), with the associated container, with its associated child-containers in threaded form and all of those containers should individually have their associated blocks with them (preferably in the right order sorted by the index column from the blocks_pages table).
Can anybody give me a clue how to wrap that all up with the query-builder? Or if that is not possible, then is it possible to do it using the new map/reduce feature, since the after-find function has been removed?
In case it helps, this will be the visualized result, if you just ignore the magenta Article-box for a moment.
Try this
$pagesTable
->find()
->where(['path' => $myPath])
->contain([
'Containers.ChildContainers' => function($q) {
return $q->formatResults(function($results) {
return $results->map(function($container) {
$container->nested = $container->source()
->find('children', ['for' => $container->id])
->find('threaded')
->contain(['Blocks']);
return $container;
});
});
},
'Containers.ChildContainers.Blocks'
])

Drupal 7 programmatically submit form

$form2_id = 'commerce_product_ui_product_form';
$form2_state['values'] = array(
'sku' => 'xyz100',
'title' => 'xyz',
'commerce_price' => '355',
'op' => t('Save Product')
);
drupal_form_submit($form2_id, $form2_state);
$form_errors = form_get_errors();
drupal_set_message('Form errors = '.$form_errors);
I get no errors but lots of warnings... and the data is not saved to the db.
call_user_func_array() expects parameter 1 to be a valid callback, function 'commerce_product_product_form' not found or invalid function name in drupal_retrieve_form()
You apparently are trying to programmatically submit values to a Drupal commerce generated form. This an unpractical approach, because of the modular achitecture of Drupal commerce: there are quite a few steps that populate a form before it is submitted, and even if you had (as you should have, anyway) prepoulate $form_state with drupal_get_form(), you would end up with errors in the submit function. I tried myself to fix your code, to no avail.
Fortunately, there is another approach, leveraging Drupal's entities, for which I must credit this post. You can create an entity metadata wrapper with a Drupal commerce's product object of your chosen type:
$wrapper = entity_metadata_wrapper('commerce_product',
commerce_product_new('[PRODUCT_TYPE_MACHINE_NAME]'));
By calling entity_metadata_wrapper this way, you create a property wrapper by which you can access a commerce_product entity; commerce_product_new('[PRODUCT_TYPE_MACHINE_NAME]') creates the entity instance with it's required defaults. Then you can do:
$wrapper->sku = 'xyz100';
$wrapper->title = 'xyz';
$wrapper->commerce_price->amount = 355;
$wrapper->commerce_price->currency_code = 'USD';
Be aware that commerce_price is a structured type, and amount and currency are required. amount must be in hundredths of the unit, so a 1.5$ price must be expressed as 150.
When your entity is fully populated with any other property, you need to issue
$wrapper->save();
When I first read your question I thought "this should be easy"... It wasn't and I spent a few hours to figure it out. It was worth the while, though, because I have found a much better solution to deal with entities (and nodes...) in Drupal.

MVC 3 LINQ Custom Sorting and Filtering with User-Specified Fields (Properties)

I'm writing a custom web app (an administrative utility) that queries a SQL Server database table, and I am giving users the ability to apply their own (limited) custom sorts and filters on the returned information. The sending page allows them to choose up to 3 sort criteria (e.g. Sort 1 then Sort 2 then Sort 3) using drop-down lists on an HTML form. They must also indicate a single letter of the alphabet (through the URL), and the application must return a list of data where field "Sort1" starts with the letter (the filtering is ALWAYS by the Sort1 field).
So for example, they could choose to return a list of all customers whose City starts with the letter "R", sorted by City then State then Name. Or, they could return all customers whose Name starts with "F", sorted by Name then Address then Customer ID.
I totally understand how to do this with fixed (known) fields/properties;
var _data = _data.Where(d => d.Name.StartsWith(letter)).OrderBy(p => p.Name).ThenBy(p => p.Address).ThenBy(p => p.CustomerID);
etc. But in my case, the table properties (fields) to be sorted/filtered are not explicitly known; they are only available to my app as strings. What I'd like to be able to do is...
var _data = _data.Where(d => d.["Sort1"].StartsWith(letter)).OrderBy(p => p.["Sort1"]).ThenBy(p => p.["Sort2"]).ThenBy(p => p.["Sort3"]);
where Sort1, Sort2 and Sort3 are posted form field values, but I know this doesn't work. How can I implement this? I'm using ASP.Net MVC 3 in C#, with LINQ using the Entity Framework (EDM).
What about this approach:
Func<Record, object> sort1 = r => GetProperty(r, "City");
Func<Record, object> sort2 = r => GetProperty(r, "State");
Func<Record, object> sort3 = r => GetProperty(r, "Address");
Func<Record, bool> filterPredicate = p => GetProperty(p, "City").ToString().StartsWith("A");
IEnumerable<Record> enumerable = list.Where(filterPredicate)
.OrderBy(sort1)
.ThenBy(sort2).
.ThenBy(sort3);
Where GetProperty is implemented as:
static object GetProperty(Record record, string paramName)
{
if (paramName == "City") return record.City;
if (paramName == "State") return record.State;
if (paramName == "Address") return record.Address;
if (paramName == "CustomerId") return record.CustomerId;
throw new InvalidEnumArgumentException();
}
You should make your query dynamic, and when make dynamic query you should use (Exec ) command on SQL to execute your dynamic query.
Thats it :D
But you should notice that execute command is not that good for performance issues.

Resources