I have the following code:
var docs = ctx.Documents.Select(a =>
new { a.ID, Content = a.Document, a.LastModified, CreatedDate = a.Created });
foreach (var doc in docs)
{
if (Utility.ContinueDocumentPreview)
{
_createFile(doc.ID, doc.Content, doc.CreatedDate, doc.LastModified);
_fireProgress(++counter, count);
}
else
{
break;
}
}
The Utility.ContinueDocumentPreview flag is set to false when a user hits a Cancel button while this process is running. The problem is when the flag is false and the code should break out of the loop, I get a SQL timeout exception.
Am I doing this incorrectly?
You could use .ToList to execute the SQL before looping, but it kind of depends on how much data you're getting in from your database. It could result in a big SQL query all at once, so test it out a lot to make sure you're getting the performance you like.
// You can add .ToList() here:
var docs = ctx.Documents.Select(a => new { a.ID, Content = a.Document, a.LastModified, CreatedDate = a.Created }).ToList();
// Or, you can add .ToList() here:
foreach (var doc in docs.ToList())
{
if (Utility.ContinueDocumentPreview)
{
_createFile(doc.ID, doc.Content, doc.CreatedDate, doc.LastModified);
_fireProgress(++counter, count);
}
else
{
break;
}
}
Related
I have an Entity Framework 6 class called Materials, which is reflected in my database as a table with the same name. Using a parent parameter, I need to return a sorted list of materials from a SQL Query, so that I can later check that edits the user makes do not affect the order. My SQL is a stored procedure that looks like this:
CREATE PROC [dbo].[GET_SortedMaterials](#FinishedGoodCode VARCHAR(50))
AS
SELECT
ROW_NUMBER() OVER (ORDER BY Component.Percentage_of_Parent DESC,Material.Material) AS _sortField
,Material.*
FROM
Components AS Component
INNER JOIN Materials AS Material ON Component.Child_Material = Material.Material
WHERE
Component.Parent_Code = #FinishedGoodCode
ORDER BY
Component.Percentage_of_Parent DESC
,Material.Material
As you can see, the orderby field is not included in the Material. For this reason, I felt I could not return just a set of Material objects and still keep the sorting - I have performed the ordering in SQL and added the _sortField (I think that field may be a bad idea).
My C# code to read the SQL looks like this:
public async Task<SortedList<int, Materials>> GET_SortedMaterials(IProgress<Report> progress, string finishedGoodCode)
{
try
{
var report = new Report { Message = "Retrieving Sorted Materials", NewLine = true, StatusCode = Enums.StatusCode.Working };
progress.Report(report);
using (var context = new DBContext())
{
var ingredientList = await context.Database.SqlQuery<(int _sortField,Materials mat)>("[app].[GET_Customers]").ToListAsync();
var sorted = new SortedList<int, Raw_Materials>();
foreach (var (_sortField, mat) in ingredientList.OrderBy(x=>x._sortField))
{
sorted.Add(_sortField, mat);
}
return sorted;
}
}
catch (Exception ex)
{ [EXCLUDED CODE]
}
}
When the code executes, I get the correct number of rows returned, but I do not get a Sorted list where the Key corresponds to the _sortField value and the Value to the Material value. I have tried various different versions of basically the same code and I cannot get the script to return a list of materials with information about their sorting, instead, the conversion to EF class fails entirely and I only get null values back:
Any advice about how to return a sorted list from SQL and maintain the sorting in C#, when the sort field is not in the return values would be very gratefully received.
use
var ingredientList = await context.Database.SqlQuery<Materials>("[app].[GET_Customers]").Select((mat, _sortField) => (_sortField, mat)).ToDictionary(x => x._sortField, x => x.mat);
or if you want async load use
var ingredientList = await context.Database.SqlQuery<Materials>("[app].[GET_Customers]").ToListAsync().Result.Select((mat, _sortField) => (_sortField, mat)).ToDictionary(x => x._sortField, x => x.mat);
full code
public async Task<SortedList<int, Materials>> GET_SortedMaterials(IProgress<Report> progress, string finishedGoodCode)
{
try
{
var report = new Report { Message = "Retrieving Sorted Materials", NewLine = true, StatusCode = Enums.StatusCode.Working };
progress.Report(report);
using (var context = new DBContext())
{
var ingredientList = await context.Database.SqlQuery<Materials>("[app].[GET_Customers]").ToListAsync().Result.Select((mat, _sortField) => (_sortField, mat)).ToDictionary(x => x._sortField, x => x.mat);
var sorted = new SortedList<int, Raw_Materials>();
foreach (var item in ingredientList.OrderBy(x => x.Key))
{
sorted.Add(item.Key, item.Value);
}
return sorted;
}
}
catch (Exception ex)
{
[EXCLUDED CODE]
}
}
I'm having a problem trying, what boils down to, incrementing a field in a document or inserting an entire document. The context is "trying to insert an initial document for a sequence or incrementing the sequence number for an existing sequence".
This code:
private async Task<int> GetSequenceNumber(string sequenceName)
{
var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
var builder = Builders<Sequence>.Update;
var update = builder
.SetOnInsert(x => x.CurrentValue, 1000)
.Inc(x => x.CurrentValue, 1);
var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
filter,
update,
new FindOneAndUpdateOptions<Sequence>
{
IsUpsert = true,
ReturnDocument = ReturnDocument.After,
});
return sequence.CurrentValue;
}
results in the exception
MongoDB.Driver.MongoCommandException: Command findAndModify failed: Updating the path 'currentvalue' would create a conflict at 'currentvalue'.
at MongoDB.Driver.Core.WireProtocol.CommandUsingCommandMessageWireProtocol`1.ProcessResponse(ConnectionId connectionId, CommandMessage responseMessage)
Removing the SetOnInsert results in no errors, but inserts a document with the currentValue equal to 1 instead of the expected 1000.
It almost appears if SetOnInsert is not being honored, and that what's happening is a default document is inserted and then currentValue is incremented via Inc atomically as the new document is created.
How do I overcome these issues? A non-C# solution would also be welcome, as I could translate that...
Ok thanks to #dododo in the comments, I now realize that both an Inc and a SetOnInsert can't be applied at the same time. It's unintuitive because you'd think the former would apply on update only and the latter on insert only.
I went with the solution below, which suffers more than one round-trip, but at least works, and appears to work with my concurrency based tests.
public async Task<int> GetSequenceNumber(string sequenceName, int tryCount)
{
if (tryCount > 5) throw new InvalidOperationException();
var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
var builder = Builders<Sequence>.Update;
// optimistically assume value was already initialized
var update = builder.Inc(x => x.CurrentValue, 1);
var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
filter,
update,
new FindOneAndUpdateOptions<Sequence>
{
IsUpsert = true,
ReturnDocument = ReturnDocument.After,
});
if (sequence == null)
try
{
// we have to try to save a new sequence...
sequence = new Sequence { Id = sequenceName, CurrentValue = 1001 };
await _context.SequenceNumbers.InsertOneAsync(sequence);
}
// ...but something else could beat us to it
catch (MongoWriteException e) when (e.WriteError.Code == DuplicateKeyCode)
{
// ...so we have to retry an update
return await GetSequenceNumber(sequenceName, tryCount + 1);
}
return sequence.CurrentValue;
}
I'm sure there are other options. It may be possible to use an aggregation pipeline, for example.
foreach (var distinctPart in distinctParts)
{
var list = partlist.Where(part =>
{
if (part.PartNumber.Equals(distinctPart))
return true;
return false;
}).Select(part =>
{
return part.Number;
}).Distinct();
int quantity = list.Count();
hwList[distinctPart] = quantity;
}
When I'm debugging and open the hwList dictionary, I get the error message:
Function evaluation disabled because a previous function evaluation timed out. You must continue execution to re enable function evaluation.
Why so complicated?
Perhaps you can already solve the problem by simplifying this code, like so:
foreach (var distinctPart in distinctParts)
{
var count = partlist.Where(part => part.PartNumber.Equals(distinctPart))
.Select(part => part.Number)
.Distinct().Count();
hwList[distinctPart] = count;
}
BTW, do you have a property called PartNumber and another Number, both defined on a Part?
I have some tags which I need to insert into the Tag database. The Tag database has only one column 'tag' which is also the primary key. This was the trick to prevent duplicates while inserting.
So now the code and the problem.
foreach (string tagval in tagarray)
{
try
{
var tag = new Tag
{
Tag1 = tagval
};
db.AddToTags(tag);
}
catch
{
}
}
db.SaveChanges();
The problem with this approach is after calling SaveChanges() if a duplicate is found early, the program exists without saving the other tags. If I call SaveChanges() after every addition to the table, the program will become inefficient and a lot of calls would need to be made. How to continue insertion even after the earlier insertions fail?
An alternate solution is also welcomed.
Couple of things you need to change here. First off, you're best off removing the duplicates from your own list before going anywhere near the database by calling .Distinct on your list to insert.
Also, there is no need for the try catch here, you should just check what's already in your database before your do the insert. Try this:
List<string> uniqueItems = tagarray
.Distinct()
.Where(x => !db.Tags.Contains(x))
.ToList();
foreach (string uniqueItem in uniqueItems)
{
var tag = new Tag
{
Tag1 = tagval
};
db.AddToTags(tag);
}
db.SaveChanges();
With Entity Framework and an ObjectContext derivation you could do somthing like this.
foreach (var newTag in tagarray.Select(t =>
new Tag { Tag1 = t }).Except(db.Tags))
{
db.Tags.AddObject(newTag);
}
try
{
db.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
}
catch (OptimisitcConcurrencyException)
{
db.Refresh(RefreshMode.StoreWins, db.Tags);
foreach (var newTag in tagarray.Select(t =>
new Tag { Tag1 = t }).Except(db.Tags))
{
db.Tags.AddObject(newTag);
}
db.SaveChanges();
}
foreach (string tagval in tagarray)
{
try
{
var tag = new Tag
{
Tag1 = tagval
};
if(Tags.Where(e =>tag ) != null)
{
dataContext.AddToTags(tag);
}
}
catch
{
}
}
dataContext.SaveChanges();
I have the following....
var jobsApplications = ( from applications in db.applications
where applications.employeeId == LogedUser.Id
select new { applications.id, applications.jobId, applications.confirmationDate });
Now I want to navigate this result like
foreach "something" in jobsApplications
But I don't now what to put in something since the select new create a new class.
Any suggestions
I guess you can let the compiler do the work for you:
foreach (var application in jobApplications)
{
// use the application wisely
}
Consider using Array.ForEach() to iterate through your IEnumerable or List. This is a bit more heavyweight.
Array.ForEach(jobsApplication, jobApp => {
if (jobApp.City == "Chicago")
{
jobApp.Approved = true;
}
});
If you want a simple foreach, then you can type the anonymous class as var
foreach (var jobApp in jobApplications)
{
if (jobApp.City == "Chicago")
{
jobApp.Approved = true;
}
}