2012-10-25 2 views
2

기본적으로 EF는 개체를 생성 할 때 EntityObject를 사용합니다. 내 자신의 AbstractEntityObject 클래스를 사용하도록 수정했습니다. 그렇게하면서, IValidatableObject를 추가했습니다. context.SaveChanges()를 호출했을 때 인상적 이었기 때문에 자동으로 Validate를 호출하고 예외를 throw합니다.EF SaveChanges가 호출하지 않음 Validate (IValidatableObject)

public abstract class AbstractEntityObject : EntityObject, IValidatableObject 
{ 
    private readonly DBTable table; 

    protected AbstractEntityObject(DBTable table) 
    { 
     this.table = table; 
    } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     List<OITValidationResult> results = new List<OITValidationResult>(); 
     List<PropertyInfo> properties = new List<PropertyInfo>(GetType().GetProperties()); 

     foreach (DBField field in table.GetFields()) 
     { 
      foreach (PropertyInfo prop in properties) 
       if (StringUtilities.EqualsIgnoreCase(field.FieldName, prop.Name)) 
       { 
        results.AddRange(field.Validate(prop.GetValue(this, null))); 
        results.AddRange(AdditionalValidation(field, prop)); 
        properties.Remove(prop); 
        break; 
       } 
     } 

     return results; 
    } 

    public abstract List<OITValidationResult> AdditionalValidation(DBField field, PropertyInfo prop); 
} 

public abstract class AbstractTLMSEntityObject : AbstractEntityObject 
{ 
    protected AbstractTLMSEntityObject(DBTable table) 
     : base(table) 
    { 
    } 

    public override List<OITValidationResult> AdditionalValidation(DBField field, PropertyInfo prop) 
    { 
     List<OITValidationResult> results = new List<OITValidationResult>(); 

     if (!EntityState.Equals(EntityState.Unchanged)) 
     { 
      if (StringUtilities.EqualsIgnoreCase(field.FieldName, "userid")) 
       prop.SetValue(this, TLMSDB.User.UserName, null); 
      else if (StringUtilities.EqualsIgnoreCase(prop.Name, "dtmod")) 
       prop.SetValue(this, DateTime.Now, null); 
     } 

     OITValidationResult additionalResult = AdditionalValidation(field.Field); 
     if (additionalResult != null) 
      results.Add(additionalResult); 

     return results; 
    } 

    /* By default there is no additional validation, subclasses should override this if they need additional validation */ 
    public virtual OITValidationResult AdditionalValidation(Enum field) 
    { 
     return null; 
    } 
} 

을 그럼,이 부분 클래스는 EF가 작성하는 하나와 함께있는 서브 클래스가 :

public partial class CSDCrud 
{ 
    public enum Fields 
    { 
     RECEIPT_NUMBER, 
     GRANT_ID, 
     GRANT_FUND, 
     TOTAL_ACRES 
    } 

    public CSDCrud() : base(CSDCrudDAO.Instance.Table) 
    { 
    } 

    public static String GetDataPropertyName(Enum field) 
    { 
     return CSDCrudDAO.Instance.Table.GetField(field).FieldName; 
    } 
} 

CSDCrud가 AbstractTLMSEntityObject 감사에서 상속을 여기

내가 가진 무엇 나는 .tt 파일에서 그것을 변경했다.

여기가 이상해집니다. DBField (부모 클래스에서 참조)가 데이터의 자체 유효성 검사를 수행하도록 설정했습니다. receipt_number가 실패하고 사실, 다음이 발생합니다 ... 예외가 발생합니다 따라서 필요하고이 경우에, 나는 결과 1 개 항목을 포함하는,

List<OITValidationResult> results = (List<OITValidationResult>)crudObject.Validate(null); 
try 
{ 
    CommonEntityManager.GetContext().CrudSet.AddObject(crudObject); 
    CommonEntityManager.GetContext().SaveChanges(); 
    return true; 
} 
catch (Exception ex) 
{ 
    return false; 
} 

이 예상대로 ... 그래서를 설정 한 적절한 오류 ... 그러나 SaveChanges 예외를 던지지 않고 그냥 괜찮아요 저장 ... 내가 뭘 놓치고?

편집 : 분명히 SavingChanges 이벤트를 사용하여 자체 처리기를 추가 할 수 있지만 기존 인프라를 사용하고 싶습니다.

static void context_SavingChanges(object sender, EventArgs e) 
    { 
     foreach (ObjectStateEntry ose in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)) 
     { 
      List<ValidationResult> results = new List<ValidationResult>(((IValidatableObject)ose.Entity).Validate(null)); 
      if (results.Count > 0) 
       throw new CustomException(results); 
     } 

     foreach (ObjectStateEntry ose in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added)) 
     { 
      List<ValidationResult> results = new List<ValidationResult>(((IValidatableObject)ose.Entity).Validate(null)); 
      if (results.Count > 0) 
       throw new CustomException(results); 
     } 
    } 
+0

당신이 ObjectContext를 사용하고 있습니까? 유효성 검사는 DbContext를 사용하는 경우에만 호출됩니다. 죄송합니다. 질문을 다시 읽어야합니다. – Pawel

+0

OK. 다시 읽었습니다. 문제의 수정 코드 인 마지막 코드 조각이 오도되었습니다. 유효성 검사는 DbContext를 사용할 때만 작동합니다. – Pawel

+0

@Pawel 엔티티 프레임 워크가 모델 유효성 검사를 수행하지 않으면 IValidatableObject 인터페이스를 구현하는 엔티티 클래스의 전체 목적은 무엇입니까? 그런 다음 엔터티가 데이터베이스에 커밋되기 전에 누가 호출해야합니까? objectContext에 객체를 추가 할 때마다 내 엔티티 객체의 속성 변경 이벤트에서 List 를 만들 수 있습니다. 이 목록은 누군가가 원할 경우 Validate 메소드를 통해 반환 될 수 있습니다. – RBT

답변

2

기본 제공 유효성 검사는 ObjectContext API가 아니라 DbContext API를 사용하는 경우에만 호출됩니다.

+0

고마워, 나는 그걸 두려워했다. – user1774827

0

엔티티 개체의 기본 제공 사용자 지정 유효성 검사는 objectContext 클래스에 의해 명시 적으로 호출되지 않지만 프로그래머는이를 명시 적으로 처리해야합니다. 끝에서 명시 적으로하려면 SavingChanges 이벤트가 가장 좋습니다. 이것은 당신이 그것을 할 수있는 방법입니까? 나는 빠른 코드에서이 문제를 설명하고있어 내가 콘솔 응용 프로그램 준비 니펫 :

Course.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data.Objects.DataClasses; 
using System.ComponentModel.DataAnnotations; 

namespace EFCoding 
{ 
    public partial class Course : EntityObject, IValidatableObject 
    { 
     List<ValidationResult> validationErrors = new List<ValidationResult>(); 
     public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
     { 
      return validationErrors; 
     } 

     partial void OnNameChanging(global::System.String value) 
     { 
      validationErrors.Clear(); 
      if (value.Length > 10) 
      { 
       ValidationResult vResult = new ValidationResult("Invalid Course Name", new string[] { "EmailID" }); 
       validationErrors.Add(vResult); 
      } 
     } 

     partial void OnDisciplineChanging(global::System.String value) 
     { 
      validationErrors.Clear(); 
      if (value.Length > 10) 
      { 
       ValidationResult vResult = new ValidationResult("Invalid Discipline", new string[] { "EmailID" }); 
       validationErrors.Add(vResult); 
      } 
     } 

    #region Factory Method 

    /// <summary> 
    /// Create a new Course object. 
    /// </summary> 
    /// <param name="id">Initial value of the Id property.</param> 
    /// <param name="name">Initial value of the Name property.</param> 
    /// <param name="discipline">Initial value of the Discipline property.</param> 
    public static Course CreateCourse(global::System.Int32 id, global::System.String name, global::System.String discipline) 
    { 
     Course course = new Course(); 
     course.Id = id; 
     course.Name = name; 
     course.Discipline = discipline; 
     return course; 
    } 

    #endregion 

    #region Primitive Properties 

    /// <summary> 
    /// No Metadata Documentation available. 
    /// </summary> 
    [EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)] 
    [DataMemberAttribute()] 
    public global::System.Int32 Id 
    { 
     get 
     { 
      return _Id; 
     } 
     set 
     { 
      if (_Id != value) 
      { 
       OnIdChanging(value); 
       ReportPropertyChanging("Id"); 
       _Id = StructuralObject.SetValidValue(value); 
       ReportPropertyChanged("Id"); 
       OnIdChanged(); 
      } 
     } 
    } 
    private global::System.Int32 _Id; 
    partial void OnIdChanging(global::System.Int32 value); 
    partial void OnIdChanged(); 

    /// <summary> 
    /// No Metadata Documentation available. 
    /// </summary> 
    [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] 
    [DataMemberAttribute()] 
    public global::System.String Name 
    { 
     get 
     { 
      return _Name; 
     } 
     set 
     { 
      OnNameChanging(value); 
      ReportPropertyChanging("Name"); 
      _Name = StructuralObject.SetValidValue(value, false); 
      ReportPropertyChanged("Name"); 
      OnNameChanged(); 
     } 
    } 
    private global::System.String _Name; 
    partial void OnNameChanging(global::System.String value); 
    partial void OnNameChanged(); 

    /// <summary> 
    /// No Metadata Documentation available. 
    /// </summary> 
    [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] 
    [DataMemberAttribute()] 
    public global::System.String Discipline 
    { 
     get 
     { 
      return _Discipline; 
     } 
     set 
     { 
      OnDisciplineChanging(value); 
      ReportPropertyChanging("Discipline"); 
      _Discipline = StructuralObject.SetValidValue(value, false); 
      ReportPropertyChanged("Discipline"); 
      OnDisciplineChanged(); 
     } 
    } 
    private global::System.String _Discipline; 
    partial void OnDisciplineChanging(global::System.String value); 
    partial void OnDisciplineChanged(); 

    #endregion 


    } 
} 

Program.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ComponentModel.DataAnnotations; 
using System.Data.Objects; 
using System.Data; 

namespace EFCoding 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var efContext = new EfTestEntities1()) 
      { 

       efContext.SavingChanges += new EventHandler(efContext_SavingChanges); 
       var course = new Course { Id = 2, Name = "Very long course name which will result in exception", Discipline = "Very long discipline name which will result in error at run time" }; 
       efContext.Courses.AddObject(course); 
       efContext.SaveChanges(); 
      } 
     } 

     static void efContext_SavingChanges(object sender, EventArgs e) 
     { 
      ObjectContext objectContext = sender as ObjectContext; 
      IEnumerable<Object> entities = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified).Select(x => x.Entity); 
      foreach (var item in entities) 
      { 
       var current = item as Course; 
       if (current != null) 
       { 
        var validationsErrors = current.Validate(new ValidationContext(current,null,null)); 
        if (validationsErrors.Count() > 0) 
        { 
         throw new InvalidOperationException("Model has validation errors"); 
        } 
       } 
      } 
     } 
    } 
} 
관련 문제