Switching unique identifiers with entityframework - c#

I am trying to switch Unique identifiers between 2 entities. My current solution works but is far from pretty. First i set one username to "temp" so there is no overlap in usernames. Afterwards I switch them.
public void SwitchUsernames(Guid personIdOld, Guid personIdNew)
{
// Get accounts
var personOld = GetAccount(personIdOld);
var personNew = GetAccount(personIdNew);
// switch usernames
string usernamePersonOld = personOld.username;
string usernamePersonNew = personNew.username;
try
{
personOld.SetUsername("Temp");
Context.SaveChanges();
}
catch (Exception) { throw; }
try
{
personNew.SetUsername(usernamePersonOld);
Context.SaveChanges();
}
catch (Exception)
{
personOld.SetUsername(usernamePersonOld);
Context.SaveChanges();
throw;
}
try
{
personOld.SetUsername(usernamePersonNew);
Context.SaveChanges();
}
catch (Exception)
{
personOld.SetUsername(usernamePersonOld);
Context.SaveChanges();
personNew.DomainAccounts.SetUsername(usernamePersonNew);
Context.SaveChanges();
throw;
}
}
The trycatch blocks are there to rollback everything in case something fails. If i do not save the context after every change it will fail. I really would like this a lot cleaner but i dont know if there is a way. I am here to learn so please enlighten me.

I really would like this a lot cleaner
Use a transaction, when an error occurs it will not push to the database so no need for a rollback.
Something like this:
public void SwitchUsernames(Guid personIdOld, Guid personIdNew)
{
// Get accounts
var personOld = Accounts.Find(personIdOld);
var personNew = Accounts.Find(personIdNew);
// switch usernames
string usernamePersonOld = personOld.UserName;
string usernamePersonNew = personNew.UserName;
using (var tran = _context.Database.BeginTransaction())
{
var temp = "TempName" + Guid.NewGuid().ToString();
personOld.UserName = temp;
_context.SaveChanges();
personNew.UserName = usernamePersonOld;
_context.SaveChanges();
personOld.UserName = usernamePersonNew;
_context.SaveChanges();
tran.Commit();
}
}

Related

GET<TModel> NOT by primaryKEY

I am using this function to query a table by its primary ID.
public virtual List<TModel> LoadAllWithChildren()
{
lock(Connection)
{
var result = LoadAll();
foreach (var item in result)
{
try
{
Connection.GetChildren(item);
}
catch (Exception e)
{
DebugTraceListener.Instance.WriteLine(GetType().Name,
"Can not load Child for itemType: " + item.GetType().Name + " reason: " + e);
}
}
return result;
}
}
But this always return results in regards to the primary key. The table I am querying on looks like this:
So when I do this, I get all entries in this table (not just the ones you see in the picture, those are the ones that I want)
So I did a small workaround:
var persistance = await Task.Run(() => PersistenceService.PresentationSequence.LoadAllWithChildren());
List<PresentationSequence> resSequence = new List<PresentationSequence>();
foreach(var elem in persistance)
if (elem.PresentationId == 166)
resSequence.Add(elem);
The result is the list as seen in the picture.
This, however, is slow.
Is there a way to use another SQlite function that will give me the list already filter for "166" in "presentationID"?
(So no local work is done)
EDIT: I have tried using this function:
public virtual TModel LoadById(int id)
{
lock(Connection)
{
try
{
var result = Connection.Get<TModel>(id);
return result;
}
catch (Exception ex)
{
DebugTraceListener.Instance.WriteLine(GetType().Name, $"Cannot find item with ID: {id}\n\n{ex}");
return null;
}
}
}
But this will only take the primary key in. But I want to query for presentation ID and not get a single result from the ID column
I ended up going with the query function:
try
{
var result = Connection.Query<TModel>(querystring);
return result;
}

Entity Framework "lost" a row after an error, can still navigate to row details

An application I'm working on at work had an interesting error that we couldn't track down and I've not been able to find anything similar on my searches.
My company has built a helpdesk system to track user issues, and after an error in submission the ticket "disappeared" from the list of all tickets. It was still visible in the database and was not filtered out anywhere. It was possible to navigate to the ticket by changing the URL to the ticket ID and all the details were the same as before the error in saving.
We ultimately solved the issue by restarting the web-app, but we'd like to track down the cause of the row disappearing from the list.
I've inherited this app and it has a whole host of issues we're already aware of, so I'm leaning towards the whole thing needing rebuilding from the ground up to make the save methods fully async instead of the half-and-half that we've currently got.
Not really sure what code to post as examples as the vast majority of the time it works, but obviously there's something not right in it.
EDITED TO ADD: Here's the edit post method from the controller
[HttpPost]
public async Task<ActionResult> Edit(EditTicketViewModel ticket)
{
try
{
var tech = (ticket.Technician_ID != null) ? this.UserService.Get(ticket.Technician_ID) : this.UserService.Get(this.User.Identity.GetUserId());
var cust = this.UserService.Get(ticket.Customer_ID);
var ogticket = this.TicketService.Get(ticket.Id);
var dateEdited = DateTime.UtcNow;
var productVersionId = OrganisationService.GetOrgProduct(ticket.OrgId, true).First(x => x.ProductID == ticket.ProductID).Product_Version.Id; ;
await this.TicketService.Edit(ticket.Id, ticket.Title, ticket.Description, dateEdited, ticket.Date_Closed, ticket.Customer_ID, ticket.State_Code, ticket.Priority_Code, ticket.Technician_ID, ticket.ProductID, productVersionId, ticket.Ticket_Type_ID);
if (ticket.State_Code == (int)StateCode.Closed)
{
await this.IntegrationService.SendEmailAsync(new EmailProperties()
{
Email = cust.Email,
Subject = "Ticket Closed",
Body = "Your ticket '{ticket.Title}' has now been closed.\n\n"//+
//$"Log into the portal to view it now -> <a href='https://helpdesk.rubixx.co.uk/Ticket/Details/{ticket.Id}'>Ticket {ticket.Id}</a>"
});
await this.IntegrationService.PrepareTeamsMessageAsync(new MessageProperties()
{
Summary = $"{this.UserService.Get(this.User).FullName} has closed a ticket",
Ticket_Id = ticket.Id,
Note_Id = null,
Type = TeamsMessageType.Closed
});
}
else await this.IntegrationService.PrepareTeamsMessageAsync(new MessageProperties()
{
Summary = $"{this.UserService.Get(this.User).FullName} has updated a ticket",
Ticket_Id = ticket.Id,
Note_Id = null,
Type = TeamsMessageType.Updated
});
this.TicketService.CreateTimelineEvent(ticket.Id, this.User.Identity.GetUserId(), DateTime.UtcNow.ToLocalTime(), "", TimelineEventType.TicketEdited);
this.AddToastMessage("Success", "Edited Successfully", ToastType.Success);
return Redirect(ticket.ReturnUrl);
}
catch (Exception ex)
{
this.IntegrationService.LogMessage($"Exception occurred: {ex.Message}");
this.AddToastMessage("Error: ", "An internal error has occurred", ToastType.Error);
return this.Redirect("/");
}
}
And the service function that actually saves the changes:
public async Task Edit(int ID, string Title, string Description, DateTime? dateedited, DateTime? dateclosed, string CustomerId, int StateCode, int Prioritycode, string TechnicianId, int Productid, int? productVersionId, int ticketType = (int)TicketTypeEnum.Support)
{
var originalTicket = Context.Tickets.Find(ID);
originalTicket.Title = Title;
originalTicket.Technician_ID = TechnicianId;
originalTicket.State_Code = ticketType == (int)TicketTypeEnum.FeatureRequest ? (int)Enums.StateCode.FeatureSuggestion : StateCode;
originalTicket.Priority_Code = ticketType == (int)TicketTypeEnum.FeatureRequest ? (int)PriorityCode.FeatureSuggestion : Prioritycode;
originalTicket.Description = Description;
originalTicket.Date_Edited = dateedited;
originalTicket.Date_Closed = dateclosed;
originalTicket.Customer_ID = CustomerId;
originalTicket.ProductID = Productid;
originalTicket.Product_Version_ID = productVersionId;
originalTicket.Ticket_Type_ID = ticketType;
await Context.SaveChangesAsync();
}
Here's the CreateTimelineEvent code:
public void CreateTimelineEvent(int ticketno, string raisedby, DateTime dateadded, string details, TimelineEventType type, string assignedto = null)
{
try
{
var timelineEvent = new TimelineEvent()
{
Ticket_Number = ticketno,
Raised_By = raisedby,
DateAdded = dateadded,
Details = details,
Type = (int)type,
Assigned_To = assignedto
};
Context.TimelineEvents.Add(timelineEvent);
Context.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
The GetTickets function:
public IEnumerable<Ticket> GetTickets(FilterType type, string current_user_id, TicketTypeEnum ticketType = TicketTypeEnum.Support)
{
IEnumerable<Ticket> tickets = null;
switch (ticketType)
{
case TicketTypeEnum.Support:
tickets = Context.Tickets.Include(e => e.Tech).Include(e => e.Customer).SupportTickets();
break;
case TicketTypeEnum.FeatureRequest:
tickets = Context.Tickets.Include(e => e.Tech).Include(e => e.Customer).FeatureRequestTickets();
break;
default:
tickets = Context.Tickets.Include(e => e.Tech).Include(e => e.Customer);
break;
}
switch (type)
{
case FilterType.OpenTickets:
return tickets.OpenTickets();
case FilterType.ClosedTickets:
return tickets.ClosedTickets();
case FilterType.MyOpenTickets:
return tickets.MyOpenTickets(current_user_id);
case FilterType.OpenedToday:
return tickets.OpenedToday();
case FilterType.DueToday:
return tickets.DueToday();
default:
return tickets;
}
}
Apologies for taking so long to get back to this question, but not long after I posted this I managed to track the issue down to a home-grown Dependency Injection system creating a static instance of the DBContext.
We've since decided to re-write the system in .Net Core to leverage the built in Dependency Injection.

Database Context save if valid

I have question regarding Entity Framework. In my program I first fill-up my DbContext with data. Then I need to delete all the data from the tables in DB, but only if new data will be saved. If db.Savechanges() throws an exception I need my old data to still be in the tables.
My code is:
static void Main(string[] args)
{
PdmContext db = new PdmContext();
FillDbContext();
try
{
if (db.SaveChanges() > 0)
{
using (var del = new PdmContext())
{
DeleteModel.deleteFromAllTables();
}
db.SaveChanges();
}
}
catch (Exception exp)
{
Logger.Log("Exception (global catch));
}
}
I can't seem to figure this out. Anyone can help with this? :)
You can use Transaction which will make sure to revert the operation done within the scope of it if the operation fails at some stage :
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var del = new PdmContext())
{
DeleteModel.deleteFromAllTables();
}
db.SaveChanges();
scope.Complete(); // commits the transaction
}
Now the changes to the database will be atomic so that it will only keep all changes or not at all. I have not included exception handling code for simplicity but due to any reason if the scope.Complete() was not being executed and control exists the transaction block without executing that the transaction will get rolled back.
You need to use a Transaction.
see how to use that:
using (var dbContextTransaction = PdmContext.Database.BeginTransaction())
{
try
{
// HERE your operation insert etc.
PdmContext.SaveChanges();
dbContextTransaction.Commit(); // here, apply your operation
}
catch (Exception)
{
dbContextTransaction.Rollback(); // here, undo your operations
}
}
You can handle such scenario with transaction management.
There is two way to handle it.
1) You can use single dbcontext for all operation instead of create multiple for single operation.
using (var context = new SchoolContext())
{
try
{
context.Students.Add(new Student()
{
FirstName = "Rama2",
StandardId = standard.StandardId
});
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Error occurred.");
}
}
2) Using single DbContextTransaction object:
using (var context = new SchoolContext())
{
context.Database.Log = Console.Write;
using (DbContextTransaction transaction = context.Database.BeginTransaction())
{
try
{
context.Students.Add(new Student()
{
FirstName = "Rama2",
StandardId = standard.StandardId
});
context.SaveChanges();
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Error occurred.");
}
}
}
I hope it work for you.

How to set delete function to In active the values from the database

I have delete function on my controller. My function deletes from the database and i works fine, but now i don't want it to permanently delete if from the database, but just set it to in active and not display it. but on database it will be existing.
I have a column called Active and it is 1. How can i make it in active not permanenlety delete it.
here is my code on the controller;
public ActionResult Deletefunction(Part model)
{
try
{
using (dbContext db = new dbContext())
{
Part del = db.Parts.Where(c => c.id == model.PartId).FirstOrDefault();
db.Parts.Remove(del);
db.SaveChanges();
}
return View();
}
catch (Exception ex)
{
throw ex;
}
}
Thanks for your help in advanced.
Assuming you have a column called Active on your Part object. You simply change the delete statement to an update as so;
public ActionResult Deletefunction(Part model)
{
try
{
using (dbContext db = new dbContext())
{
Part del = db.Parts.Where(c => c.id == model.PartId).FirstOrDefault();
// Changed from Delete to update
del.Active = 0;
db.SaveChanges();
}
return View();
}
catch (Exception ex)
{
throw ex;
}
}
Note that anywhere you display these, if you want to not show inactive ones, you will have to add a where clause like;
var onlyActiveParts = db.Parts.Where(p => p.Active == 1);

Update in couchbase

I need some help in update using couchbase. I have a task in my page. If the user clicks the like then the likes count should be updated in my couchbase bucket. I have tried my own update handler code but that has some time latency. I have included my update code too below.
This is my code for liking a task...
public ResponseVO LikeTask(LikeVO likeVO)
{
ResponseVO response = new ResponseVO();
try
{
if (!isLiked(likeVO.TaskID, likeVO.UserID))
{
UpdateTaskDB likeUpdate = new UpdateTaskDB();
UpdateTaskVO updatetaskvo = new UpdateTaskVO();
updatetaskvo.FieldName = "Likes";
LikeVO tempvo = new LikeVO();
tempvo.LikedOn = DateTime.Now.ToString();
tempvo.UserID = likeVO.UserID;
tempvo.UserName = likeVO.UserName;
tempvo.TaskID = likeVO.TaskID;
updatetaskvo.ObjectValue = tempvo;
updatetaskvo.TaskID = likeVO.TaskID;
likeUpdate.UpdateDocument(updatetaskvo);
}
response.StatusMessage = "Liked Successfully";
}
catch (Exception ex)
{
response.StatusCode = "0";
response.StatusMessage = ex.Message;
}
return response;
}
My own update handler code:
public class UpdateTaskDB
{
CouchbaseClient oCouchbase;
public UpdateTaskDB()
{
oCouchbase = new CouchbaseClient("vwspace", "");
}
public TaskVO GetTaskByID(string task_id)
{
TaskVO results = null;
try
{
String str1;
str1 = (String)oCouchbase.Get(task_id);
results = JsonConvert.DeserializeObject<TaskVO>(str1);
}
catch (Exception ex)
{
}
return results;
}
public void UpdateDocument(UpdateTaskVO inputParams)
{
try
{
var client = new CouchbaseClient("vwspace", "");
TaskVO taskDoc = GetTaskByID(inputParams.TaskID);
switch (inputParams.FieldName)
{
case "Likes":
List<LikeVO> docLikes = taskDoc.likes;
docLikes.Add((LikeVO)inputParams.ObjectValue);
taskDoc.likes = docLikes;
break;
case "UnLike":
LikeVO unlikevo = (LikeVO)inputParams.ObjectValue;
for (int count = 0; count < taskDoc.likes.Count; count++)
{
if (taskDoc.likes[count].UserID.Equals(unlikevo.UserID))
{
unlikevo = taskDoc.likes[count];
break;
}
}
taskDoc.likes.Remove(unlikevo);
break;
default:
break;
}
String json = JsonConvert.SerializeObject(taskDoc);
client.Store(StoreMode.Set, inputParams.TaskID, json);
}
catch (Exception ex)
{
Console.Write("Exception :" + ex.Message);
}
}
}
Is ther any other way to handle this update in couchbase? Kindly help me out..
The latency you're seeing is likely due to the fact that you're creating two instances of the CouchbaseClient for each click. Creating an instance of a CouchbaseClient is an expensive operation, because of the bootstrapping and configuration setup that takes place.
There are a couple of different approaches you can take to minimize how frequently you create CouchbaseClient instances. One would be to create a static client that is reused by your data access classes. Another approach for web apps is to associate instances with HttpApplication instances. For an example of the Web approach, see my (incomplete) sample project on GitHub below.
https://github.com/jzablocki/couchbase-beer.net/blob/master/src/CouchbaseBeersWeb/Models/WebRepositoryBase%271.cs
Also, I would suggest using CAS operations when updating a document's like count. You want to make sure that a "like" vote doesn't cause the entire document to be update from a stale read.
For example:
public TaskVO GetTaskByID(string task_id)
{
var getResult = oCouchbase.ExecuteGet<string>(task_id);
var results = JsonConvert.DeserializeObject<TaskVO>(str1.Value);
results.Cas = getResult.Cas; //Here I'm suggesting adding a Cas property to your TaskVO
return results;
}
Then on your update:
public void UpdateDocument(UpdateTaskVO inputParams)
{
try
{
TaskVO taskDoc = GetTaskByID(inputParams.TaskID);
switch (inputParams.FieldName)
{
...
}
String json = JsonConvert.SerializeObject(taskDoc);
client.ExecuteStore(StoreMode.Set, inputParams.TaskID, json, taskDoc.Cas);
//this will fail if the document has been updated by another user. You could use a retry strategy
}
catch (Exception ex)
{
Console.Write("Exception :" + ex.Message);
}
}

Categories