Using EF6 Framework4.5 – Creating my first n-tier app and first EF experience. I have CRUD working but one issue that I have a work-a-around but don’t like it. There must be a better way.
When data object is returned from my UI layer to the DAL layer, it has been detached, so I flag EntityState as “Modified.” But then it updates all columns in the db. Values that were not loaded in the form view (and not submitted) obviously are null and updated to such in the db.
1) My first solution does work:
Store the object in session in the UI layer and loop through the object updating edited values when the form is submitted. Thus, original values are passed back unchanged and updated to original values. I don’t think this would be best practice though.
2) The solution I think I want:
I am looking for a helper function in the DAL layer to loop through all values in the returned object and flag only non-null values as “IsModified” before calling SaveChanges.
I have found examples in C# on how to check for changed value but not null. (I am still a vb guy anyway. Don't hate.)
A) Is solution #2 a good way to do this?
B) Has anyone a piece of good to help me?
Thank you.
BTW, this is my best stab at it so far: (Errors on “CurrentValues”)
Public Overridable Function MarkEntriesModified(entity As Object)
Dim dbEntityEntry = DbContext.Entry(entity)
'Ensure only non-null values are inserted
For Each [property] In dbEntityEntry.CurrentValues.PropertyNames
If Not IsDBNull(dbEntityEntry.CurrentValues.GetValue(Of Object)([property])) Then
dbEntityEntry.[Property]([property]).IsModified = True
End If
Next
Return entity
Try this architecture. So if you are using EF then I suppose you have edmx updated and correctly representing your database objects.
eg: Say you wan to update Customer data in customer table
Create a Customer class.
when you extract a particular customer, get an instance of the
customer created.
pass this instance to the UI
and pass the instance back to Business layer to save
this way you don't loose anything in between.
some sample code
Related
I have a VB function which calls a stored procedure to get 2 kinds of reports (actives users/inactive users). The stored procedure returns the data (columns) based on the report we choose. If we choose to get active users report, the sproc doesn't return an Inactive_Date for the users, but it will return the same field, Inactive_Date field for Inactive users report. I'm getting an error when I choose to get active users report, because the sproc doesn't return Inactive_Date field, but my VB.NET code is same for both active/inactive users reports. Here are some of the ways I tried to resolve, but no luck.
If IsDBNull(dr("Inactive_Date")) Then
Result.Inactive_Date = Nothing
Else
Result.Inactive_Date = SafeStr(dr("Inactive_Date"))
End If
and
If IsDBNull(dr("Inactive_Date")) Then
Result.Inactive_Date = DateTime.Now
Else
Result.Inactive_Date = SafeStr(dr("Inactive_Date"))
End If
You're getting the exception because you're basically trying to access a dictionary entry that doesn't exist. You have a few options.
Have your stored procedure for Active Users return Inactive_Date as null. This would allow you to use code you have posted in VB with no changes.
Create a function that checks if the datareader has that column before you attempt to access it, as in this answer: Check for column name in a SqlDataReader object - this would be akin to checking if the dictionary has the key first
Write two separate functions in your VB code - one for the active report and one for the inactive report. Or, add a parameter to your function to check if this is a request for an active user, and only try to access the "Inactive_Date" field if that parameter is set to true.
If it were up to me, I'd update the stored procedure (option 1). That would probably be the least amount of work, shouldn't hurt anything semantically (unless you have queries that SELECT * from the results of that procedure), and requires no other changes to your codebase. The only other problem I can see is confusion that might arise from having a function in your VB code that's used for both active and inactive users.
The second option will involve iterating through all the fields, and that just feels wrong to me (seems like it's asking for performance issues if used incorrectly). It's the least invasive though if you can't change the stored proc.
The third option probably creates more problems elsewhere in your code base. That said, you probably shouldn't have one function do two very different things - I'd avoid the parameterized version if you end up going down this route, and go for two separate functions.
From your code and the fact that your variable is named dr, I am going to assume that you are using a DataReader.
The DataReader class exposes the column names as indexed values.
Dim columnNames = Enumerable.Range(0, dr.FieldCount).Select(dr.GetName).ToList()
Will call the method dr.GetName(index) for all columns, returning a list of column names you can check using columnNames.Contains("yourColumnName")
There is also dr.GetOrdinal, but that will throw an exception if the column doesn't exist.
I have an Stored Procedure which may return some unpredictable rows and columns, based on some business logic written in my SP. That is for sure that it will return only one set of Result Set data from this SP.
Whatever the result my SP returns, it should be reflected into my GridView. Due to some framework restrictions, I can not use ADO.Net DataSets/DataTables.
I am using Entity Framework 4.0 for my project and I want some solution to bind my GridView with dynamic nature of DataSource returned by my Stored procedure.
I think, this is very common problem which many developer has encountered in such type of situation.
Is there anyone who have found some work around to achieve the goal within the boundary line of Entity Framework?
Note: Keep in mind that, I don't want to use ADO.net DataSet or DataTable.
I think you are fetching data from proc and stored in List like that.
List<HRDocumentCheckList> searchJoiningDoc = GetJoiningDocumentForEdit(ddlCategory.SelectedValue.ToInt32(), false).ToList();
gvJoiningDocumentTemplate.DataSource = searchJoiningDoc;
gvJoiningDocumentTemplate.DataBind();
Just bind the gridview and set the autogenrated column is false
Hope it will helps you
I'm having a problem updating a disconnected POCO model in an ASP.NET application.
Lets say we have the following model:
Users
Districts
Orders
A user can be responsible for 0 or more districts, an order belongs to a district and a user can be the owner of an order.
When the user logs in the user and the related districts are loaded. Later the user loads an order, and sets himself as the owner of the order. The user(and related districts) and order(and related district) are loaded in two different calls with two different dbcontexts. When I save the order after the user has assigned himself to it. I get an exception that saying that acceptchanges cannot continue because the object's key values conflict with another object.
Which is not strange, since the same district can appear both in the list of districts the user is responsible and on the order.
I've searched high and low for a solution to this problem, but the answers I have found seems to be either:
Don't load the related entities of one of the objects in my case that would be the districts of the user.
Don't assign the user to the order by using the objects, just set the foreign key id on the order object.
Use nHibernate since it apparently handles it.
I tried 1 and that works, but I feel this is wrong because I then either have to load the user without it's districts before relating it to the order, or do a shallow clone. This is fine for this simple case here, but the problem is that in my case district might appear several more times in the graph. Also it seems pointless since I have the objects so why not let me connected them and update the graph. The reason I need the entire graph for the order, is that I need to display all the information to the user. So since I got all the objects why should I need to either reload or shallow clone it to get this to work?
I tried using STE but I ran in to the same problem, since I cannot attach an object to a graph loaded by another context. So I am back at square 1.
I would assume that this is a common problem in anything but tutorial code. Yet, I cannot seem to find any good solution to this. Which makes me think that either I do not under any circumstance understand using POCOs/EF or I suck at using google to find an answer to this problem.
I've bought both of the "Programming Entity Framework" books from O'Reilly by Julia Lerman but cannot seem to find anything to solve my problem in those books either.
Is there anyone out there who can shed some light on how to handle graphs where some objects might be repeated and not necessarily loaded from the same context.
The reason why EF does not allow to have two entities with the same key being attached to a context is that EF cannot know which one is "valid". For example: You could have two District objects in your object graph, both with a key Id = 1, but the two have different Name property values. Which one represents the data that have to be saved to the database?
Now, you could say that it doesn't matter if both objects haven't changed, you just want to attach them to a context in state Unchanged, maybe to establish a relationship to another entity. It is true in this special case that duplicates might not be a problem. But I think, it is simply too complex to deal with all situations and different states the objects could have to decide if duplicate objects are causing ambiguities or not.
Anyway, EF implements a strict identity mapping between object reference identity and key property values and just doesn't allow to have more than one entity with a given key attached to a context.
I don't think there is a general solution for this kind of problem. I can only add a few more ideas in addition to the solutions in your question:
Attach the User to the context you are loading the order in:
context.Users.Attach(user); // attaches user AND user.Districts
var order = context.Orders.Include("Districts")
.Single(o => o.Id == someOrderId);
// because the user's Districts are attached, no District with the same key
// will be loaded again, EF will use the already attached Districts to
// populate the order.Districts collection, thus avoiding duplicate Districts
order.Owner = user;
context.SaveChanges();
// it should work without exception
Attach only the entities to the context you need in order to perform a special update:
using (var context = new MyContext())
{
var order = new Order { Id = order.Id };
context.Orders.Attach(order);
var user = new User { Id = user.Id };
context.Users.Attach(user);
order.Owner = user;
context.SaveChanges();
}
This would be enough to update the Owner relationship. You would not need the whole object graph for this procedure, you only need the correct primary key values of the entities the relationship has to be created for. It doesn't work that easy of course if you have more changes to save or don't know what exactly could have been changed.
Don't attach the object graph to the context at all. Instead load new entities from the database that represent the object graph currently stored in the database. Then update the loaded graph with your detached object graph and save the changes applied to the loaded (=attached) graph. An example of this procedure is shown here. It is safe and a very general pattern (but not generic) but it can be very complex for complex object graphs.
Traverse the object graph and replace the duplicate objects by a unique one, for example just the first one with type and key you have found. You could build a dictionary of unique objects that you lookup to replace the duplicates. An example is here.
I have an application with user and admin sections. If an admin updates data with the help of sql datasource then it's updated the database. However, when we retrieve data with linq query then it's showing its old value rather than the updated value.
After some time, the linq query automatically shows the correct value.
I think its caching the value, but I find myself helpless. Please help me with this.
When you say
when we retrieve data with linq query
Do you mean you call your select methods again or are you using the current in memory objects?
In either case, you can always refresh an entity with :
Context.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, entity)
Make sure that you're using your DataContext efficiently (ideally one per unit of work).
After each update, make sure you call DataContext.SubmitChanges(); to commit your changes back to the database.
Also be aware that any context you instanciate between your changes being added to another context and calling SubmitChanges() will not reflect those changes.
Just dipping my toes into Linq2sql project after years of rolling my own SQL Server DB access routines.
Before I spend too much time figuring out how to make linq2sql behave like my custom code used to, I want to check to make sure that it isn't already "built" in behavior that I can just use by setting up the relationships right in the designer...
Very simple example:
I have two tables: Person and Notes, with a 1 to many relationship (1 Person, many notes), linked by Person.ID->Note.PersonID.
I have a stored procedure (all data access is done via SP's and I plan on continuing that) which makes the Link2SQL a bit more work for me.
sp_PersonGet(#ID int) which returns the person record and sp_PersonNotesGet(#PersonID) which returns a set of related notes for this person.
So far so good, I have an object:
Dim myPerson As Person = db.PersonGet(pnID).Single
and I can access my fields: myPerson.Name, myPerson.Phone etc.
and I can also do a
Dim myNotes As Notes = db.PersonNotesGet(pnID)
to get a set of notes and I can iterate thru this list like:
For Each N As Note In myNotes
( do something)
Next
This all works fine...BUT....What I would prefer is that if I call:
myPerson = db.PersonGet(pnID)
that I also end up with a myPerson.Notes collection that I can iterate thru.
For Each N As Note In myPerson.Notes
( do something)
Next
Basically, Linq2SQl would need to call 2 stored procedures each time a Person record is created...
Is this doable "out of the box", or is this something I need to code around for myself?
This is normally what we would call child collections and they can be eager loaded or lazy loaded. Read these:
http://davidhayden.com/blog/dave/archive/2009/01/08/QuickExamplesLINQToSQLPerformanceTuningPerformanceProfilingORMapper.aspx
http://www.thinqlinq.com/default/Fetching-child-records-using-Stored-Procedures-with-LINQ-to-SQL.aspx
It uses partial classes. You can add your own "Notes" property to your Person class and initialize it in it's GETter function. This would be better than populating the notes every time you load a person record.
I believe that you can do this more or less out of the box, although I haven't tried it -- I don't use stored procedures with LINQ. What you would need to do is change the Insert/Delete/Update methods from using the runtime to use your stored procedures. Then you'd create an Association between your two entity tables which would create an EntitySet of Notes on the Person class and a EntityRef of Person on the Notes class. You can set this up to load automatically or using lazy loading.
The only tricky bit, as far as I can see, is the change from using the runtime generated methods to using your stored procedures. I believe that you have to add them into the data context as methods (by dropping it on your table from the server explorer in the designer) before it is available to use instead.