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

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.

Related

Create database seeder with same random value in a table row with Laravel database seeder

I am trying to create a table and populate the table with the following fields with the help of database seeder:
option a
option b
option c
option d
correct option
First four fields will be assigned random word, and the last field 'correct option' will contain any one of the first four.
I could not find any solution to do it with Laravel database seeder. Can anyone help?
Something like this?
use faker random element function in your factory or seeder.
$optionA = $faker->word;
$optionB = $faker->word;
$optionC = $faker->word;
$optionD = $faker->word;
return [
'option_a' => $optionA,
'option_b' => $optionB,
'option_c' => $optionC,
'option_d' => $optionD,
'correct_option' => $faker->randomElement([$optionA,$optionB,$optionC,$optionD]),
];
Create a factory and use Faker to generate the random words you're after
This sounds like an ideal use case for JSON columns (both for questions and answers). For instance, you might decide to have multiple valid answers to a single multiple choice question.
In your migration:
// create_questions_table.php
...
$table->json('choices')->default(new Expression('(JSON_ARRAY())'));
$table->json('answer')->default(new Expression('(JSON_ARRAY())'));
From https://laravel.com/docs/7.x/migrations#column-modifiers:
Using an Expression instance will prevent wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns.
Then create a factory:
// QuestionFactory.php
$factory->define(Location::class, function (Faker $faker) {
$choices = $faker->words(4);
$answer = [ $choices[rand(1,4)] ];
return [
'choices' => $choices,
'answer' => $answer,
];
});
Using the Faker library included in Laravel, we can pick 4 words and randomly assign one of them to be the answer.

How to sort when also using a where clause

I am currently pulling a list from a database, using the following code. The list is retrieved using a WHERE condition, however the list is returned unsorted. This is in the controller.
How can I modify this code so that the returned list is sorted alphabetically?
if (!string.IsNullOrEmpty(TargetYear))
{
ViewBag.HSID = new SelectList(db.Hotspots.Where(g => g.HSID.Contains(TargetYear)).ToList(), "ID", "HSID");
}
On several other fields I have used the following method to order, but I'm not sure how, or if I can combine this with the where clause above. The key piece is ".OrderBy(e=>e.FIELD), however this is precisely the piece I'm not sure how to integrate with the query.
ViewBag.LocalityCode = new SelectList(db.Localities.OrderBy(e=>e.LOCALITY1), "LOC_CODE", "LOCALITY1");
Other helpful bits of info:
ASP.Net MVC5
Microsoft SQL 2012
if (!string.IsNullOrEmpty(TargetYear))
{
var data =
db.Hotspots
.Where(g => g.HSID.Contains(TargetYear))
.OrderBy(e=>e.HSID)
.ToList();
ViewBag.HSID = new SelectList(data,"ID", "HSID");
}

Salesforce Apex Class update custom object lookup field with id from parent

I am new to Apex and I’m struggling with creating a class to help me with some data analysis. I have data from a 3rd party (transactions__C) that has a field (fin_acct_txt__c) that is the pointer to another object (fin_accounts__C). I want to updated transactions__c with the id from fin_accounts__C into the lookup field transactions__c.fin_acct__c.
I want to do this in a class versus a trigger as there would be thousands of records loaded from the 3rd party on a monthly basis. I think doing this in bulk would be more efficient.
My thought is I create a list for transactions__c and a map for fin_accounts__c. Using the fin_acct_txt__c=fin_accounts__c.name I would be able to get the fin_accounts__c.id and update the transactions__c.fin_acct__c with that data.
But being new to Apex seems to be causing me some problems that I’m unsure how to resolve.
Here’s a copy of what I’ve done to date:
public class updateTxnFinAcctID {
// Build map of financial accts since that is unique
map<string ,fin_acct__c> finAccts = new map<string, fin_acct__c>
([select id,name from fin_acct__c where name!=null]);
//Iterate through the map to find the id to update the transactions
{
for(fin_acct__c finAcct: finAccts.values())
{
if (finAcct.name != Null)
{
finAccts.put(finAcct.name, finAcct);
}
// Find all records in transaction__c where fin_acct__c is null
//and the pointer is the name in the map
list<Transaction__c> txns =[
select id,fin_acct_txt__c from Transaction__c where fin_acct__c = null
and fin_acct_txt__c=:finaccts[0].name];
//create the list that will be used to update the transaction__c
list <Transaction__c> txnUpdate = new list <Transaction__c>();
{
//Find the id from fin_acct__c where name = fin_acct_txt__c
for (Transaction__c txn: txns){
finacct[0].Id =txn.fin_acct__c;
txnUpdate.add(txn);
}
//3. Update transaction with ID
}
}
// if (txnUpdate.size()>0 { update txnUpdate};
system.debug(txnUpdate.size());
}
}
I seem to be in a doom loop. The error I get is “Expression must be a list type: Map” pointing to the list txns = [ …]. But as that is not unique, it must be list. But I would believe I’ve got something structurally wrong here and that is a symptom of a larger issue.
Thanks.
I tried to understand what should to do your code, and I have a few tips, possibly they help to solve your issue:
1) In first loop over values of map finAccts you really don't need validation with if (finAcct.name != Null), because you already add it in SOQL query (where name!=null).
2) It's a bad practice - to put as a key to map different entities (for example, Ids and Names). I mean that when you queried fin_acct__c into the map finAccts, keys of the map are Ids of fin_acct__c. And then in the first loop you put in the same map the same objects only using their names as a key. If you really need such map with names as a keys is better to create new map and put the data there.
3) You execute SOQL query to Transaction__c object into the loop. It is likely to be the cause of an exception related to the SF limits (Especially if you are sure that the code will handle large amounts of data). Better collect all fin_acct__c names in list and move SOQL query out from the loop, using IN instead of = in where condition.
If I understood correctly that fin_acct_txt__c field contains names, not Ids, your class should looks something like:
public class updateTxnFinAcctID {
Map<String ,fin_acct__c> finAccts = new Map<String, fin_acct__c>
([select id,name from fin_acct__c where Name != null]);
Map<String, fin_acct__c> finAcctByNames = new Map<String, fin_acct__c>();
for(fin_acct__c finAcct: finAccts.values()){
finAcctByNames.put(finAcct.Name, finAcct);
}
List<Transaction__c> txns =[select id, fin_acct_txt__c, fin_acct__c
from Transaction__c where fin_acct__c = null
and fin_acct_txt__c IN finAcctByNames.keySet()];
List <Transaction__c> txnUpdate = new List<Transaction__c>();
for (Transaction__c txn: txns){
fin_acct__c relatedFinAcct = finAcctByNames.get(txn.fin_acct_txt__c);
if(relatedFinAcct != null){
txn.fin_acct__c = relatedFinAcct.Id;
txnUpdate.add(txn);
}
}
if(!txnUpdate.isEmpty()){
update txnUpdate;
system.debug(txnUpdate.size());
}
}
It possibly can contains some spelling mistakes, but it's a common idea.

How to apply a condition to a specific table in every request on Entity Framework?

I have a many-to-many structure mapped to entity framework. This is a sample of what it looks like:
User UserTag Tag
------- -------- -------
IdUser(PK) IdUserTag(PK) IdTag(PK)
Name IdUser(FK) TagName
Desc IdTag(FK) Active
Now, I needed to exclude from any request of any method the viewing of Tags that were Active=false.
First, I tried doing it manually in every method, like:
public User GetById(int id)
{
var item = UserRepository.GetById(id); //This is just a repository that calls the EF context
//EF automatically maps it to the *UserTags* property
foreach(var tag in item.UserTags)
{
if(tag.Tag.Active == false)
item.UserTags.Remove(tag);
}
}
But it throws the following exception:
The relationship could not be changed because one or more of the foreign-key properties is non-nullable
So, I wanted to know if there's a way to conditionaly filter every request made to a specific table, whether it is select or a join request.
Try this in your GetById method:
var user.UserTags = dbContext.Entry(user)
.Collection(u => u.UserTags)
.Query()
.Where(ut => ut.Active == true)
.ToList();
The supplied code fails because it is attempting to remove items from the data entities not the list. If you want to pass the data entity around instead of the data model, you need to not use Remove. Something like the below (untested should work).
tags = item.UserTags.Where((ut) => ut.Active).ToList();
This line will get you a list of data entities that are active. However, you should really map all of this into a data model (see AutoMapper) and then you would not be removing items from the database.

Building Many-to-many query with EF

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)));

Resources