Creating optimal query of oracle db using EF - entity-framework

I have an Oracle database where I am storing information about customers.
One of the fields is user number.
My UserNumber column is type of text.
A user sends a number in various formats:
+44777XXXXXXX
777XXXXXXX
0777XXXXXXX
So far I have:
var list = context.UserDetails.Where(x => x.UserNumber == number).ToList();
I can also do this with:
var strippedNumber = ConvertNumberToBasic(number); // this will return me number as 777XXXXXXX
now
var list = context.UserDetails.Where(x => x.UserNumber.Contains(number)).ToList();
Is there a more optimal way for me to do this?

You really need to ask the customer what they need before you can write any code for this...

Related

Validate data exits using EF core best practices

List list = new List();
I have a list of Guid. What is the best to check all guid exits or not using ef core table?
I am currently using the below code but the performance is very bad. assume user table as 1 million records.
for Example
public async Task<bool> IsIdListValid(IEnumerable<int> idList)
{
var validIds = await _context.User.Select(x => x.Id).ToListAync();
return idList.All(x => validIds.Contains(x));
}
The performance is bad because you are reading each row of the table into memory, and then iterating through it (ToList materializes the query.) Try using the Any() method to take advantage of the strength of the database. Use something like the following: bool exists = _context.User.Any(u => idList.Contains(u));. This should translate to an SQL IN clause.
Provided you assert that the # of IDs being sent in is kept reasonable, you could do the following:
var idCount = _context.User.Where(x => idList.Contains(x.Id)).Count();
return idCount == idList.Count;
This assumes that you are comparing on a unique constraint like the PK. We get a count of how many rows have a matching ID from the list, then compare that to the count of IDs sent.
If you're passing a large # of IDs, you would need to break the list up into reasonable sets as there are limits to what you can do with an IN clause and potential performance costs as well.

DynamoDB Swift4 complex query

I am trying to make a complex query in swift to get data from DynamoDB.
I am able to get all information by using the userID. However there are times that I may not know the entirety of the userID and need to make a more complex query.
For instance, if I know the first name and the last name, and the user id format is "firstname:lastname:email", I need to be able to query all userID's that include the first and last name, then add a where for another column.
I am very new to dynamo and want to accomplish something like the sql query below.
SQL example:
SELECT * FROM mytable
WHERE column2 LIKE '%OtherInformation%'
AND (column1 LIKE '%lastname%' OR column1 LIKE '%firstname%')
Here is the code I have in swift4 for getting the userID if I know it exaclty, not entirely sure how to modify this for complex queries.
func queryDBForUser(Fname: String, Lname: String) {
let userId = Fname + "." + Lname + ":" + (UIDevice.current.identifierForVendor?.uuidString)!
self.UserId = userId
let objectMapper = AWSDynamoDBObjectMapper.default()
let queryExpression = AWSDynamoDBQueryExpression()
queryExpression.keyConditionExpression = "#userId = :userId"
queryExpression.expressionAttributeNames = ["#userId": "userId",]
queryExpression.expressionAttributeValues = [":userId": userId,]
objectMapper.query(CheckaraUsers.self, expression: queryExpression, completionHandler: {(response: AWSDynamoDBPaginatedOutput? ,error: Error?) -> Void in
if let error = error {
print("Amazon DynamoDB Error: \(error)")
return
}
I have also tried many variations along the lines of the following code, with no luck:
queryExpression.keyConditionExpression = "#FirstName = :firstName and #LastName = :lastName,"
queryExpression.expressionAttributeNames = ["#FirstName": "FirstName" , "#LastName": "LastName"]
queryExpression.expressionAttributeValues = [":FirstName": Fname,":LastName": Lname]
Any help would be greatly appreciated, thanks in advance!
You won't be able to do this with a DynamoDB query. When you query a table (or index) in DynamoDB you must always specify the complete primary key. In your case that would mean the full value of "firstname:lastname:email".
You could sort of do this with a DynamoDB scan and a filter expression, but that will look at every item in your table, so it could be slow and expensive. Amazon will charge you for the read capacity necessary to look at every item in the table.
So if you really wanted to, the filter expression for the scan operation would be something like:
"contains (#FirstName, :firstName) and contains (#LastName, : lastName)"
Note that contains looks for an exact substring match, so if you want case insensitive matches (like ILIKE in SQL) it won't work.
If you need to do these types of queries then you need to evaluate whether or not DynamoDB is the right choice for you. DynamoDB is a NoSQL key/value store basically. It trades limited querying functionality for scalability and performance. If you are coming at DynamoDB from a SQL background and are expecting to be able to do freeform queries of anything in your table, you will be disappointed.
Got the query working by adding a secondary index to my DynamoDB table, although this is not what I initially wanted, it still works as now I can query for a value that exists in both columns I needed, without doing a table scan and filtering after.
query code:
queryExpression.indexName = "Index-Name" queryExpression.keyConditionExpression = "#LastName = :LastName and #otherValue = :otherValue"
queryExpression.expressionAttributeNames = ["#LastName": "LastName" , "#otherValue": "otherValue"]
queryExpression.expressionAttributeValues = [":LastName": Lname,":otherValue": self.otherValue!]

How to Convert list into other list using entityframework

I have two model classes first one is "Ticket" and other is "ExpTicket" there is only one additional column/properties in ExpTicket and others properties are same in both lists. I extract the data from database in the form of ExpTicket list and then re-assign all column/properties to Ticket List except one additional column which does not exist in Ticket list.
But iam unable to assign data from "ExpTicket" list to "Ticket" list. If anyone can timely help I shall be very thankful. Following is the code which i need to convert From ExpTicket into "Ticket" List but failed. Please help.
var GetTicket = ticketRepository.ExpTicket(r => r.TicketId == TicketId).ToList();
List<Ticket> CTicket = GetTicket.ToList();
First you have:
var GetTicket = ticketRepository.ExpTicket(r => r.TicketId == TicketId).ToList();
Then make a query:
var CTickets = (from t in GetTicket
select new Ticket{field1=t.field1,field2=t.field2...}).ToList();
Or just readjust your model to use TPT (Table per type, Table per type code first) Good luck
var CTickets = new List<Ticket>();
var ExpTicket = ticketRepository.Where(r => r.TicketId == TicketId);
foreach (var ticket in ExpTicket)
{
CTickets.Add(new Ticket { TicketId = ticket.TicketId, ... and so on });
}
// CTickets is the new list of tickets

Entity Framework Timeout

I have been trying to figure out how to optimize the following query for the past few days and just not having much luck. Right now my test db is returning about 300 records with very little nested data, but it's taking 4-5 seconds to run and the SQL being generated by LINQ is awfully long (too long to include here). Any suggestions would be very much appreciated.
To sum up this query, I'm trying to return a somewhat flattened "snapshot" of a client list with current status. A Party contains one or more Clients who have Roles (ASPNET Role Provider), Journal is returning the last 1 journal entry of all the clients in a Party, same goes for Task, and LastLoginDate, hence the OrderBy and FirstOrDefault functions.
Guid userID = 'some user ID'
var parties = Parties.Where(p => p.BrokerID == userID).Select(p => new
{
ID = p.ID,
Title = p.Title,
Goal = p.Goal,
Groups = p.Groups,
IsBuyer = p.Clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "buyer")),
IsSeller = p.Clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "seller")),
Journal = p.Clients.SelectMany(c => c.Journals).OrderByDescending(j => j.OccuredOn).Select(j=> new
{
ID = j.ID,
Title = j.Title,
OccurredOn = j.OccuredOn,
SubCatTitle = j.JournalSubcategory.Title
}).FirstOrDefault(),
LastLoginDate = p.Clients.OrderByDescending(c=>c.LastLoginDate).Select(c=>c.LastLoginDate).FirstOrDefault(),
MarketingPlanCount = p.Clients.SelectMany(c => c.MarketingPlans).Count(),
Task = p.Tasks.Where(t=>t.DueDate != null && t.DueDate > DateTime.Now).OrderBy(t=>t.DueDate).Select(t=> new
{
ID = t.TaskID,
DueDate = t.DueDate,
Title = t.Title
}).FirstOrDefault(),
Clients = p.Clients.Select(c => new
{
ID = c.ID,
FirstName = c.FirstName,
MiddleName = c.MiddleName,
LastName = c.LastName,
Email = c.Email,
LastLogin = c.LastLoginDate
})
}).OrderBy(p => p.Title).ToList()
I think posting the SQL could give us some clues, as small things like the order of OrderBy coming before or after the projection could make a big difference.
But regardless, try extracting the Clients in a seperate query, this will simplify your query probably. And then include other tables like Journal and Tasks before projecting and see how this affects your query:
//am not sure what the exact query would be, and project it using ToList()
var clients = GetClientsForParty();
var parties = Parties.Include("Journal").Include("Tasks")
.Where(p=>p.BrokerID == userID).Select( p => {
....
//then use the in-memory clients
IsBuyer = clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "buyer")),
...
}
)
In all cases, install EF profiler and have a look at how your query is affected. EF can be quiet surprising. Something like putting OrderBy before the projection, the same for all these FirstOrDefault or SingleOrDefault, they can all have a big effect.
And go back to the basics, if you are searching on LoweredRoleName, then make sure it is indexed so that the query is fast (even though that could be useless since EF could end up not making use of the covering index since it is querying so many other columns).
Also, since this is query is to view data (you will not alter data), don't forget to turn off Entity tracking, that will give you some performance boost as well.
And last, don't forget that you could always write your SQL query directly and project to your a ViewModel rather than anonymous type (which I see as a good practice anyhow) so create a class called PartyViewModel that includes the flatten view you are after, and use it with your hand-crafted SQL
//use your optimized SQL query that you write or even call a stored procedure
db.Database.SQLQuery("select * from .... join .... on");
I am writing a blog post about these issues around EF. The post is still not finished, but all in all, just be patient, use some of these tricks and observe their effect (and measure it) and you will reach what you want.

Entity Framework, How to include related entities in this example

I have a table AccountSecurity which is a many-to-many table that relates Account entities and Securities. When I write the query below it returns all Securities that satisfy the where clause. However each Security instance in the list no longer has the reference to the AccountSecurity it came from. So when I do list[0].AccountSecurity it is empty. Is there anyway to include that information? I know I can rewrite the query to return AccountSecurities instead and use .Include("Security") on that, but I wonder if it can be done another way.
var list = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec.Security).ToList();
UPDATE
Of course if I do two queries the graph gets populated properly, there has to be a way to do this in one shot.
var securities = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec.Security).ToList();
//this query populates the AccountSecurities references within Security instances returned by query above
var xref = (from acctSec in base.context.AccountSecurities
where acctSec.AccountId == accountId
select acctSec).ToList();
var list = (from sec in base.context.Securities
.Include("AccountSecurity")
where sec.AccountSecurities.Any(as => as.AccountId == accountId)
select sec).ToList();
Try this:
var list = (from acctSec in base.context.AccountSecurities.Include("Security")
where acctSec.AccountId == accountId
select acctSec).ToList();
Then simply use the Security property as needed, and since it's read at the same time AccountSecurities is (single SQL with join), it will be very efficient.