LLBLGEN Validation troubles

Posts   
 
    
Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 27-Feb-2008 03:12:02   

Hi LLBLGEN People

I have added to the CommonBaseEntity class a List<string> Errors to hold a collection of errors that return during the validation process.

When I save the entity, the validator goes through and checks everything is correct if there are any errors it adds them to the Errors collection so it can be returned with the entity to the PL. How do I stop the data adapter from saving the entity that fails the validation routine?

eg:



public virtual void ValidateEntityBeforeSave(IEntityCore involvedEntity)
{
      CustomerEntity Customer = (CustomerEntity) involvedEntity;

      if(Customer.StatusId == 10)
      {
             Customer.Errors.Add("Customer cannot be saved is it is in and closed state.");
      }

      if(Customer.HasErrors)
      {
             // Do something to cancel the save.
      }
}


Thanks Alex

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 27-Feb-2008 09:58:57   

If validation fails, but it's not major, don't throw an exception but simply set the IDataErrorInfo error text on the entity. If the entity isn't valid, throw an ORMEntityValidationException. The LLBLGen Pro code simply calls OnValidateEntitycontextdescription on the entity and moves on if no exception is thrown

Please check the example provided in the manual's section "using the generated code -> Validation per field or per entity"

Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 29-Feb-2008 00:57:01   

Hi Walaa,

Thanks for you help, unfortuanatly thats exactly what im doing and it doesn't seem to want to work, the entity is still being saved, even when I'm calling SetEntityError

My code looks like this:

Save:



using (CS.SM.DataAccess.DatabaseSpecific.DataAccessAdapter Adapter = new CS.SM.DataAccess.DatabaseSpecific.DataAccessAdapter())
            {
                user.Validator = new UserValidator();
                Adapter.SaveEntity(user, refetchAfterSave);
            }


Validator:



   /// <summary>
    /// Used to validate the user entity classes.
    /// </summary>
    public class UserValidator : ValidatorBase
    {
        #region Static Error Messages ...
        /// <summary>
        /// Error message for invalid password length.
        /// </summary>
        public const string ErrorMsgPasswordLength = "The password must be at least 6 characters.";

        /// <summary>
        /// Username change error message.
        /// </summary>
        public const string ErrorMsgEditUsername = "Once a user is created and saved thier username cannot be changed.";

        /// <summary>
        /// Username already in user error message.
        /// </summary>
        public const string ErrorMsgUsernameAlreadyInUser = "The choosen user name is already in use.";
        #endregion

        #region Validation Before Save ...
        /// <summary>
        /// Validate the entity before saving it to the database.
        /// </summary>
        /// <param name="involvedEntity"></param>
        public override void ValidateEntityBeforeSave(IEntityCore involvedEntity)
        {
            UserEntity UpdatedUser = (UserEntity)involvedEntity;

            //UpdatedUser.Errors.Add("test");

            UpdatedUser.SetEntityError("Error!");
            UpdatedUser.SetEntityFieldError("Firstname", "test", true);

            base.ValidateEntityBeforeSave(involvedEntity);
        }
        #endregion
    }


Nunit Test:



        [Test]
        public void TestCreateNewUserTestCase03()
        {

            UserEntity NewUser = new UserEntity();
            NewUser.UserName = "NUnitTest";
            NewUser.UserTypeId = (int)UserType.Customer;
            NewUser.Title = "Mr.";
            NewUser.Password = "Password";
            NewUser.PasswordSalt = 12345;
            NewUser.FirstName = "FirstName";
            NewUser.LastName = "LastName";

            UserManager.SaveUser(NewUser, true);
            
            // Should fail validation, and not be saved. resulting in a zero id.
            Assert.IsTrue(NewUser.Id == 0);

        }


Any ideas what I'm doing wrong?

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 29-Feb-2008 09:08:37   

If the entity isn't valid, throw an ORMEntityValidationException. The LLBLGen Pro code simply calls OnValidateEntitycontextdescription on the entity and moves on if no exception is thrown.

Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 02-Mar-2008 06:15:11   

Hi Walaa,

So basically if the entity is invalid, I have to throw an error. In some situations when the entity error is critical I can understand that. but in situations where it is simple user error it seems like overkill, Exceptions are expensive to throw and can only contain a single message (unless you do some seperating with markers and split them out, or you create a custom error class and attach a collection to it).

Also what is the purpose of having IDataError if the LLBLGEN just ignores it and saves anyway?

I there a way to make the LLBLGEN look at the entity and check if it contains an error and not save it? maybe override a save method within the data access adapter?

Thanks Alex

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 02-Mar-2008 20:55:52   

Hi Alex,

I don't really recomend to override SaveEntity method, coz you have to stop the save process at some point, and if you don't throw an exception you have to "no-call" to the base.SaveEntity. However this is kind of blind IMHO becouse some back-end process is still runing after your override. Another reason is that maybe you don't want that for ALL your entities. (http://llblgen.com/TinyForum/Messages.aspx?ThreadID=5741&StartAtMessage=0&#31515)

Here is a discussion about throwing exceptions and why they are necessary at the end of the day: http://llblgen.com/TinyForum/Messages.aspx?ThreadID=9207

David Elizondo | LLBLGen Support Team
Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 04-Mar-2008 03:26:15   

Hi Daelmo,

Thanks for posting those links, I hadn't thought of the issue with transactions, they can be dfficult to wind back without an exception. So perhaps a hybrid solution would work, where I throw an exception, but attach a collection of errors to it first eg:



ValidationException Ex = new ValidationException();

If(User.name == "Test")
{
      Ex.ValidationErrors.Add("The users name cannot be \"test\"");
}

If(Ex.HasErrors)
{
      throw Ex;
}


Then in the PL catch the exception and write out the errors?

Hows does that sound? It doesn't sound like I can avoid throwing a exception, this way at least I get the most out of the one I do throw?

Thanks Alex

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 04-Mar-2008 10:46:54   

So perhaps a hybrid solution would work, where I throw an exception, but attach a collection of errors to it first

That's exactly why the following methods were introduced: SetEntityError and SetEntityFieldError, which allows external code to set the error of a field and/or entity. If append is set to true with SetEntityFieldError, the error message is appended to an existing message for that field using a semi-colon as separator

Maxus
User
Posts: 76
Joined: 04-Aug-2006
# Posted on: 06-Mar-2008 04:37:17   

Thanks Walaa,

Im still not happy with throwing exceptions but it seems I dont really have a choice.

Thanks A