2010-10-25 45 views
4

我正在使用Entity Framework v4。我試圖通過覆蓋SaveChanges方法來實現一些邏輯來驗證我的實體,然後才能保存它們。我也是我的實體的POCO。如何避免使用正確類型的鑄件

我通過執行以下操作獲取修改和新實體的列表。

var entities = (ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)) 
       .Select(ose => ose.Entity).OfType<IValidatable>(); 
foreach(var entity in entities) 
{ 
     var validator = validatorProvider.GetValidator(entity); 
     var errors = validator.Validate(entity); 
     if (errors.Count() > 0) 
     { 
      throw new Exception("A validation error"); 
     } 
} 

這裏是我的GetValidator方法的代碼:

private static IValidator<TObject> GetValidator<TObject>(TObject obj) where TObject : IValidatable 
{ 
    var objType = obj.GetType(); 
    var validatorType = Type.GetType("SmsPortal.Validation." + objType.Name + "Validator"); 
    var varValidator = Activator.CreateInstance(validatorType); 
    return (IValidator<TObject>)varValidator; 
} 

的問題是,我得到一個失敗的話說:

Unable to cast object of type 'Blah.Validation.ConcreteValidator' to type 'Blah.Validation.IValidator`1[Blah.Validation.IValidatable]' 

,我可以做的唯一的事情擺脫這個錯誤是先將對象轉換爲正確的類型,但我不想投擲所有東西。

這裏是我的IValidator

public interface IValidator<TEntity> 
     where TEntity : IValidatable 
{ 
    IEnumerable<ValidationError> Validate(TEntity entity); 
} 

好了接口,現在我的瘋狂背後的一些推理。我試圖堅持SRP,我不希望我的對象能夠驗證自己。所以我的IValidatable接口只是一個標記。我試過了,沒有標記,它沒有任何區別。我也不希望我的單元測試體積龐大,但我知道我可以爲驗證和實際實體分開單元測試。

接下來,我很懶惰,我不想寫映射器等。另外,我想有一個更多的約定配置,如果有驗證提供它將被使用。

我在很多地方都使用過泛型。我喜歡它提供的功能,但我不是專家,目前它正在咬我。

反正有這個嗎?避免必須轉換對象的方法,以便運行時可以找出將其轉換爲什麼的方法?我使用Ninject的依賴注入是否有幫助

UPDATE: 按照要求,具體的校驗

public class ConcreteValidator : IValidator<SomeObject> 
{ 
    public IEnumerable<ValidationError> Validate(SomeObject entity) 
     { 
      if (string.IsNullOrEmpty(entity.Name)) 
        yield return new ValidationError { Message = "Name is mandatory", Property = "Name" }; 

       if (entity.Name != null && entity.Name.Length > 50) 
        yield return new ValidationError { Message = "Name must be less than 50 characters", Property = "Name" }; 
    } 
} 
+0

請寄出'ConcreteValidator'的定義。 – 2010-10-25 11:30:49

+0

@Peter,我發佈了代碼。 :) – uriDium 2010-10-25 11:39:48

+0

SomeObject擴展IValidatable嗎? – 2010-10-26 00:50:33

回答

0

是否從TObjectSomeObject繼承?即使如此,在Java中,IValidator<SomeObject>而不是IValidator<TObject>。你的代碼看起來像C#,但我不知道泛型如何在那裏工作。

我認爲你應該在你的文章中加上適當的語言標籤,因爲泛型絕對是語言相關的。

+0

你是100%正確的,這是C#不是Java。我按照你的建議更新了標籤。 – uriDium 2010-10-25 12:00:59

1

我建議你爲你的驗證器提供服務定位器,並根據你的驗證器的類型參數獲取驗證器,你可以找到如何做的例子here 我也不明白你的真實情況代碼,如下面的代碼片段。所以,請把你的整體搭配使用

public interface IValidator<T> where T: IObj 
{ 

} 

public class PersonValidator : IValidator<Person> 
{ 

} 

public static class Validators 
{ 
    public static IValidator<TObject> GetValidator<TObject>(TObject obj) 
     where TObject : IObj 
    { 
     var t = obj.GetType(); 
     var name = string.Format("{0}.{1}Validator", t.Namespace, t.Name); 
     return (IValidator<TObject>) 
      Activator.CreateInstance(Type.GetType(name)); 
    } 

} 

[TestFixture] 
public class ValidatorsTest 
{ 
    [Test] 
    public void TestPersonValidator() 
    { 
     var pValidator = Validators.GetValidator(new Person()); 

    } 
} 

你遇到的問題是在以下

var entities = (ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)) 
      .Select(ose => ose.Entity).OfType<IValidatable>(); 

的foreach(在實體VAR實體) {VAR 驗證= validatorProvider.GetValidator(實體的所有類剪斷); var errors = validator.Validate(entity);如果(errors.Count()> 0) { }拋出新的異常(「驗證錯誤」); } }

在這裏,當你調用validatorProvider.GetValidator(entity)實體類型爲IValidator,所以你在呼喚validatorProvider.GetValidator<IValidator>(entity)的,但是你真正想要做的是validatorProvider.GetValidator<TEntity>(entity) 你需要調用與反思泛型方法,動態specifing調用哪個方法。

最後調用適當的方法與反思,你最好改變方法聲明GetValidator<TObject>()並執行以下操作:

foreach(var entity in entities) 
{ 
     var validator = validatorProvider.GetType().GetMethod("GetValidator").MakeGenericMethod(entity.GetType()).Invoke(validatorProvider, null); 
     var errors = validator.Validate(entity); 
     if (errors.Count() > 0) 
     { 
      throw new Exception("A validation error"); 
     } 
} 

最後修改:

.... 
foreach(var entity in entities) 
{ 
     GetType() 
       .GetMethod("ValidateObj") 
       .MakeGenericMethod(entity.GetType()) 
       .Invoke(this, null); 

} 
.... 
.... 
public void ValidateObj<TEntity>(TEntity obj) where TEntity : IValidatable { 
    var errors = validatorProvider.GetValidator<TEntity>().Validate(obj); 
    if (errors.Count() > 0) throw new ValidationException(obj, errors); 
} 
+0

@vittore。我不認爲你讀了我的問題。我已經完成了很多。除了從實體框架中獲得某些東西外,它是一個實體對象。實體對象包裝原始對象並將其公開。但是我必須轉換爲正確的類型才能使運行時正確推斷Validator的正確類型參數。我不知道運行時的類型。我希望它是動態的。 – uriDium 2010-10-26 07:20:24

+0

我剛剛意識到發生了什麼事情。您以不正確的方式調用泛型方法。 – vittore 2010-10-27 14:15:57

+0

@uriDium我更新了我的答案 – vittore 2010-10-27 14:22:18

0

查看答案this question一個很好的解釋這裏發生了什麼。

如果更改ConcreteValidator以下內容,你應該確定:

public class ConcreteValidator : IValidator<SomeObject>, IValidator<IValidatable> 
0

一個行代碼就能解決你的問題: 動態實體= entry.Entity;