I have a class Address Generated by entity Framework.
I Have an propertie AddressID in this class.
I Would like to be able to add some treatement for this prop in the set process.
EX :
public partial class Address
{
public bool _AddressID;
public bool AddressID{get return AddressID;}
set{
if(value == -1) _AddressID = null;
}
}
Thanks
Of course you can't redefine your AddressID in order to put your custom logic in its setter, as you'll get compiler error:
The type Address already contains a definition for 'AddressID'
But no worries, if you take a look at the EF generated code for your EntityObject (let's assume its name is Address) you'll see that every scalar property of generated Address class has its own version of OnPropertyChanging and OnPropertyChanged method. For example, OnAddressIDChanging and OnAddressIDChanged in this case.
As you can see below, there is no default implementation for these two methods, only a declaration. This perfectly provides you the opportunity to execute custom logic
as the property is about to change (PropertyChanging) as well as just after the property
value has changed (PropertyChanged).
// From the designer code for Address class:
partial void OnAddressIDChanging(global::System.Int32 value);
partial void OnAddressIDChanged();
This is how your Entity Model designer code already is look like (hypotetically):
public global::System.Int32 AddressID {
get {
return _AddressID;
}
set {
if (_AddressID != value) {
// OnPropertyChanging method get called here:
OnAddressIDChanging(value);
ReportPropertyChanging("AddressID");
_AddressID = StructuralObject.SetValidValue(value);
ReportPropertyChanged("AddressID");
// OnPropertyChanged get called here:
OnAddressIDChanged();
}
}
}
So all you need to do in order to hook up your custom code is:
public partial class Address {
partial void OnAddressIDChanged() {
if(AddressID == -1) {
AddressID = 0;
}
}
}
By the way, about other posted answers - with all due respect to them - if you want this solution for a production application then you cannot use "Code First" since it merely is a CTP as for now and will be part of the next release for EF, so it cannot be an option.
About customizing default code generation, while this is indeed possible since in VS 2010, Entity Framework itself also uses T4 for designer code generation and we can take advantage of it by changing the T4, But it is an option only if you want to fundamentally change how the entity classes are generated in general and you cannot use it for customizing a setter logic for a specific entity.
Code First in EF4 is an option - it allows you to fully control all of the code. However, another option is to customize the EF4 T4 templates that ship with EF4. If you have certain patterns in your code that you consistently use, this would be a good approach. You can read more about how to customize the templates here: Customizing Entity Classes in VS2010
Related
I've got a collection of addresses which can contain multiple addresses for a Customer (delivery, invoice, etc.)
Now, I'm trying to use .Include(s => s.Addresses) on my Customer class. Addresses and the derivative DeliveryAddress are defined like this:
protected virtual ICollection<Address> Addresses
{
get { return _addresses ?? (_addresses = new Collection<Address>()); }
set { _addresses = value; }
}
[NotMapped]
public Address DeliveryAddress
{
get { return GetAddress(AddressType.Delivery); }
}
private Address GetAddress(AddressType type)
{
return Addresses.FirstOrDefault(a => a.Type == type);
}
Since it's a protected property, I've found a solution for this on Mapping but not exposing ICollections in Entity Framework
I Also found the following post, but this is about mappings instead of includes:
How to map a protected property in EF 4.3 code first
Here they add the following to the class containing the protected property public static Expression<Func<Parent, ICollection<Child>>> ChildrenAccessor = f => f.ChildrenStorage;.
So I've added the same to my Customer class like so:
public static Expression<Func<Customer, ICollection<Address>>> AddressesAccessor = f => f.Addresses;
And then i'm able to use the include with Include(Customer.AddressesAccessor)
This works great for mappings, but I'm not getting it to work with the Include method. It keeps telling me the following:
System.InvalidOperationException: A specified Include path is not valid. The EntityType 'Web.DataAccess.Customer' does not declare a navigation property with the name 'Addresses'.
When I change the signature of the Addresses property from protected back to public all works fine.
Does anyone know how to make this work for protected collections?
I tested this out, and it worked great for me. A sample project can be found at https://github.com/codethug/EF.Experiments/blob/master/EF.Test/SubCollectionTests.cs with tests that pass.
Are you using the Fluent Mapping syntax, like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasMany(Customer.AddressesAccessor)
.WithRequired();
}
Or are you using Data Annotations?
I found that when I used Data Annotations instead of the Fluent Mapping, I would get the exception you found. But using Fluent Mapping made everything work without trouble.
I'm guessing that what's happening is when you use Data Annotations, there is a lot of convention being used to figure out what should be considered a Navigation Property, and since the Addresses property is protected, the convention says that it shouldn't be a Navigation property. However, if you use the Fluent Mapping, you are explicitly telling EF that Addresses should be a Navigation property.
If you really want to use Data Annotations, you can probably code up some custom conventions, but I'm not too familiar with how that could be done.
I use EF 6.1.x Code First.
I have read that an Index with Filter Expression is not supported by EF latest.
There is also no solution on SO:
EF 6.1 Unique Nullable Index
One year later, what is the working way to make a Filter Index work with Code First and DbMigrations?
CREATE UNIQUE NONCLUSTERED INDEX [IX_DefaultLanguageApplicationId] ON [dbo].[Languages]
(
[IsDefaultLanguage] ASC,
[ApplicationId] ASC,
)
WHERE ([IsDefaultLanguage]=(1))
In EF 6.1, the working way to make the this work with Code First and DbMigrations is to use the Sql method in the DbMigration class:
public partial class AddIndexes : DbMigration
{
public override void Up()
{
Sql(#"CREATE UNIQUE NONCLUSTERED INDEX
[IX_DefaultLanguageApplicationId] ON [dbo].[Languages]
(
[IsDefaultLanguage] ASC,
[ApplicationId] ASC
)
WHERE ([IsDefaultLanguage]=(1))");
}
public override void Down()
{
DropIndex("dbo.Languages", "IX_DefaultLanguageApplicationId");
}
}
But I realise that you are probably asking if you can create an index using the IndexAttribute introduced in 6.1, but with an Filter - the answer to that is "No"
Almost a duplicate of: Entity Framework 6.1 - Create index with INCLUDE statement
Please note that right now EF core 2.1.X added built in support for filtered indexes via the HasFilter extension on the IndexBuilder, so a custom implementation is not required anymore.
See this for more details
I know that the original post referred to the 6.1 version of EF, but after some research I have found a way to add an extension method for filtered indexes to the fluent api of EF Core (1.1 version). Maybe someone will find this useful (and maybe there is a way to implement this also in older versions).
I have to warn you though. As this solution uses classes from within Microsoft.EntityFrameworkCore.Migrations.Internal and Microsoft.EntityFrameworkCore.Infrastructure namespaces, it’s no guaranteed that this code will work after EF gets updated. There is a massage included in a summary of each class within these namespaces saying that
This API may change or be removed in future releases
, so you have been warned.
But to the point.
First you have to create a standard extension method for the IndexBuilder. Its main responsibility is going to be adding a new annotation with the condition to the constructed index. One will use this method afterwards with the fluent api. Lest call our annotation SqlServer:FilteredIndex.
static class FilteredIndexExtension
{
public static IndexBuilder Filtered(this IndexBuilder indexBuilder, string condition)
{
indexBuilder.HasAnnotation("SqlServer:FilteredIndex", condition);
return indexBuilder;
}
}
Next you have to allow this annotation to be actually included inside migrations. You have to override the default behavior of SqlServerMigrationsAnnotationProvider for index builders.
class ExtendedSqlServerMigrationsAnnotationProvider : SqlServerMigrationsAnnotationProvider
{
public override IEnumerable<IAnnotation> For(IIndex index)
{
var baseAnnotations = base.For(index);
var customAnnotatinos = index.GetAnnotations().Where(a => a.Name == "SqlServer:FilteredIndex");
return baseAnnotations.Concat(customAnnotatinos);
}
}
Now comes the most difficult part. We have to override the default behavior of SqlServerMigrationsSqlGenerator regarding indexes.
class ExtendedSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
public ExtendedSqlServerMigrationsSqlGenerator(IRelationalCommandBuilderFactory commandBuilderFactory, ISqlGenerationHelper sqlGenerationHelper, IRelationalTypeMapper typeMapper, IRelationalAnnotationProvider annotations, IMigrationsAnnotationProvider migrationsAnnotations) : base(commandBuilderFactory, sqlGenerationHelper, typeMapper, annotations, migrationsAnnotations)
{
}
protected override void Generate(CreateIndexOperation operation, IModel model, MigrationCommandListBuilder builder, bool terminate)
{
base.Generate(operation, model, builder, false);
var filteredIndexCondition = operation.FindAnnotation("SqlServer:FilteredIndex");
if (filteredIndexCondition != null)
builder.Append($" WHERE {filteredIndexCondition.Value}");
if (terminate)
{
builder.AppendLine(SqlGenerationHelper.StatementTerminator);
EndStatement(builder);
}
}
}
As you can see, we are calling the base generator here, so our condition will be added at the end of it without altering it. We have to remember not to terminate the base SQL statement here (last argument passed to the base.Generate method is false). If our annotation is set we can append its value after the WHERE clause at the end of the SQL statement. After that, depending on the argument passed to this method, we can finally terminate the statement or leave it as it is.
For all those parts to work we have to replace old services with their new versions by overriding the OnConfiguring method of our DbContext.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<SqlServerMigrationsAnnotationProvider, ExtendedSqlServerMigrationsAnnotationProvider>();
optionsBuilder.ReplaceService<SqlServerMigrationsSqlGenerator, ExtendedSqlServerMigrationsSqlGenerator>();
}
Now we can use our extension method like this:
builder.HasIndex(a => a.Identity).IsUnique().Filtered("[End] IS NULL");
It will generate migration like this:
migrationBuilder.CreateIndex(
name: "IX_Activities_Identity",
table: "Activities",
column: "Identity",
unique: true)
.Annotation("SqlServer:FilteredIndex", "[End] IS NULL");
And after calling Script-Migration commad in Package Manager Console we will see a resulting SQL as this:
CREATE UNIQUE INDEX [IX_Activities_Identity] ON [Activities] ([Identity]) WHERE [End] IS NULL;
This method can actually be used to include any custom SQL generator into ef core fluent api. At least as long as the EF API remains the same.
Is it possible to add summary properties(no database column) according LINQ from another property(column) in EF generated class from database and this property don't update(delete or remove from class) when update model from database(because this property(cloumn) is not on database)
Yes, it is. Classed generated by Entity Framework as an Entitied are always marked partial. It lets you extend the functionality with your own properties or method.
Let say your entity class is named Post. You can extend it with code like that:
public partial class Post
{
public int Average
{
get
{
return this.Items.Average();
}
}
}
Because it's not a part of designer-generated file it won't be overwritten when it's regenerated. However, there is one requirement to make it work: your custom part of Post class has to be in exactly the same namespace as code generated by EF.
Try using the [NotMapped] attribute on a property in a partial class. This will be ignored by Entity Framework.
public partial class EntityName
{
[NotMapped]
public int CalculatedProperty
{
get
{
return Numbers.Sum();
}
}
}
Let's say I have this code:
public bool Important { get; set; }
private bool _dependsOnImportant;
public bool DependsOnImportant
{
get;
set
{
if (value && !Important)
throw new InvalidOperationException();
_dependsOnImportant = value;
}
}
I want to protect setting DependsOnImportant to true, so that it is only possible when Important is true. This is POCO class. According to: Entity Framework 4.1 - Code first. Doesn't EF override my virtual members? my setter will be used by EF when loading object from database.
How can I be sure that Important has already been set with data from database when DependsOnImportant setter is called? Will EF be smart enough to detect that one setter is using another and load that data first? I don't think so. I think a new object is created with default constructor (setting all fields to default values) and then setters are invoked to populate the object.
So my questions are: Can we determine the order in which setters are called? But most importantly: Do we want to have that knowledge?
I am using DBContext and have two classes whose properties are all virtual. I can see in the debugger that I am getting a proxy object when I query the context. However, a collection property is still null when I try to add to it. I thought that the proxy would ensure that collection is initialized.
Because my Poco object can be used outside of its data context, I added a check for the collection being null in the constructor and create it if necessary:
public class DanceStyle
{
public DanceStyle()
{
if (DanceEvents == null)
{
DanceEvents = new Collection<DanceEvent>();
}
}
...
public virtual ICollection<DanceEvent> DanceEvents { get; set; }
}
That works outside the data context but if I retrieve an object using a query, although the test is true, when I try to set it, I get following exception: 'The property 'DanceEvents' on type 'DanceStyle_B6089AE40D178593955F1328A70EAA3D8F0F01DDE9F9FBD615F60A34F9178B94' cannot be set because the collection is already set to an EntityCollection.'
I can see it is null and I cannot add to it, but neither can I set it to a collection because the proxy says it is already set. Therefore I cannot use it. I'm confused.
Here is the DanceEvent class:
public class DanceEvent
{
public DanceEvent()
{
if (DanceStyles == null)
{
DanceStyles = new Collection<DanceStyle>();
}
}
...
public virtual ICollection<DanceStyle> DanceStyles { get; set; }
}
I have omitted the other value-type properties from the code above. I have no other mappings for those classes in the context class.
As you correctly observed in the answer to your own question, removing the "virtual" keyword from the collection properties works around the problem, by preventing the Entity Framework from creating a change tracking proxy. However, this is not a solution for many people, because change tracking proxies can be really convenient and can help prevent issues when you forget to detect changes at the right places in your code.
A better approach would be to modify your POCO classes, so that they instantiate the collection properties in their get accessor, rather than in the constructor. Here's your POCO class, modified to allow change tracking proxy creation:
public class DanceEvent
{
private ICollection<DanceStyle> _danceStyles;
public virtual ICollection<DanceStyle> DanceStyles
{
get { return _danceStyles ?? (_danceStyles = new Collection<DanceStyle>()); }
protected set { _danceStyles = value; }
}
}
In the above code the collection property is no longer automatic, but rather has a backing field. It's better if you leave the setter protected, preventing any code (other than the proxy) from subsequently modifying these properties. You will notice that the constructor was no longer necessary and was removed.
I found the solution to this problem here: Code First adding to collections? How to use Code First with repositories?
I removed 'virtual' from all properties except collections and lazy loaded objects, that is, all native types.
But I still don't understand how you can end up with the situation where you have a null collection that you cannot use and have no way to set it to a valid collection.
I also found this answer from Rowan Miller on an MSDN forum
Hi,
If you make all your properties virtual then EF will generate proxy classes at runtime that derives from your POCO classed, these proxies allow EF to find out about changes in real time rather than having to capture the original values of your object and then scan for changes when you save (this is obviously has performance and memory usage benefits but the difference will be negligible unless you have a large number of entities loaded into memory). These are known as 'change tracking proxies', if you make your navigation properties virtual then a proxy is still generated but it is much simpler and just includes some logic to perform lazy loading when you access a navigation property.
Because your original code was generating change tracking proxies, EF was replacing your collection property with a special collection type to help it find out about changes. Because you try and set the collection back to a simple list in the constructor you are getting the exception.
Unless you are seeing performance issues I would follow Terrence's suggestion and just remove 'virtual' from your non-navigation properties.
~Rowan
So it appears that I only have the problem with a full 'change tracking proxy' if all my properties are virtual. But given that, why can I still not use the virtual property on the change tracking proxy? This code blows up on line three because ds2.DanceEvents is null and cannot be set in the constructor:
DanceStyle ds2 = ctx.DanceStyles.Where(ds => ds.DanceStyleId == 1).Single();
DanceEvent evt = CreateDanceEvent();
ds2.DanceEvents.Add(evt);
I'm still confused, even though my code is now working because of the fix above.
Old question...
Poco class:
public partial class MyPOCO
{
public MyPOCO()
{
this.MyPocoSub = new HashSet<MyPocoSub>();
}
//VIRTUAL
public virtual ICollection<MyPocoSub> MyPocoSub { get; set; }
}
and proxy code:
public override ICollection<MyPocoSubSet> MyPocoSubSets
{
get
{
ICollection<MyPocoSubSet> myPocoSubSets = base.MyPocoSubSets;
if (!this.ef_proxy_interceptorForMyPocoSubSets(this, myPocoSubSets))
{
return base.MyPocoSubSets;
}
return myPocoSubSets;
}
set
{
if (value != this.RelationshipManager.GetRelatedEnd("WindowsFormsApplication.Models.MyPocoSubSet_MyPOCO", "MyPocoSubSet_MyPOCO_Source"))
{
// EXCEPTION
throw new InvalidOperationException("The property 'MyPocoSubSets' on type 'MyPOCO_A78FCE6C6A890855C68B368B750864E3136B589F9023C7B1D90BF7C83FD291AC' cannot be set because the collection is already set to an EntityCollection.");
}
base.MyPocoSubSets = value;
}
}
As you can see that exception raised in proxy class in ExtityFramework 5. This means that behavior still exist.