Entity Framework 6 AttachAsModified

342 views Asked by At

I am trying to update EF from 4.x to 6.x (newest stable via nuget). After some work the application works for data retrieval but not for save.
For performance reasons we mostly used the "AttachAsModified" method from EF 4.x. In most cases we have the primary key, attach, modify the values we want to update and finally save.
EF 6.x does not have such a method so I tried the following:

var data = new CatalogNode
{
    CatalogNodeId = catalogNodeId,
    UpdatedOn = updatedOn,
    UpdatedBy = updatedBy,
 };

  CatalogNodes.Attach(data);
  var entry = Entry(data);
  entry.Property(e => e.UpdatedOn).IsModified = true;
  entry.Property(e => e.UpdatedBy).IsModified = true;

Unfortunately EF throws a "DbEntityValidationException" that says that some additional properties are required and need to be set.

Edit:
Here is the generated CatalogNodes entity (setter includes call to OnPropertyChanged)

public partial class CatalogNode : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public CatalogNode()
    {
        this.CatalogArticles = new HashSet<CatalogArticle>();
        this.LocalizedCatalogNodes = new HashSet<LocalizedCatalogNode>();
        this.CatalogNodeAttributeValues = new HashSet<CatalogNodeAttributeValue>();
        this.CatalogNodeMappings = new HashSet<CatalogNodeMapping>();
        this.TranslatedCatalogNodeNames = new HashSet<TranslatedCatalogNodeName>();
        this.CatalogNodeMediaAssets = new HashSet<CatalogNodeMediaAsset>();
    }

    private System.Guid _catalognodeid;
    public System.Guid CatalogNodeId  {get;set;}

    private string _catalognodeuid;
    public string CatalogNodeUid  {get;set;}

    private System.Guid _catalogid;
    public System.Guid CatalogId  {get;set;}

    private int _catalognodetype;
    public int CatalogNodeType  {get;set;}

    private Nullable<System.Guid> _parentid;
    public Nullable<System.Guid> ParentId  {get;set;}

    private Nullable<System.Guid> _linkid;
    public Nullable<System.Guid> LinkId  {get;set;}

    private int _sequence;
    public int Sequence  {get;set;}

    private Nullable<System.Guid> _attributegroupid;
    public Nullable<System.Guid> AttributeGroupId  {get;set;}

    private string _printcatalogtemplateuid;
    public string PrintCatalogTemplateUid  {get;set;}

    private bool _printcatalogpagebreak;
    public bool PrintCatalogPageBreak  {get;set;}

    private System.DateTime _createdon;
    public System.DateTime CreatedOn  {get;set;}

    private string _createdby;
    public string CreatedBy  {get;set;}

    private System.DateTime _updatedon;
    public System.DateTime UpdatedOn  {get;set;}

    private string _updatedby;
    public string UpdatedBy {get;set;}


    public virtual AttributeGroup AttributeGroup { get; set; }
    public virtual Catalog Catalog { get; set; }
    public virtual ICollection<CatalogArticle> CatalogArticles { get; set; }
    public virtual ICollection<LocalizedCatalogNode> LocalizedCatalogNodes { get; set; }
    public virtual ICollection<CatalogNodeAttributeValue> CatalogNodeAttributeValues { get; set; }
    public virtual CatalogNode LinkedCatalogNode { get; set; }
    public virtual ICollection<CatalogNodeMapping> CatalogNodeMappings { get; set; }
    public virtual ICollection<TranslatedCatalogNodeName> TranslatedCatalogNodeNames { get; set; }
    public virtual ICollection<CatalogNodeMediaAsset> CatalogNodeMediaAssets { get; set; }

    public void OnPropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

The error messages I get are like:
- "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details."
- "The field \"CreatedBy\" is required."

First question: Where can I see or set which fields are requiered? We currently use a database first approach.
Second question: Is there a workaround? The resulting update query does not update those additional properties. Of course I could set these additional properties to some random values and mark these properties as unchanged but there has to be a better way I hope.

Thanks for your help!

2

There are 2 answers

4
Alexander Polyankin On BEST ANSWER

1) Where can I see or set which fields are required?

They are marked as not nullable in edmx file: EF designer with not nullable property

Or they are marked with [Required] attribute:

[Required]
public string Name { get; set;} 

Or they are marked with IsRequired() call with Fluent API:

modelBuilder.Entity<Person>().Property(t => t.Name).IsRequired();

Or they do now allow null value to be set:

public int Age { get; set; }

For example this allows null value:

public int? Age { get; set; }

2) Is there a workaround?

For partial updates the only workaround I know it is set some junk values on not modified properties just to pass validation. For example, let's update age property of person with id = 1:

var person = new Person();
person.Id = 1;
person.Name = ""; // not null string to pass validation
person.Age = 25;

var entry = context.Entry(person);
entry.Property(e => e.Age).IsModified = true;
context.SaveChanges();

Important: Attach() is not needed.

0
Andre On

I found that the validation just dont work with partial updates. One solution is to override the ValidateEntity method or to disable validation by setting the ValidateOnSaveEnabled proeprty to false. Both are located in the db configuration class.