None of the many questions on this topic seem to match my situation. I have a large data model. In certain cases, only a few of the fields need be displayed on the UI, so for those I replaced the LINQ to Entity query that pulls in everything with an Entity SQL query retrieving only the columns needed, using a Type constructor so that I got an entity returned and not a DbDataRecord, like this:
SELECT VALUE MyModelNameSpace.INCIDENT(incident.FieldA, incident.FieldB, ...) FROM ... AS ...
This works and displays the fields in the UI. And if I make a change, the change makes it back to the entity model when I tab out of the UI element. But when I do a SaveChanges, the changes do not get persisted to the database. No errors show up in the Log. Now if I very carefully replace the above query with an Entity Sql query that retrieves the entire entity, like this:
SELECT VALUE incident FROM MyDB.INCIDENTs AS incident...
Changes do get persisted in the database! So as a test, I created another query like the first that named every column in the entity, which should be the exact equivalent of the second Entity SQL query. Yet it did not persist changes to the database either.
I've tried setting the MergeOption on the returned result to PreserveChanges, to start tracking, like this:
incidents.MergeOption = MergeOption.PreserveChanges;
But that has no effect. But really, if retrieving the entire entity with Entity Sql persists changes, what logical purpose would there be for behaving differently when a subset of the fields are retrieved? I'm wondering if this is a bug?
Gert was correct, the problem was that the entity was not attached. Dank U wel, Gert! Ik was ervan verbluft!
I just wanted to add a little detail to show the full solution. Basically, the ObjectContext has an Attach method, so you'd think that would be it. However, when your Entity SQL select statement names columns, and you create the object using a Type as I did, the EntityKey is not created, and ObjectContext.Attach fails. After trying and failing to insert the EntityKey I created myself, I stumbled across ObjectSet.Attach, added in Entity Framework 4. Instead of failing, it creates the EntityKey if it is missing. Nice touch.
The code was (this can probably be done in fewer steps, but I know this works):
var QueryString = "SELECT VALUE RunTimeUIDesigner.INCIDENT (incident.INCIDENT_NBR,incident.LOCATION,etc"
ObjectQuery<INCIDENT> incidents = orbcadDB.CreateQuery<INCIDENT>(QueryString);
incidents.MergeOption = MergeOption.PreserveChanges;
List<INCIDENT> retrievedIncidents = incidents.ToList<INCIDENT>();
orbcadDB.INCIDENTs.Attach(retrievedIncidents[0]);
iNCIDENTsViewSource.Source = retrievedIncidents;
Related
I am new to Breeze.js, but really enjoy it so far. I ran into an issue with updating a database with Breeze.js, when selecting only portion of columns of a model.
When I ran this statement:
$scope.emFac.entityQuery.from('Company');
the company entity matches my EF entity, retrieves all columns, creates entityAspect, and all is working fine when updating database:
However, when I retrieve only portion of corresponding Model's columns, Breeze.js returns anonymous object with specified properties (retrieving data works, but not updating does not), without the entityAspect, which is being used for tracking changes.
Here is the code with select statement:
$scope.emFac.entityQuery.from('Company').select('companyId, displayName');
Is there a way to retrieve only some columns of EF Model columns, and still track changes with Breeze.js, needed for database updates?
As you've discovered, Breeze treats the incoming data as plain objects instead of entities when you use select.
Your choices are:
On the server, Create a CustomerLite or similar object, and have a server endpoint that returns those without the need for select; OR
On the client, get the results from the query and create entities from each object, with status Unchanged
Example of #2:
var entities = [];
em.executeQuery(customerProjectionQuery).then(queryResult => {
queryResult.results.forEach(obj => {
// obj contains values to initialize entity
var entity = em.createEntity(Customer.prototype.entityType, obj, EntityState.Unchanged);
entities.push(entity);
});
})
Either way, you will need to ensure that your saveChanges endpoint on the server can handle saving the truncated Customer objects without wiping out the other fields.
I have a legacy database with a particular table -- I will call it ItemTable -- that can have billions of rows of data. To overcome database restrictions, we have decided to split the table into "silos" whenever the number of rows reaches 100,000,000. So, ItemTable will exist, then a procedure will run in the middle of the night to check the number of rows. If numberOfRows is > 100,000,000 then silo1_ItemTable will be created. Any Items added to the database from now on will be added to silo1_ItemTable (until it grows to big, then silo2_ItemTable will exist...)
ItemTable and silo1_ItemTable can be mapped to the same Item entity because the table structures are identical, but I am not sure how to set this mapping up at runtime, or how to specify the table name for my queries. All inserts should be added to the latest siloX_ItemTable, and all Reads should be from a specified siloX_ItemTable.
I have a separate siloTracker table that will give me the table name to insert/read the data from, but I am not sure how I can use this with entity framework...
Thoughts?
You could try to use the Entity Inheritance to get this. So you have a base class which has all the fields mapped to ItemTable and then you have descendant classes that inherit from ItemTable entity and is mapped to the silo tables in the db. Every time you create a new silo you create a new entity mapped to that silo table.
[Table("ItemTable")]
public class Item
{
//All the fields in the table goes here
}
[Table("silo1_ItemTable")]
public class Silo1Item : Item
{
}
[Table("silo2_ItemTable")]
public class Silo2Item : Item
{
}
You can find more information on this here
Other option is to create a view that creates a union of all those table and map your entity to that view.
As mentioned in my comment, to solve this problem I am using the SQLQuery method that is exposed by DBSet. Since all my item tables have the exact same schema, I can use the SQLQuery to define my own query and I can pass in the name of the table to the query. Tested on my system and it is working well.
See this link for an explanation of running raw queries with entity framework:
EF raw query documentation
If anyone has a better way to solve my question, please leave a comment.
[UPDATE]
I agree that stored procedures are also a great option, but for some reason my management is very resistant to make any changes to our database. It is easier for me (and our customers) to put the sql in code and acknowledge the fact that there is raw sql. At least I can hide it from the other layers rather easily.
[/UPDATE]
Possible solution for this problem may be using context initialization with DbCompiledModel param:
var builder = new DbModelBuilder(DbModelBuilderVersion.V6_0);
builder.Configurations.Add(new EntityTypeConfiguration<EntityName>());
builder.Entity<EntityName>().ToTable("TableNameDefinedInRuntime");
var dynamicContext = new MyDbContext(builder.Build(context.Database.Connection).Compile());
For some reason in EF6 it fails on second table request, but mapping inside context looks correct on the moment of execution.
I have a SalesOrder table with columns for ID and OrderID. ID is an auto-generated int. OrderID is a non-nullable string with a max length of 20, and we use it to store the customer's order number for reference.
After adding my new SalesOrder and calling SaveChanges, I get the following error:
Cannot insert the value NULL into column 'OrderID', table 'SalesOrder'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Problem is, the object that I'm saving actually does have an OrderID! It's almost like it's trying to save the entity first before it saves all the values. Is this how EF handles things?
My setup is EF4.1, using an EDMX model-first approach. StoreGeneratedPattern is set to None. Default Value is currently set to (None) but I've tried various values. Entity Key is False, since it's not part of the key. I've also tried deleting the SalesOrder entity and regenerating it from the database.
I would also like to see your code...I had similar problems when filling objects in a loop then saving them with savechanges. I thought all the fields were populated, but they were not.
I'd have to see your code that executes before the save changes before I can offer anything really helpful.
If your problem is like mine and you are calling savechanges after using an iterator to populate your objects, then you can find the bad data by moving savechanges into the iterator so that it is called with each iteration...but this is all hypothetical guesswork without seeing your code...
I'm building an ASP.NET MVC site using the ADO.NET Entity Framework.
I have an entity model that includes these entities, associated by foreign keys:
Report(ID, Date, Heading, Report_Type_ID, etc.)
SubReport(ID, ReportText, etc.) - one-to-one relationship with Report.
ReportSource(ID, Name, Description) - one-to-many relationship with Sub_Report.
ReportSourceType(ID, Name, Description) - one-to-many relationship with ReportSource.
Contact (ID, Name, Address, etc.) - one-to-one relationship with Report_Source.
There is a Create.aspx page for each type of SubReport. The post event method returns a new Sub_Report entity.
Before, in my post method, I followed this process:
Set the properties for a new Report entity from the page's fields.
Set the SubReport entity's specific properties from the page's fields.
Set the SubReport entity's Report to the new Report entity created in 1.
Given an ID provided by the page, look up the ReportSource and set the Sub_Report entity's ReportSource to the found entity.
SaveChanges.
This workflow succeeded just fine for a couple of weeks. Then last week something changed and it doesn't work any more. Now instead of the save operation, I get this Exception:
UpdateException: "Entities in 'DIR2_5Entities.ReportSourceSet'
participate in the 'FK_ReportSources_ReportSourceTypes' relationship.
0 related 'ReportSourceTypes' were found. 1 'Report_Source_Types' is expected."
The debug visualizer shows the following:
The SubReport's ReportSource is set and loaded, and all of its properties are correct.
The Report_Source has a valid ReportSourceType entity attached.
In SQL Profiler the prepared SQL statement looks OK. Can anybody point me to what obvious thing I'm missing?
TIA
Notes:
The Report and SubReport are always new entities in this case.
The Report entity contains properties common to many types of reports and is used for generic queries. SubReports are specific reports with extra parameters varying by type. There is actually a different entity set for each type of SubReport, but this question applies to all of them, so I use SubReport as a simplified example.
I realise I'm late to this, but I had a similar problem and I hacked through it for about 3 hours before I came up with a solution. I'd post code, but it's at home - I can do it later if someone needs it.
Here are some things to check:
Set a breakpoint on the SaveChanges() call and examine the object context in depth. You should see a list of additions and changes to the context. When I first looked, I found that it was trying to add all my related objects rather than just point to them. In your case, the context might be trying to add a new Report_Source_Type.
Related to the previous point, but if you're retrieving the report source, make sure it is being retrieved from the database by its entity key and properly attached to the context. If not, your context might believe it to be a new item and therefore its required relationships won't be set.
From memory, I retrieved my references using the context.GetObjectByKey method, and then explicitly attached those objects to the context using the context.Attach method before assigning them to the properties of my original object.
I got this error because the table didn't have a primary key, it had a FK reference, but no PK.
After adding a PK and updating the model all is well.
Check if your ReportSource was loaded with the NoTracking option or if its EntityState == 'Detached'. If so, that is your problem, it must be loaded in the context.
This tends to happen if your database tables have a 1 - 1 relationship with each other. In your example reportsourceset expects a reportsorttypes with whatever id it is referencing. I have run into this problem when my relationship is linking two primary keys from opposite tables together.
I've got the same error because of new object instance which created "behind the scene" in "Added" state. This was not obvious.
I got this error when I added the new entity to the context but forgot to add the new entity to its parent's collection in the object graph.
For example:
Pet pet = new Pet();
context.Pets.Add(pet);
// forgot this: petOwner.Pets.Add(pet);
Why would the database be hit to find a record that is already represented in the ObjectContext?
So here's what I thought would happen when you query:
SiteUser someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0];
Junk example but the idea is that I want to get a user from the table with the id of 1. Now assume this is the first time through so what I would guess is that it has to create the SiteUser list on the context, query the database, and then fill it's list. Using profiler I see this:
SELECT
[Extent1].[UserID] AS [UserID],
[Extent1].[MainEmail] AS [MainEmail],
[Extent1].[Password] AS [Password],
[Extent1].[UserName] AS [UserName]
FROM [TIDBA].[TI_USER] AS [Extent1]
WHERE 1 = [Extent1].[UserID]
Beautiful. It did what I expect and in the SiteUser list (if I dig far enough using Watch) I can see that there is one item in the context SiteUser list and it happens to be the hydrated object that represents this data row.
Next I want to change something without saving:
someUser.UserName = "HIHIHI";
Now say for some reason I want grab it again Using the same context (This is a weird example but it's actually a test so I could prove this happening) :
someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0];
What I think would happen is it would look at the SiteUser list on the context since that's what the generated property says. (If not null, return list) Then it would look to see if it's there and return it. No database hit. Guess what profiler says...
SELECT
[Extent1].[UserID] AS [UserID],
[Extent1].[MainEmail] AS [MainEmail],
[Extent1].[Password] AS [Password],
[Extent1].[UserName] AS [UserName]
FROM [TIDBA].[TI_USER] AS [Extent1]
WHERE 1 = [Extent1].[UserID]
Hrm. Ok so I start thinking that maybe it's a gut check to see if anything has changed on that data item and update the SiteUser object ONLY on values I haven't changed on the client. (Sort of like context.Refresh(RefreshMode.ClientWins, context.SiteUser) ) So I have it stopped at the :
someUser = context.SiteUser.Where(role => role.UserID == 1).ToList()[0];
Line and I change a value in the database (Password column) and let it hit the database. Nothing changed on the object.
Something doesn't seem right here. It hits the database to select the object I already have hydrated in the context yet it doesn't apply the change I manually made in the database. Why is it hitting the database at all then?
UPDATE
Thanks to some links below, I was able to dig in a bit and find this:
Merge Option
Looks like there is an enumeration that is set to tell how to deal with loads. Now after reading that I saw this for MergeOption.AppendOnly:
Objects that already exist in the
object context are not loaded from the
data source. This is the default
behavior for queries or when calling
the Load method on an
EntityCollection<(Of <(TEntity>)>).
This would suggest that if I have it in the context, there should be no hit to the database. However, this doesn't seem to be true. It would make sense if OverwriteChanges or PreserveChanges were the defaults, but they are not. This seems to be contradictory to what is supposed to happen. Only thing I can think of is that "loaded" just means there are no overwrites. However, it doesn't mean there are no queries to the database.
context.SiteUser is an property of type ObjectQuery. When you execute an ObjectQuery, it will always hit the backing store. That's what they do. If you don't want to execute a database query, then don't use an ObjectQuery.
It sounds like what you really want is a function which says, "If the entity is already materialized in the context, then just return it. If it isn't, then go grab it from the database." As it happens, ObjectContext includes such a function, called GetObjectByKey
GetObjectByKey tries to retrieve an
object that has the specified
EntityKey from the ObjectStateManager.
If the object is currently not loaded
into the object context, a query is
executed in an attempt to return the
object from the data source.
IMO, the reason that EF hits the database a second time is to make sure that there aren't any additional rows in the db that satisfy the query. It's possible that additional relevant rows have been inserted into the table since the first query was issued, and EF is seeing if any of those exist.
If I understand your question, this should answer it:
Actually, the way the entity framework
does this by default is to require
notifications of changes from the
entity objects to a framework class
called the state manager which then
keeps track of which properties have
been changed. The original values are
copied only on demand. When updates
happen, those original values are used
in talking to the server only if the
changed properties are marked as
“concurrency tokens”. That is, for
any concurrency token columns, when
the framework is creating an update
statement it will include a check to
verify that the row in the database
still has the original value, and if
not it will raise an exception to
notify the program that someone else
has changed the row in the database.
It’s also true that the entity
framework doesn’t absolutely require
notifications from the property
setters, you can also determine what’s
modified in the application code and
call an explicit method on the
framework to indicate which properties
are changed (but then the framework
will only have a record that the
property is modified, it won’t have an
original value).
Which comes from here. More can be read about it here, and here.
Edited to add:
It appears that with EF, there is an ObjectStateManager that tracks changes which never really allows for disconnected data. In order to have disconnected data, you'll have to call the ObjectContext.Detach method to disconnect your object. More can be found here and here.
What if you avoided the .ToList() and use .FirstOrDefault() ?