LINQ in WCF returning repeating results - c#

I've got a LINQ query in a WCF service that runs and returns the correct number of results that I'm looking for, but repeats the first result 25 times instead of showing me all 25 different records.
The weird thing is that when I take the SQL query that it generates from the debugger and plug it into SQL Management studio, I get the correct results.
I have tried refreshing the view I'm querying from the edmx, and I've tried rewriting the query a few different ways, but I'm starting to run out of ideas.
I've included some of the code below. Any suggestions would be helpful. Thanks!
try
{
using (Entities db = new Entities())
{
var qInventory = db.vw_Web_Store_Inventory_Live
.Where(qi => qi.Sku_Number == inputSKU)
.ToList();
resultPInventory.SKU = inputSKU;
resultPInventory.StoreInventory = new List<StoreItem>();
foreach (var qi in qInventory)
{
resultPInventory.StoreInventory.Add(new StoreItem
{
StoreNum = qi.Store_Number,
Quantity = qi.Curr_Inv
});
}
}
}
catch (Exception e)
{
log.Error("[" + e.TargetSite + "] | " + e.Message);
}
log.Info("ProductInventory(" + inputSKU + ") returned " + resultPInventory.StoreInventory.Count + " results");
return resultPInventory;

As Gert's link in the comments points out, sometimes LINQ may do that if your primary keys are not set up well, or if there are multiple rows with no unique values in your database.
This link also shows a similar problem.
The solution, other than rewriting your database columns (although that would be better on the long run) with better primary / unique keys, is to select specific values anonymously (later you can assign them easily):
var qInventory = db.vw_Web_Store_Inventory_Live
.Where(qi => qi.Sku_Number == inputSKU)
.Select(qi => new { qi.Store_Number, qi.Curr_Inv })
.ToList();
resultPInventory.SKU = inputSKU;
resultPInventory.StoreInventory = new List<StoreItem>();
foreach (var qi in qInventory)
{
resultPInventory.StoreInventory.Add(new StoreItem
{
StoreNum = qi.Store_Number,
Quantity = qi.Curr_Inv
});
}
Of course this won't be the best way if you later need to use qInventory for other things. (In that case you can just select more fields)
PS, here is a way to shorten your code, but I am not sure if LINQ to Entities will allow it, so test it first:
resultPInventory.SKU = inputSKU;
resultPInventory.StoreInventory = db.vw_Web_Store_Inventory_Live
.Where(qi => qi.Sku_Number == inputSKU)
.Select(qi => new StoreItem { StoreNum = qi.Store_Number, Quantity = qi.Curr_Inv })
.ToList();

Assign new StoreItem() to a variable before adding it.

Related

Comparing two lists to see if I need to update the database or even add to

I have a simple class which holds a primary key of which I don't know what type it will be before it runs, as i'm getting the data from COM. It will either be an int or string.
I basically just need to fill up my toUpdateList & toAddList. This was working fine below with not too many records to play around with. However now the mongoDBList returns around 65k records and it's all turned very slow and it's taking 15+ minutes to resolve toUpdateList.
I'm pretty new to C# so I'm likely missing something.
I basically just need to compare one list to another and see if the RecordRevision is higher in the toUpdateList. For the toAddList this ones pretty simple as if it doesn't exist it needs to be added.
Thanks for looking I appreciate it!
class KeyRevision
{
public dynamic RecordRevision;
public dynamic PrimaryKey;
}
List<KeyRevision> potentialUpdateList = new List<KeyRevision>();
List<KeyRevision> mongoDBList = new List<KeyRevision>();
List<KeyRevision> toUpdateList = new List<KeyRevision>();
List<KeyRevision> toAddList = new List<KeyRevision>();
var sql = env.ExecuteSQL(sqlQuery);
sql.First();
// Loop over them and add to array
do
{
if (sql.RecordCount > 0)
{
//Console.WriteLine(sql.GetPropertyValue(primaryKey).ToString() + " : " + sql.RecordRevision);
var o = new KeyRevision();
o.PrimaryKey = sql.GetPropertyValue(primaryKey);
o.RecordRevision = sql.RecordRevision;
potentialUpdateList.Add(o);
}
sql.Next();
} while (!sql.EOF);
// Ask mongo for docs
var docs = collection1.Find(_ => true).Project("{RecordRevision: 1}").ToList();
// Get them into our type
mongoDBList = docs.ConvertAll(x => new KeyRevision()
{
PrimaryKey = x.GetValue("_id"),
RecordRevision = x.GetValue("RecordRevision")
});
// Finds which records we need to update
toUpdateList = potentialUpdateList.Where(x =>
mongoDBList.Any(y => y.PrimaryKey == x.PrimaryKey && y.RecordRevision < x.RecordRevision)).ToList();
// Finds the records we need to add
toAddList = potentialUpdateList.Where(x =>
mongoDBList.FindIndex(y => y.PrimaryKey == x.PrimaryKey) < 0).ToList();
Console.WriteLine($"{toUpdateList.Count} need to be updated");
Console.WriteLine($"{toAddList.Count} need to be updated");

CRM 2011: Retrive multiple from QueryExpression have data on Entity return but GetAttributeValue always blank

I'm using Query Expression to retrieve related records through N:N relationship between 2 entities (User and Engagement)
But when I proceed with return data from query. the method entity.Attributes["systemuserid"] always return blank although it has data.
Here is my code:
QueryExpression query = new QueryExpression(CrmAttributes.EntityName.User);
query.ColumnSet = new ColumnSet(CrmAttributes.SystemUserAttributes.UserId);
LinkEntity linkEntityFrom = new LinkEntity(CrmAttributes.EntityName.User,
CrmAttributes.EntityRelationships.EngagementToSystemUser,
CrmAttributes.SystemUserAttributes.UserId, CrmAttributes.SystemUserAttributes.UserId,
JoinOperator.Inner);
LinkEntity linkEntityTo = new LinkEntity(CrmAttributes.EntityRelationships.EngagementToSystemUser,
CrmAttributes.EntityName.Engagement,
CrmAttributes.EngagementAttributes.EngagementId, CrmAttributes.EngagementAttributes.EngagementId,
JoinOperator.Inner);
linkEntityFrom.LinkEntities.Add(linkEntityTo);
query.LinkEntities.Add(linkEntityFrom);
linkEntityFrom.LinkCriteria = new FilterExpression();
linkEntityFrom.LinkCriteria.AddCondition(
new ConditionExpression(CrmAttributes.EngagementAttributes.EngagementId, ConditionOperator.Equal, id));
var results = service.RetrieveMultiple(query);
foreach (var entity in results.Entities)
{
if(entity.Attributes.Contains(CrmAttributes.SystemUserAttributes.UserId)) {
Guid userGuid = entity.Attributes[CrmAttributes.SystemUserAttributes.UserId] //systemuserid
tracer.Trace("Guid: " + userGuid); // Always blank
}
}
But when I use KeyValuePair to get data. The data is exactly what I want
foreach (var entity in results.Entities)
{
Guid userGuid = new Guid();
foreach (KeyValuePair<string, object> attribute in entity.Attributes)
{
if (attribute.Key == CrmAttributes.SystemUserAttributes.UserId)
{
userGuid = (Guid)attribute.Value;
tracer.Trace("Guid: " + userGuid); //Return expected data
}
}
}
Are you certain systemuserid is the correct field name to use for this query?
When joins are involved, CRM will often use a different attribute name as many entities could have fields with the same name.
For example it could be something like user1.systemuserid.
I would suggest debugging the code to review the attributes returned by the query - I suspect you will find the information you require.
After a day leave this problem alone. It run perfectly now without any modification was made... Maybe something went wrong on the SDK or my CRM server and it took me a day to workaround with no answer ##.

Speeding up a linq query with 40,000 rows

In my service, first I generate 40,000 possible combinations of home and host countries, like so (clientLocations contains 200 records, so 200 x 200 is 40,000):
foreach (var homeLocation in clientLocations)
{
foreach (var hostLocation in clientLocations)
{
allLocationCombinations.Add(new AirShipmentRate
{
HomeCountryId = homeLocation.CountryId,
HomeCountry = homeLocation.CountryName,
HostCountryId = hostLocation.CountryId,
HostCountry = hostLocation.CountryName,
HomeLocationId = homeLocation.LocationId,
HomeLocation = homeLocation.LocationName,
HostLocationId = hostLocation.LocationId,
HostLocation = hostLocation.LocationName,
});
}
}
Then, I run the following query to find existing rates for the locations above, but also include empty the missing rates; resulting in a complete recordset of 40,000 rows.
var allLocationRates = (from l in allLocationCombinations
join r in Db.PaymentRates_AirShipment
on new { home = l.HomeLocationId, host = l.HostLocationId }
equals new { home = r.HomeLocationId, host = (Guid?)r.HostLocationId }
into matches
from rate in matches.DefaultIfEmpty(new PaymentRates_AirShipment
{
Id = Guid.NewGuid()
})
select new AirShipmentRate
{
Id = rate.Id,
HomeCountry = l.HomeCountry,
HomeCountryId = l.HomeCountryId,
HomeLocation = l.HomeLocation,
HomeLocationId = l.HomeLocationId,
HostCountry = l.HostCountry,
HostCountryId = l.HostCountryId,
HostLocation = l.HostLocation,
HostLocationId = l.HostLocationId,
AssigneeAirShipmentPlusInsurance = rate.AssigneeAirShipmentPlusInsurance,
DependentAirShipmentPlusInsurance = rate.DependentAirShipmentPlusInsurance,
SmallContainerPlusInsurance = rate.SmallContainerPlusInsurance,
LargeContainerPlusInsurance = rate.LargeContainerPlusInsurance,
CurrencyId = rate.RateCurrencyId
});
I have tried using .AsEnumerable() and .AsNoTracking() and that has sped things up quite a bit. The following code shaves several seconds off of my query:
var allLocationRates = (from l in allLocationCombinations.AsEnumerable()
join r in Db.PaymentRates_AirShipment.AsNoTracking()
But, I am wondering: How can I speed this up even more?
Edit: Can't replicate foreach functionality in linq.
allLocationCombinations = (from homeLocation in clientLocations
from hostLocation in clientLocations
select new AirShipmentRate
{
HomeCountryId = homeLocation.CountryId,
HomeCountry = homeLocation.CountryName,
HostCountryId = hostLocation.CountryId,
HostCountry = hostLocation.CountryName,
HomeLocationId = homeLocation.LocationId,
HomeLocation = homeLocation.LocationName,
HostLocationId = hostLocation.LocationId,
HostLocation = hostLocation.LocationName
});
I get an error on from hostLocation in clientLocations which says "cannot convert type IEnumerable to Generic.List."
The fastest way to query a database is to use the power of the database engine itself.
While Linq is a fantastic technology to use, it still generates a select statement out of the Linq query, and runs this query against the database.
Your best bet is to create a database View, or a stored procedure.
Views and stored procedures can easily be integrated into Linq.
Material Views ( in MS SQL ) can further speed up execution, and missing indexes are by far the most effective tool in speeding up database queries.
How can I speed this up even more?
Optimizing is a bitch.
Your code looks fine to me. Make sure to set the index on your DB schema where it's appropriate. And as already mentioned: Run your Linq against SQL to get a better idea of the performance.
Well, but how to improve performance anyway?
You may want to have a glance at the following link:
10 tips to improve LINQ to SQL Performance
To me, probably the most important points listed (in the link above):
Retrieve Only the Number of Records You Need
Turn off ObjectTrackingEnabled Property of Data Context If Not
Necessary
Filter Data Down to What You Need Using DataLoadOptions.AssociateWith
Use compiled queries when it's needed (please be careful with that one...)

quick and simple way to sort this data taken from a tsv and make it distinct as per one of the fields that it contains

I want to know the quickest and simplest way to sort the code shown below. Sorting from newRecord.AppCode would not be suitable as it will change the meaning of the output. So I need to sort every line from string outp. What would be the best way? Also I would like to make every row distinct. I beleive using LINQ would be very quick but I am not that great at it. Help appreciated. So close to getting it done! Note: Data is being pulled from a tsv. Using .net 3.5, visual studio 2008) Will mark answer as soon as I get progress. :)
while ((line = sr.ReadLine()) != null)
{
String[] splitted = line.Split('\t');
appcodes.Add(line);
Records newRecord = new Records();
newRecord.Server = splitted[0];
newRecord.Instance = splitted[1];
newRecord.AppCode = splitted[2];
newRecord.Database = splitted[3];
listrecords.Add(newRecord);
for (int i = 0; i < appcodes.Count(); i++)
{
if (newRecord.AppCode==appcodes[i].ToUpper())
{
String outp = newRecord.AppCode + " " + newRecord.Server + " " + newRecord.Instance + " " + newRecord.Database;
Console.WriteLine(outp);
}
}
}
have lists named Keepers and newkeepers. Was trying to do something like outp.sort() and outp.sort() but it doesnt work in strings. This is how I solved the problem.
Keepers.Add(outp);
Keepers.Sort();
newKeepers = Keepers.Distinct().ToList();
foreach (object o in newKeepers)
{
Console.WriteLine(o);
}
Console.ReadLine();
As you can see, newrecords contain different fields so I wrote a LINQ statement to solve the problem.
var sorted_list = (from r in newrecords
orderby r.AppCode, r.Server, r.Instance, r.Database
select r).Distinct().ToList();
var distinctSortedList = sorted_list.Distinct().ToList();

Newbie performance issue with foreach ...need advice

This section simply reads from an excel spreadsheet. This part works fine with no performance issues.
IEnumerable<ImportViewModel> so=data.Select(row=>new ImportViewModel{
PersonId=(row.Field<string>("person_id")),
ValidationResult = ""
}).ToList();
Before I pass to a View I want to set ValidationResult so I have this piece of code. If I comment this out the model is passed to the view quickly. When I use the foreach it will take over a minute. If I hardcode a value for item.PersonId then it runs quickly. I know I'm doing something wrong, just not sure where to start and what the best practice is that I should be following.
foreach (var item in so)
{
if (db.Entity.Any(w => w.ID == item.PersonId))
{
item.ValidationResult = "Successful";
}
else
{
item.ValidationResult = "Error: ";
}
}
return View(so.ToList());
You are now performing a database call per item in your list. This is really hard on your database and thus your performance. Try to itterate trough your excel result, gather all users and select them in one query. Make a list from this query result (else the query call is performed every time you access the list). Then perform a match between the result list and your excel.
You need to do something like this :
var ids = so.Select(i=>i.PersonId).Distinct().ToList();
// Hitting Database just for this time to get all Users Ids
var usersIds = db.Entity.Where(u=>ids.Contains(u.ID)).Select(u=>u.ID).ToList();
foreach (var item in so)
{
if (usersIds.Contains(item.PersonId))
{
item.ValidationResult = "Successful";
}
else
{
item.ValidationResult = "Error: ";
}
}
return View(so.ToList());

Categories

Resources