EF6 not saving changes - c#

In the following code, I have wrapped a dbContextTransactions around multiple savechanges. Everything from the ViewModel contains the correct data but for some reason, EF does not save the changes. When I debug it, I'm not seeing any exception thrown as well. Is there something special I need to do for a transaction that would prevent this from working?
I have validated that I am indeed targeting the correct database as I can query it through another page successfully.
public static Logger logger = LogManager.GetCurrentClassLogger();
public static Guid AddPlanItem(PlanItemAddViewModel viewModel, IEnumerable<HttpPostedFileBase> images)
{
Guid planIdGUID = Guid.NewGuid();
using (var context = new ApplicationDbContext())
{
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
PlanItem planItem = context.Plans.Create();
planItem.PlanId = planIdGUID;
planItem.CreateDate = DateTime.Now;
planItem.PlanIdTitle = viewModel.PlanIdTitle;
planItem.PlanTitle = viewModel.PlanTitle;
planItem.SolutionTitle = viewModel.SolutionTitle;
planItem.ActivityCodeId = viewModel.ActivityCodeId;
planItem.RegionId = viewModel.RegionId;
planItem.OperatingCenterId = viewModel.OperatingCenterId;
planItem.DistrictId = viewModel.DistrictId;
planItem.ServiceCenterId = viewModel.ServiceCenterId;
planItem.PlannerRACFId = viewModel.PlannerRACFId;
planItem.SeverityId = viewModel.SeverityId;
planItem.Description = viewModel.Description;
planItem.PreviousPlan = viewModel.PreviousPlan;
context.Plans.Add(planItem);
context.SaveChanges();
foreach (HttpPostedFileBase image in images)
{
var imageSubmit = new PlanItemImage
{
PlanId = planIdGUID,
PlanImageId = Guid.NewGuid(),
Image = image.ConvertToByte(),
ImageTitle = image.FileName
};
context.Image.Add(imageSubmit);
context.SaveChanges();
}
dbContextTransaction.Commit();
}
catch (Exception ex)
{
logger.Error("Error: " + ex.Message);
dbContextTransaction.Rollback();
}
}
}
return planIdGUID;
}

For the code I see, I'm not quite sure what the issue could be (yet). Even so, we can take an approach to determine what the issue could be.
First let's simplify the simplify the code.
public static Guid AddPlanItem(PlanItemAddViewModel viewModel, IEnumerable<HttpPostedFileBase> images)
{
Guid planIdGUID = Guid.NewGuid();
using (var context = new ApplicationDbContext())
{
PlanItem planItem = context.Plans.Create();
planItem.PlanId = planIdGUID;
planItem.CreateDate = DateTime.Now;
planItem.PlanIdTitle = viewModel.PlanIdTitle;
planItem.PlanTitle = viewModel.PlanTitle;
planItem.SolutionTitle = viewModel.SolutionTitle;
planItem.ActivityCodeId = viewModel.ActivityCodeId;
planItem.RegionId = viewModel.RegionId;
planItem.OperatingCenterId = viewModel.OperatingCenterId;
planItem.DistrictId = viewModel.DistrictId;
planItem.ServiceCenterId = viewModel.ServiceCenterId;
planItem.PlannerRACFId = viewModel.PlannerRACFId;
planItem.SeverityId = viewModel.SeverityId;
planItem.Description = viewModel.Description;
planItem.PreviousPlan = viewModel.PreviousPlan;
context.Plans.Add(planItem);
context.SaveChanges();
}
return planIdGUID;
}
This way we achieve two things:
By removing the try/catch we would be able to see any possible exceptions
By simplifying the save logic we can determine which where the issue is. If with this code everything works, we can then include the piece that saves the image and how it behaves.
Second, If everything works fine, we can add the next piece, the images but this time with the save changes outside the for bucle.
foreach (HttpPostedFileBase image in images)
{
var imageSubmit = new PlanItemImage
{
PlanId = planIdGUID,
PlanImageId = Guid.NewGuid(),
Image = image.ConvertToByte(),
ImageTitle = image.FileName
};
context.Image.Add(imageSubmit);
}
context.SaveChanges();
UPDATE
The issue was caused due to the multiple SaveChanges() calls. Editing the code to a single one solve the issue.

Related

Cancellation Token Disposed Exception

I had this piece of code that initially worked fine. However, After adding it to a class where I store my methods that are reused, it keeps failing. The exception that is caught states that the CancellationTokenSource has been Disposed. Can someone point me in the right direction?
I have tried creating a new client and Adding CancellationToken.None to the PutAsync() method from HTTPClient Class but it still fails with the CancellationTokenSource Disposed exception.
public async void AddProduct(Product product)
{
string storeId = "";
try
{
var storeData = JObject.Parse(Connect.Json).SelectToken("store").ToString();
var stores = JsonConvert.DeserializeObject<List<Store>>(storeData);
var store = stores[0];
storeId = store.Id;
store.Products.Add(product);
ProdInfo info = new Info();
foreach(Product p in store.Products)
{
info.AddedProducts = + p.Id;
}
var content = JsonConvert.SerializeObject(info);
using (Connect.Client)
using (var response = await Connect.Client.PutAsync(_url + "/stores/" + storeId, new StringContent(content)))
{
var cont = response.Content;
string result = await cont.ReadAsStringAsync();
if ((int)response.StatusCode == 200)
{
this.JobResult = result;
//this.JobResult = "Store has been successfully updated";
}
else
{
this.JobResult = result;
//this.JobResult = "Store was not updated!";
}
}
}
catch (Exception ex)
{
//this.JobResult = "Store has not been updated due to an error.";
this.JobResult = ex.Message;
}
}
I was able to solve this by simple removing 'using(Connect.Client)' from all of my methods. As #sellotape stated, They were disposing of the HttpClient before I was able to use it again. Thank you all for your contributions.

Why am I getting a deadlock when my code is accessed multiple times?

In my c# code I have the following method that creates a document in the database, adds metadata regarding the document to the database and then updates some information regarding the date the repository was last updated. This method is often called numerous times in quick succession as multiple file uploads are common. However I am having problems with the code failing due to deadlock in sql server.
private IEnumerable<DocumentMetadata> CreateDoc(int? jobId, int?repositoryId, int? folderId, string documentTypeString, IEnumerable<DocumentModel> files)
{
if ((jobId == null && repositoryId == null) || (jobId != null && repositoryId != null))
{
throw new InvalidOperationException("Either job id or repository id must be specified");
}
using (var tran = new TransactionScope())
{
List<DocumentMetadata> newDocuments = new List<DocumentMetadata>();
var documentType = GetDocumentTypeByPrefix(documentTypeString);
if (folderId == null)
{
// Find the root folder
var job = getJob(jobId);
var rootFolder = getRootFolder(job);
// If we can't find a root folder, create one
if (rootFolder == null)
{
rootFolder = CreateRootDirectory(job);
}
folderId = rootFolder.FolderId;
}
User currentUser = _userService.GetCurrentUser();
foreach (var file in files)
{
var document = new Document() { Document1 = file.Data };
var documentMetadata = new DocumentMetadata
{
Document = document,
CreatedDate = file.CreatedDate,
FileName = file.Filename,
FileSize = file.Data.Length,
FolderId = folderId,
DocumentType = documentType,
JobId = jobId,
RepositoryId = repositoryId,
User = currentUser
};
_unitOfWork.DocumentMetadata.Add(documentMetadata);
newDocuments.Add(documentMetadata);
}
// set repository updated date
if (repositoryId != null)
{
DocumentRepository repo = GetDocumentRepository(repositoryId);
if (repo != null)
{
repo.UpdatedDate = new DateTimeOffset(DateTime.Now);
}
}
_unitOfWork.SaveChanges();
tran.Complete();
return newDocuments;
}
}
After some debugging it would appear that the updating of the repository id is causing the deadlock problem. If I remove this code block outside of the transaction all files are saved with no errors.
Why would this code block
if (repositoryId != null)
{
DocumentRepository repo = GetDocumentRepository(repositoryId);
if (repo != null)
{
repo.UpdatedDate = new DateTimeOffset(DateTime.Now);
}
}
cause the deadlock? No other access is being made to the DocumentRepository table apart from in this method - as the locks are obtained in the same order surely there should be no deadlock?
What is it about this code that is leading to deadlock?
Updated: The code for GetDocumentRepository is:
public DocumentRepository GetDocumentRepository(int repositoryId)
{
var result = DocumentRepositories.SingleOrDefault(x => x.RepositoryId == repositoryId); return result;
}
Have you checked the code without defining a transaction explicitly? Based on your code I would say that you are trying to read something that has been modified but not commited.
Another test you could do is to try to add a breakpoint in your code and try to get the DocumentRepository using READ UNCOMMITTED.

Error adding multiple records MVC

i'm trying to add multiple textbox values to database it is working on just single textbox row but now working when i'm adding multiple rows of textboxes. i'm sharing what i have done so far.
Action Method:
public async Task<ActionResult> Create(FormCollection values)
{
var customer = new Customer();
var model = new TicketViewModel();
TryUpdateModel(model.TicketDetail);
try
{
foreach (var ticket in model.Tickets)
{
ticket.Date = DateTime.Now;
ticket.ProcessId = DateTime.Now.Ticks.ToString().Substring(12, 6);
ticket.CreationMethod = "Manual";
ticket.isCustomer = User.IsInRole("Customer") ? true : false;
ticket.Total = 0;
ticket.Email = model.TicketDetail.Ticket.Email;
customer.City = "Not Specified";
customer.Country = "Not SPecified";
customer.Image = "~/Images/nopic.jpg";
customer.Password = System.Web.Security.Membership.GeneratePassword(11, 3);
customer.IsActive = true;
customer.CreationMethod = "Manual";
customer.DateAdded = DateTime.Now;
customer.Email = ticket.Email;
customer.FirstMidName = string.IsNullOrEmpty(ticket.FullName) ? "Not Specified" : ticket.FullName;
customer.LastName = "Not Specified";
customer.Salutation = "Not Specified";
customer.UserName = DateTime.Now.Ticks.ToString().Substring(3, 9);
//ticket detail
var abcd = values["abcd"].ToString();
var getID = await db.Parts.Where(c => c.PartNumber == abcd)
.FirstOrDefaultAsync();
model.TicketDetail.GenericOrderId = ticket.GenericOrderId;
model.TicketDetail.PersonID = customer.PersonID;
model.TicketDetail.Status = "New";
model.TicketDetail.PartId = getID.PartId;
model.TicketDetail.Ticket.Date = DateTime.Now;
}
try
{
// db.Tickets.Add(ticket);
db.Customers.Add(customer);
db.TicketDetails.Add(model.TicketDetail);
}
catch (Exception ex)
{
ViewBag.PartId = new SelectList(db.Parts.Take(5), "PartId", "Name");
ModelState.AddModelError("", string.Format(ex.Message + "\n" + ex.InnerException));
return View(model.TicketDetail);
}
// Save all changes
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
catch(Exception ex)
{
ModelState.AddModelError("", String.Format(ex.Message + "\n" + ex.InnerException));
//Invalid - redisplay with errors
return View(model.TicketDetail);
}
}
ViewModel:
public class TicketViewModel
{
public TicketViewModel()
{
TicketDetails = new List<TicketDetail>();
TicketDetail = new TicketDetail();
Ticket = new Ticket();
Tickets = new List<Ticket>();
}
public virtual Ticket Ticket { get; set; }
public virtual IEnumerable<Ticket> Tickets { get; set; }
public virtual TicketDetail TicketDetail { get; set; }
public virtual IEnumerable<TicketDetail> TicketDetails { get; set; }
}
it is also giving error on this "TryUpdateModel(model.TicketDetail);" the error is value cannot be null, please guide me i'm stuck here i have searched internet but couldn't found any appropriate solution. i want to add multiple records
First all properties of your TicketViewModel class have to be instantiated.
To add multiple records (multiple Insert) you could use a StringBuilder and append the insert statements to it. You then have one big query string to be executed on your database.
If using values this is also a valid way:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
And loading the data to be inserted directly from a file is even faster.
Edit
(after down voting for no reason)
Because some people act like they know it all:
SQL injections are indeed a serious problem when dealing with Database access. That's no secret at all. A common technique to prevent the SQL query from being 'highjacked' you simply use the SQLCommand.Parameters property which is used to map each value individually to the statement to separate the query statement from the data (values) this way. It's now impossible to inject or manipulate statements whithout breaking them. And server side validation is standard to obtain maximum security as well as escaping special input characters and avoiding the use of privilegs with no or low restrictions.
This is NOT a wrong answer.

Creating Large List<T>

I have the following code:
var lstMusicInfo = new List<MediaFile>();
var LocalMusic = Directory.EnumerateFiles(AppSettings.Default.ComputerMusicFolder, "*.*", SearchOption.AllDirectories).AsParallel().ToList<string>();
LocalMusic = (from a in LocalMusic.AsParallel()
where a.EndsWith(".mp3") || a.EndsWith(".wma")
select a).ToList<string>();
var DeviceMusic = adb.SyncMedia(this.dev, AppSettings.Default.ComputerMusicFolder, 1);
Parallel.ForEach(LocalMusic, new Action<string>(item =>
{
try
{
UltraID3 mFile = new UltraID3();
FileInfo fInfo;
mFile.Read(item);
fInfo = new FileInfo(item);
bool onDevice = true;
if (DeviceMusic.Contains(item))
{
onDevice = false;
}
// My Problem starts here
lstMusicInfo.Add(new MediaFile()
{
Title = mFile.Title,
Album = mFile.Album,
Year = mFile.Year.ToString(),
ComDirectory = fInfo.Directory.FullName,
FileFullName = fInfo.FullName,
Artist = mFile.Artist,
OnDevice = onDevice,
PTDevice = false
});
//Ends here.
}
catch (Exception) { }
}));
this.Dispatcher.BeginInvoke(new Action(() =>
{
lstViewMusicFiles.ItemsSource = lstMusicInfo;
blkMusicStatus.Text = "";
doneLoading = true;
}));
#endregion
}));
The first part of the code gives me almost instant result containing:
Address on computer of 5780 files.
Get list of all music files on an android device compare it with those 5780 files and return a list of files found on computer but not on device (in my case it returns a list with 5118 items).
The block of code below is my problem, I am filling data into a class, then adding that class into a List<T>, doing it for 5780 times takes 60 seconds, how can I improve it?
// My Problem starts here
lstMusicInfo.Add(new MediaFile
{
Title = mFile.Title,
Album = mFile.Album,
Year = mFile.Year.ToString(),
ComDirectory = fInfo.Directory.FullName,
FileFullName = fInfo.FullName,
Artist = mFile.Artist,
OnDevice = onDevice,
PTDevice = false
});
//Ends here.
Update:
Here is the profiling result and I see it's obvious why it's slowing down >_>
I suppose I should look for a different library that reads music file information.
One way to avoid loading everything once, up front, would be to lazy load the ID3 information as necessary.
You'd construct your MediaFile instances thus...
new MediaFile(filePath)
...and MediaFile would look something like the following.
internal sealed class MediaFile
{
private readonly Lazy<UltraID3> _lazyFile;
public MediaFile(string filePath)
{
_lazyFile = new Lazy<UltraID3>(() =>
{
var file = new UltraID3();
file.Read(filePath);
return file;
});
}
public string Title
{
get { return _lazyFile.Value.Title; }
}
// ...
}
This is possibly less ideal than loading them as fast as you can in the background, if you do something like MediaFiles.OrderBy(x => x.Title).ToList() and nothing has been lazy loaded then you'll have to wait for every file to load.
Loading them in the background would make them available for use immediately after the background loading has finished. But you might have to concern yourself with not accessing some items until the background loading has finished.
You biggest bottleneck is new FileInfo(item), but you don't need FileInfo just to get the Directory and File names. You can use Path.GetDirectoryName and Path.GetFileName, which are must faster since no I/O is involved.
UltraID3 mFile = new UltraID3();
//FileInfo fInfo;
mFile.Read(item);
//fInfo = new FileInfo(item);
bool onDevice = true;
if (DeviceMusic.Contains(item))
{
onDevice = false;
}
// My Problem starts here
lstMusicInfo.Add(new MediaFile()
{
Title = mFile.Title,
Album = mFile.Album,
Year = mFile.Year.ToString(),
ComDirectory = Path.GetDirectoryName(item), // fInfo.Directory.FullName,
FileFullName = Path.GetFileName(item), //fInfo.FullName,
Artist = mFile.Artist,
OnDevice = onDevice,
PTDevice = false
});
//Ends here.

C# webservice losing data on return

I am programming a client program that calls a webmethod but when I get the return data there are missing values on some of the fields and objects.
The webmethod in turn is calling a WCF method and in the WCF method the return data is fine. But when it is passing to the webservice the return data is missing.
Is there any way to fix this problem?
This is my client code calling the webservice:
ReLocationDoc query = new ReLocationDoc();
query.PerformerSiteId = 1;
query.PerformerUserId = 1;
query.FromStatus = 10;
query.ToStatus = 200;
ReLocationDoc doc = new ReLocationDoc();
ServiceReference1.QPSoapClient service = new QPSoapClient();
try {
service.GetRelocationAssignment(query, out doc);
string test = doc.Assignment.Id.ToString();
} catch(Exception ex) {
MessageBox.Show(ex.Message);
}
The webmethod code is here:
[WebMethod]
return m_reLocationClient.GetRelocationAssignment(query, out reLocationDoc);
}
And at last the WCF code:
public ReLocationResult GetRelocationAssignment(ReLocationDoc query, out ReLocationDoc reLocationDoc) {
try {
LOGGER.Trace("Enter GetRelocationAssignment().");
ReLocationResult result = reLocationCompactServiceClient.GetRelocationAssignment(out reLocationDoc, query);
if(reLocationDoc.Assignment == null || reLocationDoc.Assignment.CurrentStatus == STATUS_FINISHED) {
ReLocationDoc newQuery = new ReLocationDoc();
newQuery.Assignment = new AssignmentDoc();
newQuery.Assignment.EAN = DateTime.Today.ToString();
newQuery.PerformerSiteId = QPSITE;
newQuery.PerformerUserId = QPUSER;
reLocationDoc.AssignmentStatus = m_settings.ReadyStatus; ;
result = reLocationCompactServiceClient.CreateReLocationAssignment(out reLocationDoc, newQuery);
}
return result;
} finally {
LOGGER.Trace("Exit GetRelocationAssignment().");
}
}
The GetRelocationAssignment:
public ReLocationResult GetRelocationAssignment(ReLocationDoc query, out ReLocationDoc reLocationDoc) {
try {
LOGGER.Trace("Enter GetRelocationAssignment().");
ReLocationDoc doc = new ReLocationDoc();
ReLocationResult result = new ReLocationResult();
new Database(Connection).Execute(delegate(DBDataContext db) {
User user = GetVerifiedUser(db, query, MODULE_ID);
SiteModule siteModule = SiteModule.Get(db, query.PerformerSiteId, MODULE_ID);
Status status = Status.Get(db, query.FromStatus, query.ToStatus, 0);
Status startStatus = Status.Get(db, query.FromStatus, 0);
Status endStatus = Status.Get(db, query.ToStatus, 0);
IQueryable<Assignment> assignments = Assignment.GetAssignmentsWithEndStatus(db, siteModule, endStatus);
assignments = Assignment.FilterAssignmentStartStatus(assignments, startStatus);
foreach(Assignment assignment in assignments) {
LOGGER.Debug("Handling assignment: " + assignment.Id);
result.Status = true;
AssignmentDoc assignmentDoc = FillAssignmentDoc(assignment);
//ReLocationDoc doc = new ReLocationDoc();
AssignmentStatus sts = assignment.AssignmentStatus.OrderByDescending(ass => ass.Id).First();
assignmentDoc.CurrentStatus = sts.Status.Zone;
Status currentStatus = sts.Status;
IList<Item> items = assignment.Items.ToList();
IList<ItemDoc> itemDocs = new List<ItemDoc>();
foreach(Item item in items) {
ItemDoc itemDoc = FillItemDoc(item);
ItemDetail itemDetail;
if(ItemDetail.TryGet(db, item.Id, out itemDetail)) {
ItemDetailDoc itemDetailDoc = FillItemDetailDoc(itemDetail);
itemDoc.Details = new ItemDetailDoc[1];
Event eEvent = null;
if(Event.GetEvent(db, itemDetail, currentStatus, out eEvent)) {
EventDoc eventDoc = FillEventDoc(eEvent);
itemDetailDoc.Events = new EventDoc[1];
if(eEvent.LocationId.HasValue) {
Location location = null;
if(Location.TryGet(db, eEvent.LocationId.Value, out location)) {
eventDoc.Location = new LocationDoc();
eventDoc.Location = FillLocationDoc(location, db);
}
}
itemDetailDoc.Events[0] = eventDoc;
}
itemDoc.Details[0] = itemDetailDoc;
}
itemDocs.Add(itemDoc);
}
assignmentDoc.Items = itemDocs.ToArray();
doc.Assignment = assignmentDoc;
}
}, delegate(Exception e) {
result.Message = e.Message;
});
reLocationDoc = doc;
return result;
} finally {
LOGGER.Trace("Exit GetRelocationAssignment().");
}
}
In all this code the return data is fine. It is loosing data only when passing to the webmetod.
Enter code here.
Also, the ordering of the XML tags in the message makes difference - I had a similar problem about maybe two years ago, and in that case parameter values were dissappearing during transmission because the sending part ordered the tags differently than what was defined in the schema.
Make surethe XML tags are being accessed with the same casing at either end. if the casing is not the same then the value won't be read.
You should check it all message are sending back from your webservice. Call your webservice manually and check its response.
If all data is there, probably your webservice reference is outdated; update it by right-clicking your webservice reference and choose "Update"
If your data don't came back, your problem is probably related to webservice code. You should check your serialization code (if any) again, and make sure all returned types are [Serializable]. You should check if all return types are public as it's mandatory for serialization.
As noted per John Saunders, [Serializable] isn't used by XmlSerializer.

Categories