The environment is React, .NET Core 2.2.5, Oracle EF Core 2.19.30, Oracle 12c.
I am using the following code to add change log entries whenever an entity is changed in some way. Everything I've seen on Google indicates this should work. However, when it gets to the "base.SaveChanges()" call, I get the error "database operation expected to affect 1 row(s) but actually affected 5 row(s)". If I remove the change log code and just call "base.SaveChanges()", it works fine and does update the expected row, so the issue is clearly related to the change log entries itself.
I've seen this error on Google, but the actual number is always 0, never more. The other strange thing is that every time I run the code, the "actual" number increases by 5 (I'm up to 95 at the moment, and that's after I rebooted), which does happen to be the number of change log entries that should be added.
I don't see anything unusual in my situation, other than I'm using Oracle (I plan on testing this with SQL Server at home soon).
public override int SaveChanges()
{
List<EntityEntry> entries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified || x.State == EntityState.Deleted).ToList();
foreach (EntityEntry entry in entries.ToArray())
{
string tableName = new SafetyDataContext().Model.FindEntityType(entry.Entity.GetType()).Relational().TableName;
string keyData = GetPrimaryKeyValue(entry);
if (entry.State == EntityState.Added)
{
PropertyValues currentValues = entry.CurrentValues;
for (int i = 0; i < currentValues.Properties.Count; i++)
{
string columnName = currentValues.Properties[i].Relational().ColumnName;
string newValue = currentValues[currentValues.Properties[i].Name] != null ? currentValues[currentValues.Properties[i].Name].ToString() : string.Empty;
EntityChangeModel log = new EntityChangeModel()
{
Change_Type = "ADD",
Change_Date = DateTime.Now,
Change_User_ID = 0,
Table_Name = tableName,
Column_Name = columnName,
Primary_Key = keyData,
Old_Value = string.Empty,
New_Value = newValue
};
this.Changes.Add(log);
}
}
else if (entry.State == EntityState.Modified)
{
PropertyValues currentValues = entry.CurrentValues;
PropertyValues originalValues = entry.OriginalValues;
for (int i = 0; i < currentValues.Properties.Count; i++)
{
string columnName = currentValues.Properties[i].Relational().ColumnName;
string oldValue = entry.GetDatabaseValues().GetValue<object>(originalValues.Properties[i].Name) != null ? entry.GetDatabaseValues().GetValue<object>(originalValues.Properties[i].Name).ToString() : string.Empty;
string newValue = currentValues[currentValues.Properties[i].Name] != null ? currentValues[currentValues.Properties[i].Name].ToString() : string.Empty;
if (oldValue != newValue)
{
EntityChangeModel log = new EntityChangeModel()
{
Change_Type = "CHANGE",
Change_Date = DateTime.Now,
Change_User_ID = 0,
Table_Name = tableName,
Column_Name = columnName,
Primary_Key = keyData,
Old_Value = oldValue,
New_Value = newValue
};
this.Changes.Add(log);
}
}
}
else if (entry.State == EntityState.Deleted)
{
PropertyValues originalValues = entry.OriginalValues;
for (int i = 0; i < originalValues.Properties.Count; i++)
{
string columnName = originalValues.Properties[i].Relational().ColumnName;
string oldValue = entry.GetDatabaseValues().GetValue<object>(originalValues.Properties[i].Name) != null ? entry.GetDatabaseValues().GetValue<object>(originalValues.Properties[i].Name).ToString() : string.Empty;
EntityChangeModel log = new EntityChangeModel()
{
Change_Type = "DELETE",
Change_Date = DateTime.Now,
Change_User_ID = 0,
Table_Name = tableName,
Column_Name = columnName,
Primary_Key = keyData,
Old_Value = oldValue,
New_Value = string.Empty
};
this.Changes.Add(log);
}
}
}
return base.SaveChanges();
}
Related
var result = (from p in db.push_notifications
join nu in db.notification_recievers on p.id equals nu.push_notification_id
join nt in db.notification_types on p.notification_type_id equals nt.id
where (p.id == pushNotificationId && p.send_criteria == criteria && nu.delete_flag == false && p.delete_flag == false && nt.delete_flag == false)
select new NotificationList
{
conferenceId = p.conference_id,
pushNotificationId = p.id,
notificationId = nt.id,
notificationType = nt.notification_type,
nottificationDate = p.created_dt_tm,
criteria = (int)p.send_criteria,
notificationMessage = p.notification_msg,
userEmail=null,
userInterests = **getInterestNamesByPushNotificationId(p.id)**,
userEvents=null
}).Distinct();
public string getInterestNamesByPushNotificationId(int id)
{
string interests = string.Empty;
var query = from i in db.interests
join pn in db.notification_recievers
on i.id equals pn.interest_id
where pn.push_notification_id == id && pn.delete_flag == false
select new
{
name = i.name
};
foreach (var intr in query.Distinct())
{
if (interests == "")
{
interests = intr.name;
}
else
{
interests = interests + ", " + intr.name;
}
}
return interests;
}
this is throwing me error
LINQ to Entities does not recognize the method 'System.String
getInterestNamesBy PushNotification(Int32)' method, and this method
cannot be translated into a store expression.
The Entity Framework is trying to execute your LINQ clause on the SQL side, obviously there is no equivalent to 'getInterestNamesBy PushNotification(Int32)' from a SQL perspective.
You need to force your select to an Enumerable and then reselect your object using the desired method.
Not ideal but something like this should work - (not tested this so be nice).
var result = (from p in db.push_notifications
join nu in db.notification_recievers on p.id equals nu.push_notification_id
join nt in db.notification_types on p.notification_type_id equals nt.id
where (p.id == pushNotificationId && p.send_criteria == criteria && nu.delete_flag == false && p.delete_flag == false && nt.delete_flag == false)
select new { p=p, nu = nu, nt = nt }).AsEnumerable().Select( x => new NotificationList()
{
conferenceId = x.p.conference_id,
pushNotificationId = x.p.id,
notificationId = x.nt.id,
notificationType = x.nt.notification_type,
nottificationDate = x.p.created_dt_tm,
criteria = (int)x.p.send_criteria,
notificationMessage = x.p.notification_msg,
userEmail=null,
userInterests = getInterestNamesByPushNotificationId(x.p.id),
userEvents=null
}).Distinct();
i have done it this way
In my model
using (NotificationService nService = new NotificationService())
{
modelView = nService.DetailsOfNotifications(pushNotificationId, criteriaId).Select(x => new NotificationViewModelUI(x.conferenceId, x.pushNotificationId, x.notificationId, x.notificationType, x.nottificationDate, x.criteria, x.notificationMessage, x.userEmail, nService.getInterestNamesByPushNotificationId(x.pushNotificationId), nService.getIEventTitlesByPushNotificationId(x.pushNotificationId))).ToList();
}
public NotificationViewModelUI(int conferenceId, int pushNotificationId, int notificationId, string notificationType, DateTime dateTime, int criteria, string nMessage, string emailId = null, string interestNames = null, string eventTitles = null)
{
this.conferenceId = conferenceId;
this.pushNotificationId = pushNotificationId;
this.notificationId = notificationId;
this.notificationType = notificationType;
this.notificationDate = dateTime;
this.sendCriteria = (NotificationCriteria)criteria;
this.notificationMessage = nMessage;
this.emailId = NotificationCriteria.SpecificUser.Description() +"... "+ emailId;
this.interestNames = NotificationCriteria.UserByInterests.Description() + "... " + interestNames;
this.eventTitles = NotificationCriteria.UserByEvents.Description() + "... " + eventTitles;
}
I have 2 objects (lists loaded from XML) report and database (showed bellow in code) and i should analyse them and mark items with 0, 1, 2, 3 according to some conditions
TransactionResultCode = 0; // SUCCESS (all fields are equivalents: [Id, AccountNumber, Date, Amount])
TransactionResultCode = 1; // Exists in report but Not in database
TransactionResultCode = 2; // Exists in database but Not in report
TransactionResultCode = 3; // Field [Id] are equals but other fields [AccountNumber, Date, Amount] are different.
I'll be happy if somebody could found time to suggest how to optimize some queries.
Bellow is the code:
THANK YOU!!!
//TransactionResultCode = 0 - SUCCESS
//JOIN on all fields
var result0 = from d in database
from r in report
where (d.TransactionId == r.MovementID) &&
(d.TransactionAccountNumber == long.Parse(r.AccountNumber)) &&
(d.TransactionDate == r.MovementDate) &&
(d.TransactionAmount == r.Amount)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 0
};
//*******************************************
//JOIN on [Id] field
var joinedList = from d in database
from r in report
where d.TransactionId == r.MovementID
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount
};
//Difference report - database
var onlyReportID = report.Select(r => r.MovementID).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 1 - Not Found in database
var result1 = from o in onlyReportID
from r in report
where (o == r.MovementID)
orderby r.MovementID
select new TransactionList()
{
TransactionId = r.MovementID,
TransactionAccountNumber = long.Parse(r.AccountNumber),
TransactionDate = r.MovementDate,
TransactionAmount = r.Amount,
TransactionResultCode = 1
};
//*******************************************
//Difference database - report
var onlyDatabaseID = database.Select(d => d.TransactionId).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 2 - Not Found in report
var result2 = from o in onlyDatabaseID
from d in database
where (o == d.TransactionId)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 2
};
//*******************************************
var qwe = joinedList.Select(j => j.TransactionId).Except(result0.Select(r => r.TransactionId));
//TransactionResultCode = 3 - Transaction Results are different (Amount, AccountNumber, Date, )
var result3 = from j in joinedList
from q in qwe
where j.TransactionId == q
select new TransactionList()
{
TransactionId = j.TransactionId,
TransactionAccountNumber = j.TransactionAccountNumber,
TransactionDate = j.TransactionDate,
TransactionAmount = j.TransactionAmount,
TransactionResultCode = 3
};
you may try something like below:
public void Test()
{
var report = new[] {new Item(1, "foo", "boo"), new Item(2, "foo2", "boo2"), new Item(3, "foo3", "boo3")};
var dataBase = new[] {new Item(1, "foo", "boo"), new Item(2, "foo22", "boo2"), new Item(4, "txt", "rt")};
Func<Item, bool> inBothLists = (i) => report.Contains(i) && dataBase.Contains(i);
Func<IEnumerable<Item>, Item, bool> containsWithID = (e, i) => e.Select(_ => _.ID).Contains(i.ID);
Func<Item, int> getCode = i =>
{
if (inBothLists(i))
{
return 0;
}
if(containsWithID(report, i) && containsWithID(dataBase, i))
{
return 3;
}
if (report.Contains(i))
{
return 2;
}
else return 1;
};
var result = (from item in dataBase.Union(report) select new {Code = getCode(item), Item = item}).Distinct();
}
public class Item
{
// You need also to override Equals() and GetHashCode().. I omitted them to save space
public Item(int id, string text1, string text2)
{
ID = id;
Text1 = text1;
Text2 = text2;
}
public int ID { get; set; }
public string Text1 { get; set; }
public string Text2 { get; set; }
}
Note that you need to either implement Equals() for you items, or implement an IEqualityComparer<> and feed it to Contains() methods.
I would like to optimize following lines of code for Sorting.
public ViewResult Index(string sortorder, int? pagesize, int? page)
{
int pageSize = pagesize ?? 10;
if (Request.HttpMethod != "GET")
{
page = 1;
pageSize = 10;
}
ViewBag.SelectedPageSize = pageSize;
ViewBag.CurrentSort = sortorder;
ViewBag.FirstNameSortParm = String.IsNullOrEmpty(sortorder) ? "FirstName desc" : "";
ViewBag.LastNameSortParm = sortorder == "LastName" ? "LastName desc" : "LastName";
ViewBag.DepNameSortParm = sortorder == "depName" ? "depName desc" : "depName";
var joined = from tm in db.TabMasters select tm;
switch (sortorder)
{
case "FirstName":
joined = joined.OrderBy(m => m.FirstName);
break;
case "FirstName desc":
joined = joined.OrderByDescending(m => m.FirstName);
break;
case "LastName":
joined = joined.OrderBy(m => m.LastName);
break;
case "LastName desc":
joined = joined.OrderByDescending(m => m.LastName);
break;
case "depName":
joined = joined.OrderBy(m => m.depName);
break;
case "depName desc":
joined = joined.OrderByDescending(m => m.depName);
break;
default:
joined = joined.OrderBy(m => m.FirstName);
break;
}
int pageIndex = (page ?? 1) - 1;
int start = (pageIndex * pageSize);
ViewBag.TotalRecord = joined.Count();
ViewBag.StartRecord = start + 1;
ViewBag.EndRecord = ((start + pageSize) >= ViewBag.TotalRecord) ? ViewBag.TotalRecord : (start + pageSize);
return View(joined.ToPagedList(pageIndex, pageSize));
}
Because this is very tedious way if i have more the 10 fields to perform sort.
Thanks,
Imdadhusen
It's a bit vague to me what your actual goal is but for the switch part you could use an extension method as the below.
public static class SortExtensions
{
public static IEnumerable<T> SortByField<T>(this IEnumerable<T> sequence, string sortOrder)
{
var tokens = sortOrder.Trim().Split(' ');
var field = tokens[0];
var direction = tokens.Skip(1).Single().ToLower();
var prop = typeof(T).GetProperty(field);
return direction == "desc"
? sequence.OrderByDescending(m => prop.GetValue(m, null))
: sequence.OrderBy(m => prop.GetValue(m, null));
}
}
It will make a very simplified parsing of the sort order. It puts the responsibility on the calling party which is generally not what you want to do, so you might want some error handling in case the sortorder string does not fulfill the requirements.
from the sortorder string it fetches a name used to identify a property which can be used to fetch the value used for sorting.
you can use it like this:
db.TabMasters.SortByField(sortOrder)
EDIT based on comment:
The line typeof(T).GetProperty(field) is fragile in the absence of any error handling. It relies on the first token to be a name of a public property of the type T. It will return null if the name doesn't match a property. Including if it matches a Field name. A similar function exist for getting a FieldInfo
prop.GetField(field) will return a fieldinfo object of there's a public field with the given name otherwise null. To get the value of a field simply omit the last parameter to the GetValue call.
You should take a look at Linq.DynamicQuery.
There's more info in this blogpost http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
The library lets you write following code:
var query = northwind.Products
.Where("CategoryID = 3 AND UnitPrice > 3")
.OrderBy("SupplierID");
instead of
var query = from p in northwind.Products
where p.CategoryID == 3 && p.UnitPrice > 3
orderby p.SupplierID
select p;
If you want to add the sortdirection:
var query = northwind.Products.OrderBy("SupplierID Descending");
Here is the code snippet, actually the whole method. This method works f,ine when NULLAblE Foreign Key Refernces has value. When the value is not there, then this method does not work. My idea is to get all the records even if the references column is NULL. Here is the code :
public List<PostedJob> GetPostedJobs(int startingIndex, int maximumRows)
{
using (var records = new CommonEvent())
{
var resultSet =
from r in records.ProjectPosts
join rr in records.Categories on r.Category_FK equals rr.ID
join al in records.ApplyForLimits on r.ApplyForLimit_FK
equals al.Id
//from uImage in
// records.UploadedFiles
// .Where(uu=>uu.Id == r.UploadedFileInfo_FK
// || r.UploadedFileInfo_FK == null).DefaultIfEmpty()
join a in records.UploadedFiles on r.UploadedFileInfo_FK
equals a.Id into something
from uImage in something.DefaultIfEmpty()
orderby r.PostId
select new Models.PostedJob
{
ApplyForLimitName = al.Name,
ProjectTitle = r.ProjectTitle,
ProjectDescription = r.ProjectDescription,
ProjectSummaryDescription = r.ProjectSummaryDescription,
SkillsRequirements = r.SkillsRequirements,
CategoryName = rr.CategoryName,
UploadedFileID = (int) r.UploadedFileInfo_FK,
UploadedFileInformation = uImage == null ?
new Models.UploadedFile
{
fileContents = new byte [] { (byte) 0},
FileExtension = string.Empty,
FileName = string.Empty,
FileSize = 0,
UploadedDate = DateTime.Now
}
:
new Models.UploadedFile
{
fileContents = uImage.FileContents,
FileExtension = uImage.FileExtension,
FileName = uImage.FileName,
FileSize = uImage.FileSize,
UploadedDate = DateTime.Now
}
};
return resultSet.Skip(startingIndex).Take(maximumRows).ToList();
}
Thank you for any suggestions or ideas on how to proceed . I am using .NET 4.0
Can you not use the Associations generated for you?
var a = records
.ProjectPosts
.Select(
projectPost =>
new Models.PostedJob()
{
ProjectTitle = projectPost.ProjectTitle,
CategoryName = projectPost.Category.CategoryName,
});
Something along those lines?
EDIT: And just add Null checks when the FK may fail
example:
CategoryName = projectPost.Category == null ? String.Empty : projectPost.Category.CategoryName,
How can I project the row number onto the linq query result set.
Instead of say:
field1, field2, field3
field1, field2, field3
I would like:
1, field1, field2, field3
2, field1, field2, field3
Here is my attempt at this:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank=i++,
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
Unfortunately, the "Rank=i++" line throws the following compile-time exception:
"An expression tree may not contain an assignment operator"
Well, the easiest way would be to do it at the client side rather than the database side, and use the overload of Select which provides an index as well:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new
{
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.AsEnumerable() // Client-side from here on
.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index + 1;
})
.ToList();
}
}
Ok, that did the trick. Thanks.
Here is my final code...
Server:
public List<Score> GetHighScores(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select s;
return query.ToList<Score>();
}
}
Client:
void hsc_LoadHighScoreCompleted(object sender, GetHighScoreCompletedEventArgs e)
{
ObservableCollection<Score> list = e.Result;
_listBox.ItemsSource = list.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index+=1
}).ToList();
}
You could also make just a slight adjustment to your original code to get it working. Word of caution, if you databind or access the object again, the Rank will increment each time. In those cases the top answer is better.
let Rank = i++
and
Rank.ToString()
Full code:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
let Rank = i++
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank.ToString(),
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
This solution worked for me.
http://www.dotnetfunda.com/articles/article1995-rownumber-simulation-in-linq.aspx
.Select((x, index) => new
{
SequentialNumber = index + 1
,FieldFoo = x.FieldFoo
}).ToList();
List<Emp> Lstemp = GetEmpList();
int Srno = 0;
var columns = from t in Lstemp
orderby t.Name
select new {
Row_number=++Srno,
EmpID = t.ID,
Name = t.Name,
City = t.City
};