ASP.Net - Mvc5 : LINQ , Saving duplicate record problem - c#

I am coding daily counter. Database Counter Table is empty. If someone is my first visitor of current day, then I am adding a record to database and i am setting counter=1; After this, when other visitor visit current day, then i am increasing my counter++ and i am updating the record.
So I my records must be like this:
Date:2018-10-01 counter:23
Date:2018-10-02 counter:65
Date:2018-10-03 counter:20
Date:2018-10-04 counter:89
My problem is this: If the site get visitor same time, linq save 2 record for same day. Like this:
Date:2018-10-01 counter:23
Date:2018-10-02 counter:1 //First record: counter=1
Date:2018-10-02 counter:65 //Second record: counter=65
Date:2018-10-03 counter:20
Date:2018-10-04 counter:1 //First record
Date:2018-10-04 counter:89 //second record
Date must be unique. How can I resolve this problem? My code is below. Thanks a lot.
public static int IncreaseCounter_DailySiteVisitors()
{
int counter = 0;
using (var context = new MyProjectEntities())
{
try
{
string format = "dd.MM.yyyy";
DateTime Today = DateTime.Now;
var obj = (from record in context.CounterDailySiteVisitor
where
record.DateRecord != null
&& record.DateRecord.HasValue
&& record.DateRecord.Value.Year == Today.Year
&& record.DateRecord.Value.Month == Today.Month
&& record.DateRecord.Value.Day == Today.Day
select record).FirstOrDefault();
//var obj = context.CounterDailyVisitor.Where(x => x.DateRecord != null && ((DateTime)x.DateRecord).ToString("yyyy.MM.dd") == DateTime.Now.ToString("yyyy.MM.dd")).FirstOrDefault();
if (obj != null)
{
counter = obj.Count ?? 0;
counter++;
obj.Count = counter;
context.SaveChanges();
}
else
{
var newRecordObj = context.CounterDailySiteVisitor.Create();
newRecordObj.Count = 1;
newRecordObj.DateRecord = Today;
context.CounterDailySiteVisitor.Add(newRecordObj);
context.SaveChanges();
}
}
catch (Exception e)
{
}
}
return counter;
}

the chances of this being hit by two thread at the same time is quite low.
but i guess technically it can so you would need to wrap this in a lock
Something like below...
public static int IncreaseCounter_DailySiteVisitors()
{
private readonly object somethingObject = new object();
var context = new MyProjectEntities()
var today = DateTime.Now;
var todaysRecord = context.CounterDailyVisitor
.SingleOrDefault(x => x.DateRecord.Year == Today.Year
&& x.DateRecord.Month == Today.Month
&& x.DateRecord.Day == Today.Day
);
if (todaysRecord != null)
{
//the existing count + 1
todaysRecord.Count = todaysRecord.Count++;
}
else
{
Lock(somethingObject)
{
//recheck
var todaysRecord = context.CounterDailyVisitor
.SingleOrDefault(x => x.DateRecord.Year == Today.Year
&& x.DateRecord.Month == Today.Month
&& x.DateRecord.Day == Today.Day
);
if (todaysRecord != null)
{
//the existing count + 1
todaysRecord.Count = todaysRecord.Count++;
}
else
{
var newRecordObj = new CounterDailyVisitor();
newRecordObj.Count = 1;
newRecordObj.DateRecord = DateTime.Now; //this shouldnt be nullable
context.CounterDailySiteVisitor.Add(newRecordObj);
}
}
}
context.SaveChanges();
}

This is quite a common concurrency problem i.e. race condition. You will either have to Lock around the code that reads and subsequently updates/inserts the value. Or you should call a stored procedure and have all the logic inside the stored proc.
Lock comes with it's own set of issues if you're planning on using a web farm or running multiple instances of this MVC app.

Related

Return error message from a void method in C#

I'm currently trying to get my method to return an error message if a condition isn't valid. But I am uncertain how to go about this in a void method.
I have a method that looks like this
[HttpPost]
[ValidateAntiForgeryToken]
[Audit]
public void AddUnits(int so_id, int site_id, int[] addItem_id, int[] addItem_qty, int[] addItem_disc)
{
// Loop however many times is necessary to iterate through the largest array
for (int i = 0; i < Math.Max(Math.Max(addItem_id.Length, addComp_id.Length), addPart_id.Length); i++)
{
foreach (SODetails sod in db.SalesOrders.Find(so_id).SalesDetails)
{
if (i < addItem_id.Length && addItem_qty[i] != 0 && sod.ItemID == addItem_id[i] && addItem_id[i] != 365 && addItem_id[i] != 410)
{
sod.item_qty += addItem_qty[i];
sod.item_discount = addItem_disc[i];
addItem_id[i] = 0;
addItem_qty[i] = 0;
addItem_disc[i] = 0;
}
}
db.SaveChanges();
if(i < addItem_qty.Length && addItem_qty[i] != 0)
{
SODetails sODetails = new SODetails
{
SalesOrderID = so_id,
SiteID = site_id
};
// Only add a unit to the SODetails object if it's not null and has an id and quanitity specified
if(i < addItem_id.Length && addItem_id[i] != 0 && addItem_qty[i] != 0)
{
sODetails.ItemID = addItem_id[i];
sODetails.item_qty = addItem_qty[i];
sODetails.item_discount = addItem_disc[i];
}
if (sODetails.SiteID == 0)
sODetails.SiteID = null;
SalesOrder SO = db.SalesOrders.Find(sODetails.SalesOrderID);
SODetails salesOrderDetails = db.SODetails.Add(sODetails);
salesOrderDetails.SalesOrder = SO;
Item SO_Item = db.Items.Find(sODetails.ItemID);
Component SO_Component = db.Components.Find(sODetails.ComponentID);
Part SO_Part = db.Parts.Find(sODetails.PartID);
if (SO_Item != null)
{
if (SO.OrderType == SOType.OffSiteInventory && sODetails.InventorySite == "Main Inventory" && SO_Item.On_Hand < salesOrderDetails.item_qty)
{
ModelState.AddModelError(string.Empty, "Not enough stock in inventory");
//TempData["SalesOrderMessage"] = SO_Item.SalesOrderMessage;
}
sODetails.item_qty = sODetails.item_qty == null ? 0 : sODetails.item_qty;
int qtyOrdered = sODetails.item_qty == null ? 0 : (int)sODetails.item_qty;
salesOrderDetails.dynamicItem_qty = qtyOrdered;
if (SO_Item.SalesOrderMessage != null)
TempData["SalesOrderMessage"] = SO_Item.SalesOrderMessage;
}
db.SODetails.Add(sODetails);
}
}
db.SaveChanges();
}
Here is the part of the code where I am doing the validation check
if (SO.OrderType == SOType.OffSiteInventory && sODetails.InventorySite == "Main Inventory" && SO_Item.On_Hand < salesOrderDetails.item_qty)
{
ModelState.AddModelError(string.Empty, "Not enough stock in inventory");
//TempData["SalesOrderMessage"] = SO_Item.SalesOrderMessage;
}
If the condition is valid I want it to return to the screen with an error message showing up. But with the method being Void, I don't know how I can make it do this, or I don't know if it is even possible.
You could create a specific exception class that you can throw in your void function. You then handle this exception in the calling function.
class NotEnoughStockException : Exception {}
Then in your method:
If no stock ...
throw new NotEnoughStockException();
In the calling method
try {
call the stock method
} catch NotEnoughStockException {
whatever you want to do
}
Create a wrapper function that will call your stock function. You do try catch in that new function and return an error message. Your Ajax should call the new function.

multiple filters on ICollectionView with multiple condition checks WPF

This question has been asked before in different forms, but none answer my question. I have scraped this forum and Windows dev forum for info, before posting this. I cannot make sense of how to accomplish this task.
I am at my wits end.
I have a DataGrid that is binded to an ICollectionView. This data was pulled via a stored procedure and the DataGrid is automatically generated based on the columns in each table. For reference, the SP returns a list of objects, each with a series of members like stock_symbol, stock_price, Date etc.
There are a series of filters I would like to apply to this collection view. Two comboboxes and two datepickers, to be more specific. Each with a checkbox to signify they are active.
Each checkbox event handler stores the data that was selected from the combobox or the datepicker. I am trying to compare what is in those variables, to each relevant member of the object list and send that filtered object list back to the DataGrid.
This is my code:
private void FillDataGrid()
{
//Connect contains a simple stored procedure connection to SQL server
var Client = Connect();
DTOClass[] dTOs = Client.GetData();
SetDTOClass(dTOs);
MainGrid.ItemsSource = FilterView(dTOs);
}
Here is FilterView() (apologies for the long commented sections, I am trying to include my attempts in one foul sweep):
public ICollectionView /*List<DTOClass>*/ FilterView(DTOClass[] DTO)
{
if (_CollectionViewInternal == null)
{
//Assign collected DTO object to an ICollectionView
_CollectionViewInternal =
CollectionViewSource.GetDefaultView(DTO);
}
/*
ObservableCollection<DTOClass> DTOview = null;
if (DTOViewInternal == null)
{
int j = DTO.Length;
DTOview = new ObservableCollection<DTOClass>();
for(int i = 0; i < j; i++)
{
DTOview.Add(DTO[i]);
}
DTOViewInternal = DTOview;
}
*/
//Add a default sort description to the Date column
_CollectionViewInternal.SortDescriptions.Add(new SortDescription("Date", ListSortDirection.Ascending));
//assign our view to the maingrid (move this to later in the
//MainGrid.ItemsSource = _CollectionViewInternal;
if (MainGrid.ItemsSource != null)
{
/*List<Predicate<IEnumerable<DTOClass[]>>>*/ FilteredView = new List<Predicate<IEnumerable<DTOClass[]>>>();
//DateTime _zeroDay = new DateTime(1, 1, 1);
//DateTime _now = DateTime.Now;
FilteredView.Clear();
return FilteredView = _CollectionViewInternal.Where(Function(w) w.accountname.Contains(txtFilter.Text) _
Or w.firstname.Contains(txtFilter.Text) _
Or w.lastname.Contains(txtFilter.Text) _
Or w.isenabled.Contains(txtFilter.Text) _
Or w.description.Contains(txtFilter.Text) _
Or w.lastlogontimestamp.Contains(txtFilter.Text) _
Or w.whencreated.Contains(txtFilter.Text) _
Or w.whenchanged.Contains(txtFilter.Text) _
Or w.oulocation.Contains(txtFilter.Text) _
Or w.co.Contains(txtFilter.Text) _
Or w.l.Contains(txtFilter.Text) _
Or w.state.Contains(txtFilter.Text))
//if (yearsChosen > 0)
/* Stock, Maxadj, FromDate, ToDate */
/*
if (Stock_CheckBox.IsChecked != null)
{
FilteredView.Add(new Predicate<IEnumerable<DTOClass[]>>(x => x.Where(item => item. == Stock_ComboBoxText)));
}
if (letterChosen != "Any")
{
FilteredView.Add(new Predicate<IEnumerable<DTOClass[]>>(x => x.LastName.StartsWith(letterChosen)));
}
if (genderChosen != "Any")
{
FilteredView.Add(new Predicate<IEnumerable<DTOClass[]>>(x => x.Gender.Equals(genderChosen.Substring(0, 1))));
}
_CollectionViewInternal.Filter = dynamic_Filter;
RaisePropertyChanged("PeopleView");
// Bring the current person back into view in case it moved
if (CurrentPerson != null)
{
IEnumerable<DTOClass[]> current = CurrentPerson;
_CollectionViewInternal.MoveCurrentToFirst();
_CollectionViewInternal.MoveCurrentTo(current);
}
*/
/*
if (DTOview == null)
{
DTOview = DTOViewInternal;
} else
{
DTOViewInternal.
}
*/
//var collection = DTO;
//var symbol = collection.Where(item => item.Date == ).ToList();
//DTOview = new ObservableCollection<DTOClass>();
//IEnumerable<DTOClass> DTOview2;
//List<IEnumerable<DTOClass>> FilteredView = new List<IEnumerable<DTOClass>>();
/*
if (Stock_ComboBoxText != null)
{
//var collection = DTO;
var collection = DTO.Where(item => item.stock_symbol == Stock_ComboBoxText).Cast<DTOClass>().ToList();
//DTOview.Add(filtered.Cast<DTOClass>());
//FilteredView.Add(collection.Cast<DTOClass>());
FilteredView.Add(collection);
MainGrid.ItemsSource = FilteredView[0];
//FilteredView = filtered.Cast<DTOClass>();
}
if (Maxadj_ComboBoxText != 0)
{
var collection = DTO.Where(item => item.stock_price_adj_close == Maxadj_ComboBoxText).Cast<DTOClass>().ToList();
FilteredView.Add(collection);
MainGrid.ItemsSource = FilteredView[0];
//DTOview.Add(DTO.Where(item => item.stock_price_adj_close == ).ToList());
}
if (From_DatePickValue != null)
{
var collection = DTO.Where(item => item.Date >= From_DatePickValue).Cast<DTOClass>().ToList();
FilteredView.Add(collection);
MainGrid.ItemsSource = FilteredView[0];
}
if (To_DatePickValue != null)
{
var collection = DTO.Where(item => item.Date <= To_DatePickValue).Cast<DTOClass>().ToList();
FilteredView.Add(collection);
MainGrid.ItemsSource = FilteredView[0];
}
*/
//DTOview = DTOViewInternal;
//DTOview = null;
//DTOClass[] dto = GetDTOClass();
//ListCollectionView collectionView = new ListCollectionView(DTOViewInternal);
/*
collectionView.Filter = (e) =>
{
//int j = DTO.Length;
DTOClass[] dtofiltered = e as DTOClass[];
//for (int i = 0; i < j; i++)
//{
if ((Stock_ComboBoxText != null) && (DTOview[0][i].stock_symbol == Stock_ComboBoxText))
{
return true;
}
if ((Maxadj_ComboBoxText != 0) && (DTOview[0][i].stock_price_adj_close == Maxadj_ComboBoxText))
{
return true;
}
if ((From_DatePickValue != null) && (DTOview[0][i].Date >= From_DatePickValue))
{
return true;
}
if ((To_DatePickValue != null) && (DTOview[0][i].Date <= To_DatePickValue))
{
return true;
}
}
return true;
};
*/
//return collectionView.Cast<DTOClass>().ToList();
//return collectionView.Filter;
//return null;
//MainGrid.ItemsSource = null;
//MainGrid.ItemsSource = (CollectionView)CollectionViewSource.GetDefaultView(collectionView.ToString());
}
else
{
//MainGrid.ItemsSource = DTOview[0].ToList();
//MainGrid.ItemsSource = DTOview;
//return DTOview[0].ToList();
return _CollectionViewInternal;
}
return _CollectionViewInternal;
}
I only want to filter on a column, if it's relevant checkbox is checked. This is easy pickings with one filter, but more than one is proving to be beyond challenging.
As you can see I have attempted numerous solutions. I have tried using an ObservableCollection, I have tried filtering the object list directly and then adding it to an ICollectionView. Nothing works.
I have been attempting to graft this example: Complicated Filtering ICollectionView. But I cannot make heads or tails of it. I still don't understand predicates and tbh I really can't wrap my head around how it works.
I know it's frowned upon to ask 'gimme the code' questions. But if someone could just see past that and point out what I am doing wrong here, maybe even give me the code, I would be very grateful. I have spent weeks trying to understand this and I have run out of time on this assignment.
If not, then that's cool but please refrain from commenting on this thread. Don't take pride in withholding the answer either, I am usually an embedded C programmer and I just finished a full-sized OSX-Windows port for a massive Adobe AfterFX plugin. So I don't need snide remarks or any nonsense about putting more effort into learning, I just want to finish this assignment and be done with it.
Thank you all in advance.
Code to complement above comment:
List<Predicate<IEnumerable<DTOClass[]>>> FilteredView = null;
public ICollectionView FilterView(DTOClass[] DTO)
{
List<Predicate<IEnumerable<DTOClass[]>>>FilteredView = new
List<Predicate<IEnumerable<DTOClass[]>>>();
FilteredView.Clear();
if (Stock_CheckBox.IsChecked != null)
{
FilteredView.Add(new Predicate<IEnumerable<DTOClass[]>>(x => x.Where(item => item.stock_symbol == Stock_ComboBoxText)));
}
}
I can't even get the first predicate working. Apparently .stock_symbol isn't there and I can't index item. I suppose my real question here is how do I access the stock_symbol member?
Apologies for taking so long to get there, I am extremely sleep deprived.
EDIT:
Dumb mistakes are made with no sleep.
List<Predicate<DTOClass>> FilteredView = new List<Predicate<DTOClass>>();
if (Stock_CheckBox.IsChecked != null)
{
for (int i = 0; i < DTO.Length; i++)
{
FilteredView.Add(new Predicate<DTOClass>(x => x.stock_symbol == _Stock_ComboBoxText));
//FilteredView.Add(new Predicate<DTOClass[]>>(x => x.stock_symbol == _Stock_ComboBoxText));
}
}

linq conditional query

What would be the best practice for setting a status depending on several other "columns" retrieved in a linq query.
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = 0
});
I'd like to set the status to either 0, 1, 2.
(ApprovedDate == null and DeclinedDate == null) --> 0
(ApprovedDate != null and DeclinedDate == null) --> 1
(DeclinedDate != null) --> 3
So perhaps something like:
var result = (from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate,
Status = (q.CreatedDate == null && q.DeclinedDate == null) ? 0 : (q.ApprovedDate != null && q.DeclinedDate == null) ? 1 : 2
});
I might add even more status combinations, so should I try and do this in the linq select query, in my repository object.. Or later on in the controller where I would do a .ToList() and then foreach the list to set the correct status code?
Having even more than 3 statuscodes, the linq query gets "hard" to read.
What about moving status calculation to Item class? If status property depends on other properties value, then it's definitely calculated property:
var result = from q in query
select new Item
{
ApprovedDate = q.ApprovedDate,
CreatedDate = q.CreatedDate,
DeclinedDate = q.DeclinedDate
});
And
public class Item
{
// other properties
public int Status
{
get
{
if (ApprovedDate == null and DeclinedDate == null)
return 0;
if (ApprovedDate != null and DeclinedDate == null)
return 1;
if (DeclinedDate != null)
return 3;
// etc
}
}
}
Actually I think it's best option, because in this case status calculation logic will be close to required data. If (for some reason) you can't use this approach, then move setting statuses to local items collection:
var items = result.ToList().ForEach(i => i.Status = CalculateStatus(i));
Maybe wrapped all in a function An do a linq like this
var result = (from q in query sele q).AsEnumerable()
.Select( x => new Item()
{
ApprovedDate = x.ApprovedDate,
CreatedDate = x.CreatedDate,
DeclinedDate = x.DeclinedDate,
Status = MyStatusFunction(x.CreatedDate,q.DeclinedDate)
});
public int MyStatusFunction(DateTime ApprovedDate , Datetime DeclinedDate)
{
if (ApprovedDate == null and DeclinedDate == null) return 0;
else if(ApprovedDate != null and DeclinedDate == null) return 1;
else if (DeclinedDate != null) return 3;
}

EF Duplicated Value

Im getting angry with this error and cannot solve it.
Please, some Jedi master help me.
I'm trying to save trhee Entities: Region, Content and RegionalContent. Region is OK but Regional Content has to be associated with one Content and each Content may have Many RegionalContents(Translations). But I always get a DbUpdateException that has a UpdateException that has a SqlCeException that says something like:
*Impossible to insert a duplicated value with same index. Table name = XBLContents,Constraint name = PK_XBLContents_000000000000001C *
I'm debugging it for some days and could not find the error. Please, note that I'm still a little Padawan.
This is the code that saves the objects in they proper Tables:
Region region;
if (!db.Regions.Any(x => x.ID == Locale))
{
region = new Region { ID = Locale };
db.Regions.Add(region);
db.SaveChanges();
}
else
region = db.Regions.SingleOrDefault(x => x.ID == Locale);
for (int i = start; i < (start + 2); i++)
{
string guid = itens[i].Groups["guid"].Value;
Content c = new Content(guid);
if (!db.Contents.Any(x => x.GUID == guid))
{
c.Type = Type.ToString();
c.PopularInfo(Locale);
db.Contents.Add(c);
}
else
c = db.Contents.SingleOrDefault(x => x.GUID == c.GUID);
RegionalContent regionalcontent;
if (!db.RegionalInfos.Any(x => x.ContentId == guid && x.RegionId == Locale))
{
if (c.HTML == null)
c.PopularInfo(Locale);
regionalcontent = new RegionalContent(c, Locale);
regionalcontent.Region = region;
regionalcontent.Name = HttpUtility.HtmlDecode(itens[i].Groups["name"].Value);
db.RegionalInfos.Add(regionalcontent);
db.Contents.Add(c);
db.SaveChanges();
}
else
regionalcontent = db.RegionalInfos.SingleOrDefault(x => x.ContentId == guid && x.RegionId == Locale);
c.RegionalInfo.Clear();
regionalcontent.Region = region;
c.RegionalInfo.Add(regionalcontent);
Contents.Add(c);
}
You are calling SingleOrDefault when you know 1 already exists. Just use Single.
I would not call SaveChanges to the very end.
Are you sure the GUIDs are unique every time?

Separating Records into indvidual months for mvc

I have a collection of records. Which have two boxers, match date, location etc...
I want to separate them by months and group them together. Currently I have what is below. And it works to a degree. That looks for matchdates in the future. that is this year and steps through each month (1-12) and finds any matches in that date range.
Placing it into a nice dictionary of int, enumerable where int is the month and enumberable is the collection of matches in that month
//Build the matches list by Months!!!
var summarysDic = new Dictionary<int, IEnumerable<MatchSummary>>();
for (int i = 1; i <= 12; i++)
{
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value.Year == DateTime.Now.Year &&
x.MatchDate.Value.Month == i &&
!x.HasResult() &&
x.MatchDate.Value > DateTime.Now);
if (MatchesOfMonth.Count() > 0)
{
summarysDic.Add(i, MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x=> new MatchSummary(x)).ToArray());
}
}
Problem is this currently only deals with this year. I would like to instead make it so it works for "the next 6 months" but this would of course have to work over the new year as well!
Whats the best/cleanest way to go about doing this?
thanks in advance!
P.S on a side note i have yet to find how to simply do DateTime.Now.Month.add(1) for example (as i will always be going from current date forwards!)
-----COMPLETED CODE!-----
//Build the matches list by Months!!!
var summarysDic = new Dictionary<string, IEnumerable<MatchSummary>>();
for (int i = 1; i <= 12; i++)
{
var checkDate = DateTime.Now.AddMonths(i);
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value.Month == checkDate.Month &&
x.MatchDate.Value.Year == checkDate.Year &&
!x.HasResult() &&
x.MatchDate.Value > DateTime.Now);
if (MatchesOfMonth.Count() > 0)
{
var firstMatchDate = MatchesOfMonth.First().MatchDate.Value;
if (firstMatchDate.Year != DateTime.Now.Year)
{
summarysDic.Add(firstMatchDate.ToString("MMMM yyyy"), MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x => new MatchSummary(x)).ToArray());
}
else
{
summarysDic.Add(firstMatchDate.ToString("MMMM"), MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x => new MatchSummary(x)).ToArray());
}
}
}
I believe you can get what you want without modifying your algorithm significantly:
//Build the matches list by Months!!!
var summarysDic = new Dictionary<int, IEnumerable<MatchSummary>>();
for (int i = 0; i <= 6; i++)
{
var checkDate = DateTime.Now.AddMonths(i);
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value.Year == checkDate.Year &&
x.MatchDate.Value.Month == checkDate.Month &&
!x.HasResult() &&
x.MatchDate.Value > DateTime.Now);
if (MatchesOfMonth.Count() > 0)
{
summarysDic.Add(i, MatchesOfMonth.OrderBy(x => x.MatchDate).Select(x=> new MatchSummary(x)).ToArray());
}
}
What's wrong with DateTime.Now.AddMonth(1)?
var MatchesOfMonth = matches.Where(x => x.MatchDate.Value <= DateTime.Now.AddMonth(i)
&& !x.HasResult()
&& x.MatchDate.Value > DateTime.Now);
I haven't compiled that, but it should run with only fairly minor tweeking...

Categories