Custom Code on Entity Lazy Load - c#

I've written a WPF desktop application, making use of Entity Framework to persist data (code first). I've got lazy loading enabled, which is working great.
For some of my entities, I need to implement some code every time it is instantiated through lazy loading. For example, I save some of my Datatables as lists in the database, and would like to convert these lists back to Datatables whenever the entity is lazy loaded.
I don't want to go through this conversion for the whole project necessarily (as not only can this be somewhat expensive, but for some projects the total objects can exceed available RAM). I also have all the 'custom' code that I'd like to run contained in one method. Lazy loading is therefore perfect, I just need to be able to run some custom code every time an entity is loaded.
So my question: is there any event (or other structure/pattern) that I can utilise or subscribe to so that I can run custom code every time an entity is initialised through lazy loading? I cannot use the parameterless constructor of said entity, as the properties haven't been loaded at that stage.

Thanks to the comments, I've implemented the necessary code in the appropriate get and set accessors. All properties are therefore being 'lazy loaded', which is in line with my requirements. My datatables therefore look as follows:
[NotMapped]
public DataTable MyTable
{
get
{
//Deserialize MyTable from byte[] or string property, obtained from database.
}
set
{
//Serialize MyTable to a byte[] or string property, which is saved to database.
}
}

Related

C# EF 6 CurrentValues.SetValues cannot change Object's Key Information

I have seen other questions about this same error, but I am unable to correct the error with those suggestions in my code; I think that this is a different problem and not a duplicate.
I have an app that makes a series of rules, of which the user can set properties in the GUI. There is a table of Rules in a connected database, with the primary key on the Rule.Id. When the user saves changes to a rule, the existing rule gets "IsActive=0" to hide it, then a new database record is made with the properties from the GUI written to the database. It looks to the user as though they have edited the rule, but the database actually sees a new rule reflecting the new properties (this allows for a history to be kept), connected to the old rule by another reference field.
In the C# code for the app, the View Model for each rule contains an EF Rule object property. When the user clicks "save" I use the parameters set in the view to build the ruleViewModel.Rule for each ruleViewModel they want to save, with porperties matching the GUI. The MainViewModel contains the DbContext object called dbo, so I use the ruleViewModel.Rule to write to the mainViewModel.dbo.Entry which I save to the Entity Framework. Here are the three basic steps performed for each saveable Rule View Model:
// get the rule from the GUI and use it to make sure we are updating the right rule in EF (which is connected to the mainViewModel)
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
// set the values in the EF item to be those we got from the GUI
ruleViewModel.MainViewModel.dbo.Entry(dboItem).CurrentValues.SetValues(ruleViewModel.Rule);
// Save the differences
ruleViewModel.MainViewModel.dbo.SaveChanges();
If the user only saves a single rule, it all works fine, but if they subsequently try to save another, or if they save more than one at once, they get the following error, which is return by the ..SetValues(..) line:
Message = "The property 'Id' is part of the object's key information and cannot be modified. "
I see from other questions on this subject that there is a feature of EF that stops you from writing the same object twice to the database with a different Id, so this error often happens within a loop. I have tried using some of the suggestions, like adding
viewModel.MainViewModel.dbo.Rules.Add(dboItem);
and
viewModel.MainViewModel.dbo.Entry(dboItem).Property(x => x.Id).IsModified = false;
before the SaveChanges() command, but that has not helped with the problem (not to mention changing the function of the code). I see that some other suggestions say that the Entry should be created within the loop, but in this case, the entries are all existing rules in the database - it seems to me (perhaps erroneously) that I cannot create them inside the save loop, since they are the objects over which the loop is built - for each entity I find, I want to save changes.
I'm really confused about what to do and tying myself increasingly in knots trying to fix the error. It's been several days now and my sanity and self-esteem is beginning to wane! Any pointers to get me working in the right direction to stop the error appearing and allow me to set the database values would be really welcome as I feel like I have hit a complete dead end! The first time around the loop, everything works perfectly.
Aside from the questionable location of the DbContext and view models containing entities, this looks like it would work as expected. I'm assuming from the MVVM tag that this is a Windows application rather than a web app. The only issue is that this assumes that the Rule entity in your ruleViewModel is detached from the DbContext. If the DbContext is still tracking that entity reference then getting the entity from the DbContext again would pass you back the same reference.
It would probably be worth testing this once in a debug session. If you add the following:
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
bool isReferenceSame = Object.ReferenceEquals(dboItem, ruleViewModel.Rule);
Do you get an isReferenceSame value of True or False? If True, the DbContext in your main view model is still tracking the Rule entity and the whole get dboItem and SetValues isn't necessary. If False, then the ruleViewModel is detached.
If the entities are attached and being tracked then edits to the view model entities would be persisted when you call a SaveChanges on the DbContext. (No load & SetValues needed) This should apply to single or multiple entity edits.
If the entities are detached then normally the approach for updating an entity across DbContext instances would look more like:
var context = mainViewModel.dbo;
foreach( var ruleViewModel in updatedRuleViewModels)
{
// This should associate the Entity in the ruleViewModel with the DbContext and set it's tracking state to Modified.
context.Entry(ruleViewModel.Rule).State = EntityState.Modified;
}
context.SaveChanges();
There are a couple of potential issues with this approach that you should consider avoiding if possible. A DbContext should be kept relatively short lived, so seeing a reference to a DbContext within a ViewModel is a bit of a red flag. Overall I don't recommend putting entity references inside view models or passing them around outside of the scope of the DbContext they were created in. EF certainly supports it, but it requires a bit more care and attention to assess whether entities are tracked or not, and in situations like web applications, opens the domain to invalid tampering. (Trusting the entity coming in where any change is attached or copied across overwriting the data state)

How to set default values in Entity Framework 6 (Database First)

Background: I’m beginning a project to convert a web application using Linq2Sql to use Entity Framework (v6) instead. I have a lot of experience with L2S, but I’m brand-new to EF. Since our application and its database already exist, we’re using the “Database First” approach. Also, the database is evolving, so we’re making changes in the schema and the model is updated from the revised database, which regenerates code for the EF model each time.
For many of our entities (database tables), we set default values in our code whenever an entity is constructed. In Linq2Sql it’s easy: define a partial class for the entity, and add a method to the class like this:
partial void OnCreated() { SomeProperty = SomeDefaultValue; }
Whenever Linq2Sql constructs a new entity object, it calls the OnCreated() method you define, and the default values are set as desired. It works great.
The Problem: In EF, I don’t see a way to do this in a Database First scenario.
If I modify the model code generated by EF, the model code is overwritten whenever we update the model after a database revision.
If I define a partial class for the entity in a separate file and define a constructor, the compiler complains that the constructor is already defined.
There doesn’t seem to be any support for something like L2S's OnCreated() method, either.
Any suggestions?
EDIT: Thanks everyone for the helpful comments, but I think I need to point out an important consideration: My goal is to use the database-first approach and stick with it, rather than switching to code-first. When the database schema changes over time I want the EF Designer (or POCO Generator or whatever tools) to update my EF entity classes to match. All without losing my additions to initialize class properties when the class is constructed in the application. This is easy in Linq2Sql, but I just don’t see a way to accomplish this in EF database-first. All suggestions are welcome!
1 . Open .edmx file
2 . Select the field that has the default value and go to the properties
3 . then select StoreGeneratedPattern
4 . then change the value to Computed
i think it's worked.
OP here – I’ve given credit for the answer to ErikEJ but I’d like to recap what I’ve learned on this topic to share with others. There are three goals:
Use the database-first approach and stick with it, even as the database schema is changed over time. That is, have EF produce the code for each database table entity, based on a preexisting database, and update the code when the database is altered.
Provide a mechanism to initialize entity object properties each time the object is constructed, such as Employee.Dependents = 1, for example. (I know simple defaults can be set by the database schema, but more complex initializations must be executed by code.)
The custom initialization code must be preserved when the database schema is altered and EF regenerates the model code.
EF doesn’t provide a way to set properties each time an entity object is constructed in the database-first scenario. Editing the EF-generated code doesn’t work because it gets overwritten whenever EF regenerates the code after a change to the database schema. So far, four workarounds come to mind:
One idea is to add a constructor with more than the default zero-parameter constructor for each entity class. For example, c = new Customer(x) rather than the default c = new Customer(). Application code would call the new constructor, which would inherit the default constructor and add additional code to initialize the class properties. This avoids duplicating the default constructor, something that isn’t permitted by C#. The new constructor is in a separate partial class file, so it will not be overwritten when EF generates updated models from the database.
However, there is a risk that the application programmer may call the default constructor by accident, leading to subtle bugs.
Another solution is to wrap the entity classes in another class, say, a Customer2 class wrapped around the Customer class. The new class would inherit the original class and add initialization code for any properties as needed.
Since these new classes are separate from the original entity class code, they will not be overwritten by EF when it regenerates model code. It may be possible to hide the entity classes from the top-level application code, to avoid accidentally referring to the original classes by accident. If so, this should be a good technique, although I haven’t tested it yet.
A third-party tool, EntityFramework Reverse POCO Generator, is a help. It generates POCO model code much as EF does, but it is not EF. It has an option to generate partial classes, and the entity classes include a InitializePartial() method much like Linq2Sql’s OnCreated(). I think this will work fine for regenerating code as the database is altered over time. My concern here is that this is a third-party product, and there’s always a risk that it can become obsolete or unsupported.
Finally, you can alter the template that EF uses to generate code. The basic idea is to have the generated code add “partial void OnCreated()” to each class, and this lets us use the same convenient technique built into Linq2Sql. I assume newer versions of EF may overwrite the template changes, but it’s just one change in the template rather than changes to every entity class. This method is described here (How to efficiently set default entity values in Entity Framework 6, Database First) and the YouTube video is here (https://www.youtube.com/watch?v=i8J2ipImMuU).
Thanks to all who contributed! I hope this page is helpful to others, so you don’t have to burn as much time as I did looking for a solution.
Use the EF Reverse poco template- it will derive defaults from the database. You can override the InitializePartial method in your partial class to set defaults in code.
Coming from the EF background, I generally do it in the code first migration manually. In the up function of the generated migration, you can do something like this
AddColumn("dbo.Person", "IsActive", c => c.Boolean(nullable: false, defaultValue: true));
AddColumn("dbo.Person", "Name", c => c.String(nullable: false, defaultValue: "Mirza"));
Or to add default SQL value, use
AddColumn("dbo.Person", "CreatedDate",
c => c.String(nullable: false, defaultValueSql: "GETDATE()"));
However there's a downside of this approach in my opinion which is you have to keep a track of your (useless) migrations.
Just found this post looking for an answer to the same issue. Here's a work around that works for me.
Create a partial class for the entity (DB Table) you want to specify default values for, eg:
namespace myApplication.MyModel
{
public partial class myEntityName
{
public myEntityName(bool InitialiseOnConstruct) : this()
{
if (InitialiseOnConstruct)
{
this.property1 = defaultValue1;
this.property2 = defaultValue1;
}
}
}
}
Then in the code, to construct the entity:
thisEntity = new EntityName(true);
OK, it's an extra step, but it works. Hope that helps.

How to to prevent EF from retrieving certain objects

Excuse me for my broken English.
In my application, all objects in the context have a property called ObsoleteFlag, which basically means if the object should still be used on the frontend. It's some sort of "soft-delete" flag without actually having to delete the data.
Now I want to prevent EF from returning any object where ObsoleteFlag is set to true (1)
If for example I retrieve object X, the navigational list property Y contains all the related objects of type Y, no matter what the ObsoleteFlag is set to.
Is there some general way of preventing EF from doing this? I don't want to check on the ObsoleteFlag property everywhere I access the context, and for every navigational property that may be loaded too.
Thanks and sorry for my broken English.
Two different approaches:
In your repository layer have a GetAllWhatever() that returns IQueryable<Whatever> and uses Where(x => !x.Obsolete) and use this whenever you retrieve objects of this type.
Create a view of Create View ActiveWhatever As Select * from ActiveWhatever Where obsolete = 0 and bind to that rather than the table.
The first is essentially checking the flag every time, but doing so in one place, so you don't have to keep thinking about it.
The second is much the same, but the work is pushed to the database instead of the .NET code. If you are going to modify the entities or add new entities you will have to make it a modifiable view, but just how that is done depends on the database in question (e.g. you can do it with triggers in SQL Server, and triggers or rules in PostgreSQL).
The second can also include having a rule or trigger for DELETE that sets your obsolete property instead of deleting, so that a normal delete as far as Entity Framework is concerned becomes one of your soft-deletes as far as the database is concerned.
I'd go for that approach unless you had a reason to object to a view existing just to help the application's implementation (that is you're heavily into the database being "pure" in being concerned with the data rather than its use). But then, if it's handy for one application it's likely handy for more, given the very meaning of this "obsolete".

Keeping A Record of Changes to Entity Framework object

I'm writing a software which receives person information from various sources and all this data is stored into database. The tricky part is when this information is combined to master records. This combining is implemented as a pipeline of different kinds of steps which update the master record from the information received from sources. All steps implement a common interface IPersonUpdatingStep which has Update(Person person) method.
I would like to keep a record about which updated fields and especially about which step was responsible for the change. Different steps might update the same field and I'm only interested in the last change which is present after the master record has traveled through the whole pipeline.
My first instinct was to take a deep copy of the object when it enters a step and another deep copy after it returns and make a diff based on these copies. However deep copying an object of a class which is generated with Entity Framework's Database First tools seems to be quite difficult. It would be quite easy if shallow copies were enough but I need to know if any of the related entities stored in collections were changed.
One suggested easy solution for deep copying was to serialize and deserialize an object but as the model is autogenerated based on database, I cannot add these Serializable attributes.
The problem is even more difficult because the object has related entities and collections of related entities so I need to know if some step changed some properties of these related collection entities.
I wouldn't want to change the pipeline steps so that each would contain code like this:
if (targetPerson.FirstName != sourcePerson.FirstName)
{
targetPerson.FirstName = sourcePerson.FirstName;
changes.Add("FirstName", "Name of the step responsible for the change");
}
But is there another readable and reliable option? I am aware of how I could write an audit trail with Entity Framework when I'm saving the object to database but at that stage I can only log which properties were changed whereas I'm more interested in what pipeline step was responsible for changing the field.
I feel like I'm not doing anything so groundbreaking that others wouldn't have run across this same problem.
UPDATE: haim770's suggestion about ChangeTracker API was a good one. I managed to get information about changed basic properties but how to get information about changed relationships? For example, my Person object has one-to-many relationship with Address objects. I would like to get the information not only about which Address object was updated but even more specifically, which property of that Address object was changed.
DbEntityEntry entry = _source.Context.Entry(person);
foreach (var property in entry.Entity.GetType().GetProperties())
{
DbMemberEntry propertyEntry = entry.Member(property.Name);
if (propertyEntry is DbPropertyEntry)
{
var foo = propertyEntry as DbPropertyEntry;
if (foo.IsModified)
{
changes.Add(step.GetType().Name + ": " + property.Name);
}
}
// How to handle relationships?
}

Entity Framework 4 (Assocations configured with filters)

I have a user entity that contains a collection of survey entities. i would like the assocation to include a filter on the relationship, such as 'IsCompleted', so whenever i eager load (or lazy load for that matter) the collection, this filtering happens.
Is this something we have control over?
thanks!
If you are using a DB back-end that supports views, you might consider using the view as the source for the collection of survey entities. Leverage the power of the DB to do that filtering for you.
Loading of associations for an entity always just gets them all, whether because you used Include during the initial query, called Load after the fact, or lazy-loading caused it. The concept of the navigation property kind of assumes this behavior.
E.J. Brennan's answer would work well. If you're not concerned about loading all surveys behind the scenes (because of performance/memory reasons or something) then you might also consider creating a separate property via a partial class definition on your entity that returns the filtered list.
public partial class User
{
public ICollection<Survey> CompletedSurveys
{
get { return Surveys.Where(s => s.IsCompleted); }
}
}

Categories