ObjectContext's SavingChanges: find out which columns have changed? - entity-framework-4

I'm handling ObjectContext's SavingChanges event to timestamp entries. The requirement is that, if only ColumnA has changed, I don't timestamp the entry when it changes.
Is there a way that I can find out which columns have changed (are changing) during this event?

This should work for you, this will loop through any Added/Modified entries, and if there is more than 1 modified property, and it's not "ColumnA" it you can modify the timestamp:
public int SaveChanges()
{
foreach( ObjectStateEntry entry in ObjectStateManager.GetObjectStateEntries( EntityState.Added | EntityState.Modified ) )
{
var properties = entry.GetModifiedProperties();
if (!(properties.Count() == 1 && properties.First() == "ColumnA"))
{
//modify timestamp here
}
}
return base.SaveChanges();
}

Related

Generic Linked Lists find duplicates

Good day all,
I am studying Bsc-IT but am having problems.
With the current covid-19 situation we have been left to basically self-study and I need someone to put me in the right direction (not give me the answer) with my code.
I must write a program called appearsTwice that receives a linked list as parameter and return another list containing all the items from the parameter list that appears twice or more in the calling list.
My code so far(am I thinking in the right direction? What must I look at?)
public MyLinkedList appearsTwice(MyLinkedList paramList)
{
MyLinkedList<E> returnList = new MyLinkedList<E>(); // create an empty list
Node<E> ptrThis = this.head;//Used to traverse calling list
Node<E> ptrParam= paramList.head; //neither lists are empty
if (paramList.head == null) // parameter list is empty
return returnList;
if (ptrThis == null) //Calling list is empty
return returnList;
for (ptrThis = head; ptrThis != null; ptrThis = ptrThis.next)
{
if (ptrThis == ptrThis.element)
{
returnList.append(ptrThis.element);
}
}
Some issues:
Your code never iterates through the parameter list. The only node that is visited is its head node. You'll need to iterate over the parameter list for every value found in the calling list (assuming you are not allowed to use other data structures like hashsets).
if (ptrThis == ptrThis.element) makes little sense: it tries to compare a node with a node value. In practice this will never be true, nor is it useful. You want to compare ptrParam.element with ptrThis.element, provided that you have an iteration where ptrParam moves along the parameter list.
There is no return statement after the for loop...
You need a counter to address the requirement that a match must occur at least twice.
Here is some code you could use:
class MyLinkedList {
public MyLinkedList appearsTwice(MyLinkedList paramList) {
MyLinkedList<E> returnList = new MyLinkedList<E>();
if (paramList.head == null) return returnList; // shortcut
for (Node<E> ptrParam = paramList.head; ptrParam != null; ptrParam = ptrParam.next) {
// For each node in the param list, count the number of occurrences,
// starting from 0
int count = 0;
for (Node<E> ptrThis = head; ptrThis != null; ptrThis = ptrThis.next) {
// compare elements from both nodes
if (ptrThis.element == ptrParam.element) {
count++;
if (count >= 2) {
returnList.append(ptrParam.element);
// no need to look further in the calling list
// for this param element
break;
}
}
}
}
return returnList; // always return the return list
}
}

how to convert var data to int type?

I have "Products table" with following fields
PID int,
product_name varchar(),
product_price int
"Cart table" with following fields
cart_ID
user_id
PID
So I want to display cart items of logged in user
For example if user_ID=100 is logged in , then only his cart items should be displayed to him, with all the product details.
Am using asp.net with entity framework
public ActionResult Cart()
{
Products pro = new Products();
Cart cart =new Cart();
var productID=db.cartDetails.Where(pid=>pid.productId==cart.productId && cart.user_id==session["user_ID"]);
return View(db.productsDetails.Where(pid => pid.productId == productID));
}
Now problem arise, "ProductID" being var type i cannot compare it with "pid=>pid.productid".
How to store productid in int List, so that i can compare it with individual productid from product table to show product details?
try to convert it before check
var productID=int.Parse(db.cartDetails.Where(pid=>pid.productId==cart.productId).ProductId);
return View(db.productsDetails.Where(pid => pid.productId == productID));
EDIT
db.cartDetails.Where(pid=>pid.productId==cart.productId
this will return object of product or something so need to change your code in to
var product = db.cartDetails.Where(pid=>pid.productId==cart.productId).FirstOrDefault();
if(product!= null){
return View(db.productsDetails.Where(pid => pid.productId == product.productID));
}
db.cartDetails.Where(pid=>pid.productId==cart.productId) returns a `WhereListIterator<CartDetail>` object.
So you'll need to to do something like -
var productID=db.cartDetails.Where(pid=>pid.productId==cart.productId).FirstOrDefault();
return View(db.productsDetails.Where(pid => pid.productId == productID.productID));
You can try this
Edit :
First thing you can not compare directly session["user_ID"] with int datatype field as you are doing in you comment cart.user_id==session["user_ID"] follow updated code.
var userID = Convert.ToInt32(Session["user_ID"]); //And please check first session is null or not than go ahead.
//Here as you said now you can compare productID and userId as follow.
//And if these both conditions will be satisfied than you will get cartDetails table first record otherwise it will return null value
var cartDetail = db.cartDetails.Where(pid => pid.productId == cart.productId && pid.user_id == userID).FirstOrDefault();
if(cartDetail != Null)
{
return View(db.productsDetails.Where(pid => pid.productId == cartDetail.productID));
}
FirstOrDefault() Returns the first element of a sequence, or a default value if the sequence contains no elements.
My First line code return you int productID if it is not null.

Groovy removeAll closure not removing objects in Set

I'm using data binding with parent/child relationships in Grails 2.3.7 and am having trouble with deletes. The form has many optional children, and to keep the database tidy I'd like to purge blank (null) values. I've found some nice articles which suggest using removeAll to filter my entries but I can't get remove or removeAll to work!
For example... (Parent has 10 children, 5 are blank)
def update(Parent parent) {
parent.children.getClass() // returns org.hibernate.collection.PersistentSet
parent.children.size() // returns 10
parent.children.findAll{ it.value == null }.size() // returns 5
parent.children.removeAll{ it.value == null } // returns TRUE
parent.children.size() // Still returns 10!!!
}
I've read PersistentSet is finicky about equals() and hashCode() being implemented manually, which I've done in every domain class. What baffles me is how removeAll can return true, indicating the Collection has changed, yet it hasn't. I've been stuck on this for a couple days now so any tips would be appreciated. Thanks.
Update:
I've been experimenting with the Child hashcode and that seems to be the culprit. If I make a bare-bones hashcode based on the id (bad practice) then removeAll works, but if I include the value it stops working again. For example...
// Sample 1: Works with removeAll
int hashCode() {
int hash1 = id.hashCode()
return hash1
}
// Sample 2: Doesn't work with removeAll
int hashCode() {
int hash1 = id.hashCode()
int hash2 = value == null ? 0 : value.hashCode()
return hash1 + hash2
}
// Sample Domain classes (thanks Burt)
class Parent {
static hasMany = [children: Child]
}
class Child {
String name
String value
static constraints = {
value nullable: true
}
}
This behavior is explained by the data binding step updating data, making it dirty. (ie: child.value.isDirty() == true) Here's how I understand it.
First Grails data binding fetches the Parent and children, and the hashcode of each Child is calculated. Next, data updates are applied which makes child.value dirty (if it changed) but the Set's hashcodes remain unchanged. When removeAll finds a match it builds a hashCode with the dirty data, but that hashcode is NOT found in the Set so it can't remove it. Essentially removeAll will only work if ALL of my hashCode variables are clean.
So if the data must be clean to remove it, one solution is to save it twice. Like this...
// Parent Controller
def update(Parent parent) {
parent.children.removeAll{ it.value == null } // Removes CLEAN children with no value
parent.save(flush:true)
parent.refresh() // parent.children is now clean
parent.children.removeAll{ it.value == null } // Removes (formerly dirty) children
parent.save(flush:true) // Success!
}
This works though it's not ideal. First I must allow null values in the database, though they only exist briefly and I don't want them. And second it's kinda inefficient to do two saves. Surely there must be a better way?
hashCode and equals weirdness aren't an issue here - there are no contains calls or something similar that would use the hashCode value and potentially miss the actual data. If you look at the implementation of removeAll you can see that it uses an Iterator to call your closure on every instance and remove any where the closure result is truthy, and return true if at least one was removed. Using this Parent class
class Parent {
static hasMany = [children: Child]
}
and this Child
class Child {
String name
String value
static constraints = {
value nullable: true
}
}
and this code to create test instances:
def parent = new Parent()
5.times {
parent.addToChildren(name: 'c' + it)
}
5.times {
parent.addToChildren(name: 'c2' + it, value: 'asd')
}
parent.save()
it prints 5 for the final size(). So there's probably something else affecting this. You shouldn't have to, but you can create your own removeAll that does the same thing, and if you throw in some println calls you might figure out what's up:
boolean removeAll(collection, Closure remove) {
boolean atLeastOne = false
Iterator iter = collection.iterator()
while (iter.hasNext()) {
def c = iter.next()
if (remove(c)) {
iter.remove()
atLeastOne = true
}
}
atLeastOne
}
Invoke this as
println removeAll(parent.children) { it.value == null }

MVC Enity Framework get KEY attribute from table

I am trying to extract the [key] value from a table.
This is for a logging method which looks like this:
private List<Log> GetAuditRecordsForChange(DbEntityEntry dbEntry, string userId)
{
List<Log> result = new List<Log>();
DateTime changeTime = DateTime.Now;
// Get the Table() attribute, if one exists
TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
// Get table name (if it has a Table attribute, use that, otherwise get the pluralized name)
string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
// Get primary key value
string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;
if (dbEntry.State == EntityState.Added)
{
result.Add(new Log()
{
LogID = Guid.NewGuid(),
EventType = "A", // Added
TableName = tableName,
RecordID = dbEntry.CurrentValues.GetValue<object>(keyName).ToString(),
ColumnName = "*ALL",
NewValue = (dbEntry.CurrentValues.ToObject() is IDescribableEntity) ? (dbEntry.CurrentValues.ToObject() as IDescribableEntity).Describe() : dbEntry.CurrentValues.ToObject().ToString(),
Created_by = userId,
Created_date = changeTime
}
);
}
The problem is to get the RecordID when a Record is added, when it get deleted or modified it works. (The code to get it is the same)
When I debug I also see that it has the KeyAttribute in the CustomAttributes base but not sure why it always shows up as 0.
I can debug more if needed
After savechanges you can fetch the newly created key. (Guess the key is generated automatically inserting a new record).
for me you have several solutions.
first solution:
enlist added entity from the context
SaveChanges
enumerate the enlisted entities to add log
SaveChanges
the problem (or not) here is that the business and the logging are not in the same transaction.
antother problem, depending on the implementation, is to prevent loging of log of log of log... This can be donne by filtering entities by typeName for example.
other solution:
add and ICollection<Log> to your entities.
the problem here is to unify the logs:
inheritance of entity, or
several log tables + a view
...
other solution
use trigger at database level
other solution
..., use cdc if you have Sql Server Enterprise Edition

The specified type member is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported

var result =
(from bd in context.tblBasicDetails
from pd in context.tblPersonalDetails.Where(x => x.UserId == bd.UserId).DefaultIfEmpty()
from opd in context.tblOtherPersonalDetails.Where(x => x.UserId == bd.UserId).DefaultIfEmpty()
select new clsProfileDate()
{
DOB = pd.DOB
});
foreach (clsProfileDate prod in result)
{
prod.dtDOB = !string.IsNullOrEmpty(prod.DOB) ? Convert.ToDateTime(prod.DOB) : DateTime.Today;
int now = int.Parse(DateTime.Today.ToString("yyyyMMdd"));
int dob = int.Parse(prod.dtDOB.ToString("yyyyMMdd"));
string dif = (now - dob).ToString();
string age = "0";
if (dif.Length > 4)
age = dif.Substring(0, dif.Length - 4);
prod.Age = Convert.ToInt32(age);
}
GetFinalResult(result);
protected void GetFinalResult(IQueryable<clsProfileDate> result)
{
int from;
bool bfrom = Int32.TryParse(ddlAgeFrom.SelectedValue, out from);
int to;
bool bto = Int32.TryParse(ddlAgeTo.SelectedValue, out to);
result = result.AsQueryable().Where(p => p.Age >= from);
}
Here I am getting an exception:
The specified type member "Age" is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties
are supported.
Where Age is not in database it is property I created in clsProfileDate class to calculate Age from DOB. Any solution to this?
You cannot use properties that are not mapped to a database column in a Where expression. You must build the expression based on mapped properties, like:
var date = DateTime.Now.AddYears(-from);
result = result.Where(p => date >= p.DOB);
// you don't need `AsQueryable()` here because result is an `IQueryable` anyway
As a replacement for your not mapped Age property you can extract this expression into a static method like so:
public class clsProfileDate
{
// ...
public DateTime DOB { get; set; } // property mapped to DB table column
public static Expression<Func<clsProfileDate, bool>> IsOlderThan(int age)
{
var date = DateTime.Now.AddYears(-age);
return p => date >= p.DOB;
}
}
And then use it this way:
result = result.Where(clsProfileDate.IsOlderThan(from));
A lot of people are going to say this is a bad answer because it is not best practice but you can also convert it to a List before your where.
result = result.ToList().Where(p => date >= p.DOB);
Slauma's answer is better, but this would work as well. This cost more because ToList() will execute the Query against the database and move the results into memory.
You will also get this error message when you accidentally forget to define a setter for a property.
For example:
public class Building
{
public string Description { get; }
}
var query =
from building in context.Buildings
select new
{
Desc = building.Description
};
int count = query.ToList();
The call to ToList will give the same error message. This one is a very subtle error and very hard to detect.
I forgot to select the column (or set/map the property to a column value):
IQueryable<SampleTable> queryable = from t in dbcontext.SampleTable
where ...
select new DataModel { Name = t.Name };
Calling queryable.OrderBy("Id") will throw exception, even though DataModel has property Id defined.
The correct query is:
IQueryable<SampleTable> queryable = from t in dbcontext.SampleTable
where ...
select new DataModel { Name = t.Name, Id = t.Id };
In my case, I was getting this error message only in Production but not when run locally, even though my application's binaries were identical.
In my application, I'm using a custom DbModelStore so that the runtime-generated EDMX is saved to disk and loaded from disk on startup (instead of regenerating it from scratch) to reduce application startup time - and due to a bug in my code I wasn't invalidating the EDMX file on-disk - so Production was using an older version of the EDMX file from disk that referenced an older version of my application's types from before I renamed the type-name in the exception error message.
Deleting the cache file and restarting the application fixed it.
Advanced answer:
Search in edmx file EntitySetMapping and check if the field is mapped to a column in database:
<EntitySetMapping Name="MY_TABLE">
<EntityTypeMapping TypeName="MYMODEL.MY_TABLE">
<MappingFragment StoreEntitySet="MY_TABLE">
<ScalarProperty Name="MY_COLUMN" ColumnName="MY_COLUMN_NAME" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
I was having this problem because the edmx had changes I didn't want and through git I discarded too many changes...
Checking Count() before the WHERE clause solved my problem. It is cheaper than ToList()
if (authUserList != null && _list.Count() > 0)
_list = _list.Where(l => authUserList.Contains(l.CreateUserId));

Resources