I am using ASP.NET DynamicData (based on LINQ to SQL) on my site for basic scaffolding. On one table I have added additional properties, that are not stored in the table, but are retrieved from somewhere else. (Profile information for a user account, in this case).
They are displayed just fine, but when editing these values and pressing "Update", they are not changed.
Here's what the properties look like, the table is the standard aspnet_Users table:
public String Address
{
get
{
UserProfile profile = UserProfile.GetUserProfile(UserName);
return profile.Address;
}
set
{
UserProfile profile = UserProfile.GetUserProfile(UserName);
profile.Address = value;
profile.Save();
}
}
When I fired up the debugger, I've noticed that for each update the set accessor is called three times. Once with the new value, but on a newly created instance of user, then once with the old value, again on an new instance, and finally with the old value on the existing instance.
Wondering a bit, I checked with the properties created by the designer, and they, too, are called three times in (almost) the same fashion. The only difference is, that the last call contains the new value for the property.
I am a bit stumped here. Why three times, and why are my new properties behaving differently? I'd be grateful for any help on that matter! =)
I observed something similar when I let Linq to SQL use stored procedures for inserting/updating. I am not sure if I remember correctly, but I think that Linq to SQL uses these three instances of the entity class to figure out what changed so that the required SQL statement can be built.
I see basically two options (though I am not sure if this really works):
You could probably store the extra field(s) in the "OnValidate" event of the entity.
You could overwrite the partial methods for inserting/updating. In that case you will also need to take care of storing the entity in the database (e.g. with stored procedure).
The property would look then like this:
private string address = null;
public string Address
{
get
{
if (this.address == null)
{
// Load on first use: This might make a problem...
UserProfile profile = UserProfile.GetUserProfile(UserName);
this.address = profile.Address;
}
return this.address;
}
set
{
this.address = value;
}
}
In both cases you have the problem that you might update the extra fields though the update of the rest of the entity fails. This was of course also a problem with your initial approach.
I think the best solution would be to implement your own profile provider and store the profile information in your own tables. If you do that you could let Linq to SQL create entities for your profile information: Everything would be "standard" and you would not have to resort to some kind of "hack"...
Related
I've shown three programmers this problem and we're all stumped. I call a Sql Server stored procedure in a foreach loop and the result always is the same as the first call. Even if I hard code parameters (removing the loop) only the first result is assigned to all subsequent calls.
The stored procedure is called by an Entity Framework function import (EF4 database first using the designer). The calling code lives in a repository that is a class library. The repository is called by a separate Asp.net webforms project. The problem code looks like this:
IEnumerable<WorkOrder> orders = _context.GetWorkOrders(UserName, workOrder, customerCode).ToList();
OrderStatus lastStatus = new OrderStatus();
foreach (Order order in orders)
{
lastStatus = _context.GetOrderStatus(order.OrderNumber).FirstOrDefault();
order.LastOrderStatus = lastStatus.OrderStatus;
}
As you can see this is pretty basic stuff. Depending on the order numbers passed in I always get the result of the first order number in the loop. I've turned off Ajax (part of the Telerik controls I use) because that has caused baffling errors for me in the past. I really hope you can suggest a way to debug this problem! Thanks in advance.
EDIT: Daniel J.G.'s comment led me to this possible solution. Now I need to figure out how to apply Ladislav Mrnka's answer..."Try to call ExecuteFunction directly with MergeOption.OverwriteChanges."
I'm answering my own question (since no one else has after a few days). The problem is caused by the Entity Framework database first designer. It generates code that caches the first stored procedure result causing the bad results in subsequent calls.
As I mentioned in the edit to my question the fix involves replacing the default MergeOption parameter used by ExecuteFunction. You need to use MergeOption.OverwriteChanges instead of the default (which I believe is MergeOption.PreserveChanges).
You could change that parameter in the generated code but your changes would be lost each time the designer is rebuilt. Instead I simply copied the generated code to my repository class, changed the MergeOption to OverwriteChanges, and stopped using the generated code. The end result looks like this:
IEnumerable<WorkOrder> orders = _context.GetWorkOrders(UserName, workOrder, customerCode).ToList();
OrderStatus lastStatus = new OrderStatus();
foreach (Order order in orders)
{
ObjectParameter workOrderParameter;
if (wo.WorkOrder != null)
{
workOrderParameter = new ObjectParameter("WorkOrder", order.WorkOrder);
}
else
{
workOrderParameter = new ObjectParameter("WorkOrder", typeof(global::System.String));
}
lastStatus = _context.ExecuteFunction<OrderStatus>("GetOrderStatus", MergeOption.OverwriteChanges, workOrderParameter).FirstOrDefault();
if (status != null)
{
order.LastOrderStatus = status.OrderStatus;
}
}
I also see that there is a way you can modify the T4 template to make the generated code use the correct MergeOption parameter. I haven't tried it though. If you're interested take a look here.
I'm back with a second answer to my own question. Be sure the Entity Key is truly a unique identifier for each Entity!
In my case, the OrderStock Entity was missing the OrderID (along with StockID) as the Entity Key. Typically the designer culls the primary key fields from the database but I have a unique situation (where my entity is based on a view). Since I left off OrderID from the Entity Key I saw duplicate rows for a single OrderStock Entity.
When I marked OrderID Entity Key = True the duplicate problem went away.
As I've mentioned in a couple other questions, I'm currently trying to replace a home-grown ORM with the Entity Framework, now that our database can support it.
Currently, we have certain objects set up such that they are mapped to a table in our internal database and a table in the database that runs our website (which is not even in the same state, let alone on the same server). So, for example:
Part p = new Part(12345);
p.Name = "Renamed part";
p.Update();
will update both the internal and the web databases simultaneously to reflect that the part with ID 12345 is now named "Renamed part". This logic only needs to go one direction (internal -> web) for the time being. We access the web database through a LINQ-to-SQL DBML and its objects.
I think my question has two parts, although it's possible I'm not asking the right question in the first place.
Is there any kind of "OnUpdate()" event/method that I can use to trigger validation of "Should this be pushed to the web?" and then do the pushing? If there isn't anything by default, is there any other way I can insert logic between .SaveChanges() and when it hits the database?
Is there any way that I can specify for each object which DBML object it maps to, and for each EF auto-generated property which property on the L2S object to map to? The names often match up, but not always so I can't rely on that. Alternatively, can I modify the L2S objects in a generic way so that they can populate themselves from the EF object?
Sounds like a job for Sql Server replication.
You don't need to inter-connect the two together as it seems you're saying with question 2.
Just have the two separate databases with their own EF or L2S models and abstract them away using repositories with domain objects.
This is the solution I ended up going with. Note that the implementation of IAdvantageWebTable is inherited from the existing base class, so nothing special needed to be done for EF-based classes, once the T4 template was modified to inherit correctly.
public partial class EntityContext
{
public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
var modified = this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added); // Get the list of things to update
var result = base.SaveChanges(options); // Call the base SaveChanges, which clears that list.
using (var context = new WebDataContext()) // This is the second database context.
{
foreach (var obj in modified)
{
var table = obj.Entity as IAdvantageWebTable;
if (table != null)
{
table.UpdateWeb(context); // This is IAdvantageWebTable.UpdateWeb(), which calls all the existing logic I've had in place for years.
}
}
context.SubmitChanges();
}
return result;
}
}
With the new release of Entity Framework 4.1, I thought it would be a good time to learn how to utilise it in my coding. I've started off well but I seem to have hit a brick wall and I dont know what the best approach is.
My issue is when using lookup tables, I cant see how to keep my data as objects (rather than lists, anonymous types etc) when pulling in data from a lookup table.
I have looked around on Google but most of the posts I find are prior to the latest release of EF 4.1 and I am assuming that there is a better way to do it.
I have a simple 'invoice header' and 'customer' situation so I have set the mappings up as you would expect (the invoice header has the Id of the customer it relates to).
If I pull in data from only the invoice table then I get a true object that I can bind in to a datagrid and later save changes but this doesnt pull in the customer name like this, for example:
var results = from c in context.InvoiceHeaders
select c;
If I restructure the query to pull back specific columns including drilling down in to the customer table and and getting the customer name directly then I get the data I want but it's now not a type of object that I would expect (invoice object), like this:
var results = from c in context.InvoiceHeaders
select new { c.CreatedBy, c.Customer.Name };
But it now becomes an anonymous type and it seems to lose its bindings back to the database (hope I'm making sense)
So - my question is, "what is the best/official way to use lookup tables in EF 4.1" and/or "can I use lookup tables and keep my bindings"?
Please let me know if you need me to post any code but on this occasion, as it was a general question, I didnt feel I needed to.
Thanks in advance,
James
EF classes are partial so you may expand them :
public partial class InvoiceHeaders
{
public string CustomerName
{
get
{
try
{
return this.Customer.Name;
}
catch
{
return string.Empty;
}
}
private set { }
}
}
But when designing forms, data binding tools does note correctly use this expansion, so you should define a new class and use this class as data source when bind a component to your data source:
public partial class InvoiceHeadersEx : InvoiceHeaders
{
}
and in form.load event change the binding datasource:
private void Form1_Load(object sender, EventArgs e)
{
InvoiceHeadersExDataGridView.DataSource = InvoiceHeadersSource;
InvoiceHeadersBindingSource.DataSource = context.InvoiceHeaders;
}
I think the answer to this is to make sure you're using reference objects (I think that's what EF calls them) in your structure. So that an Invoice doesn't just have public int ClientId {get; set;} but also has public virtual Client Client {get; set;} This gives you a direct link to the actual client- and should still return Invoice objects.
Oh, I get the problem now. When you create an anonymous type, it's basically a new class (it has a type definition and everything). Because it's a new type, which you have control over, it's not an EF data type or linked to a data context.
You're best bet is returning the entire customer object. I appreciate this can cause performance issues when you have large objects, all I can say is, keep your objects smallish.
I'm playing around with NHibernate 3.0. So far things are pretty cool. I'm trying to attach an entity that wasn't detached previously:
var post = new Post(){ Id = 2 };
session.Update(post); // Thought this would work but it doesn't.
post.Title = "New Title After Update";
session.Flush();
What I'm trying to write is the following:
var post = new Post(){ Id = 2 };
session.Attach(post);
post.Title = "New Title After Update";
session.Flush(); // Sql should be something like: UPDATE Post SET Title='New Title After Update' WHERE Id=2
Is this possible so that only Title gets updated? This is currently possible in EntityFramework. I'd like to not have to load Post from the database when I just need to update a few properties. Also, I'm trying to avoid a method call that would create the object... since it's moving away from an object oriented approach in my opinion.
EDIT: I know about using transactions, I just used Flush() to make the code simple. Ok so I think we're sort of getting on the right track for what I'm trying to achieve. I'd like to be able to create an entity with a known Id using the constructor, like I have in the 2nd code block above. I don't want to have to make a call to Get<T> or Load<T> since it feels rather wrong constructing objects like this that already exist in the database. For example, in Entity Framework I can write the 2nd code example and it will "just work". It only updates the Title property.
You can session.Save() or session.SaveOrUpdate()
update
Okay, I think I see now what you are trying to do. You are trying to update a single property on a Post that was previously persisted, not a new Post, and to do that you're instantiating a new Post and giving it the Id of one in the database.
I'm not sure what you mean when you say you're trying to avoid a method call that would create the object, but the way to do this with NHibernate is this:
var post = session.Load<Post>(2);
post.Title = "New Title";
session.SaveOrUpdate(post);
In general, you should not be calling Flush() on your sessions.
The important thing to note here is the use of session.Load. Calling Load with an id in and of itself does not load the entity from the database. The entity's property values will only be loaded when/if you access them.
Of course, in this scenario, I believe that NHibernate will load the properties for the Post, (but not collections unless you've specified an eager fetch mode), and that makes sense (frankly, I don't understand why EF would not load the entity). What if the setter for your Title property does something important, like check it against the existing title, validate the title's length, check your credentials, or update another property? Simply sending an UPDATE to the database isn't sufficient.
It's possible to only update changed properties by setting dynamic-update in the mapping. However, as far as I know, it is not possible (without reverting to SQL) to perform an update without retrieving the object from the database at some point.
Use the Merge method. You have to create a new instance variable to accept the attached entity = nhibernate will not do anything else with your detached instance.
var post = new Post(){ Id = 2 };
post.Title = "New Title After Update";
// Must create a new instance to hold final attached entity
var attachedPost = session.Merge(post);
session.Update(attachedPost);
session.Flush();
// Use attachedPost after this if still needed as in session entity
That covers the "attach" functionality you are looking for, but I don't see how you are going to be able to only update the one property. if the object instance has not been populated from the database, the properties will be different. Dynamic mapping will not solve this - NHibernate sees the properties as "updated" to a bunch of nulls, empty strings.
Gotta say, you are creating a new instance but what you are actually doing is updating an existing instance. You are working directly with IDs not objects. And you are setting a single property and now have an instance potentially hanging around and doing more things but it has not enforced any invariants and may in fact bear no resemblence to the real deal other than the id property...
It all feels pretty anti-object oriented to me personally.
Problem
I got two related tables, with a foreign key that doesn't enforce FK contraints. The "child" table should be allowed to specify a "parent" key, which is either NULL or non-existing ID.
My data access is built using Linq2Sql and it generates a one-to-many relation in the model of my tables.
The problem comes in some cleanup code, which will daily look through all the changes for the last day and fix any "errors" in the data.
Example
foreach (var user in data.Users)
{
// Check if the user has a specified office, which does not exists.
if (user.OfficeId != null && user.Office == null)
{
user.OfficeId = null; // This throws exception
}
}
Exception: System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException: Operation is not valid due to the current state of the object
This is normal behavior and somewhat expected, so I tried to set the reference object instead. But how do I define an "empty" Office?
var emptyOffice = new Office();
user.Office = emptyOffice;
The above code sets the OfficeId to NULL on the user instance, but obviously it fails during update as it tries to add a new "empty" Office to the database.
Possible solution
What I'm left with is running my own SQL which does the update, though it would be nice to actually be able to do this through Linq2Sql as there are other columns on the same row I'm updating when the related "parent" is non-existing.
Notes
There are some requirements here that is important for any comments:
The database cannot be changed.
Multiple systems is dependent on the same database schema.
Can potentially change to Entity Framework, if it supports such a scenario.
Running a custom SQL is not a problem and I'll be doing that until I find a better solution.
Would like to learn and know how to do this without any special code.
My code has FKs enforced, so I haven't tested this, but have you tried explicitly setting
user.Office = null;
That should force the FK value OfficeId to null, too.
If that doesn't work, then it the following certainly should:
user.Office = new Office(); // or keep a cached dummy Office object to save time
user.Office = null;
That will force the FK reference to realize that its value has been changed and set to null.