Data Access in custom Validation Rule

Data Access in custom Validation Rule

Old forum URL: forums.lhotka.net/forums/t/1527.aspx


SashaV posted on Wednesday, October 18, 2006

Is there any "standard" way to implement the validation rule in which i need to interact with DB?

skagen00 replied on Wednesday, October 18, 2006

An option is certainly a command object - what is the sort of validation rule you're looking to implement?

SashaV replied on Wednesday, October 18, 2006

Thank you for answer!


The situation is:


When admin process request he must view all brokenrule. One of the rule is:
“check is there any object in DB with the “serial number” from request?”.


 I will use broken rule with severity – warning, for have ability to store business object in DB.

skagen00 replied on Wednesday, October 18, 2006

I think I follow you.

If it's OK if it's just checked when the admin opens it, you might just return a value from the DB on the fetch request - maybe server request id(s) / names that have the same serial number (but that are different objects than the one you're retrieving). So a collection of ServerRegisterRequestInfo lightweight objects, named as _duplicateSerials). You might just grab a separate result set for the duplicates and pass the data reader along to the collection on the main BO fetch.

If you fetch a collection you can display the ID's to the admin (and perhaps some navigation to the other objects) as well as utilize it in a business rule - is the collection count greater than 0? then the warning rule is broken.

Hope that helps.

 

SashaV replied on Wednesday, October 25, 2006

Thank you for your help! I will consider your decision. I was far from work, so I couldn’t work on this problem, but I start work now.

In our system we have BO: UserRequestList and UserRequest.

When we receive message from user, UserRequest is created by calling:

    public static RevocationRequest NewRevocationRequest(string serialNumber)
    {
      return DataPortal.Create<RevocationRequest>(new Criteria(serialNumber));
    }


As you can see, we use DataPortal.Create with Criteria class and  serialNumber as parameters. So I can access DB for see if duplicated serial number exists:

    private void DataPortal_Create(Criteria criteria)
    {
      using (SqlConnection cn = new SqlConnection(Database.CAConnection))
      {
        cn.Open();
        using (SqlCommand cm = cn.CreateCommand())
        {
          cm.CommandType = CommandType.StoredProcedure;
          cm.CommandText = "getUser";
          cm.Parameters.AddWithValue("@serialNumber", criteria.SerialNumbers[0]);

          using (SafeDataReader dr = new SafeDataReader(cm.ExecuteReader()))
          {
            if (!dr.Read())
              isValidSerialNumber = false;
            else
              isValidSerialNumber = true;
          }
        }
      }

      ValidationRules.CheckRules();
    }


I use isValidSerialNumber for my validation rule:

    private static bool ValidCertificateSerialNumberRequired(object targets, Csla.Validation.RuleArgs e)
    {
     if (!(((UserRequest)targets).isValidSerialNumber))
      {
        e.Description = "Duplicated Serial Number!";
        return false;
      }
      else
      {
        return true;
      }     
    }

   
What do you think about this?

kids_pro replied on Wednesday, October 25, 2006

Base on the book:
If(!UserRequest.Exist(serialNum)){
  // call create routine ...
}

skagen00 replied on Wednesday, November 01, 2006

So there is a server register request object - I'm not sure if you care about the broken rule (warning) at the time the user is working with it, but let's just assume you do.

I would want this rule to be dependant on the serial number property.

So, you'll create a rule attached to the serial number property - we'll call it "ValidCertificateSerialNumberRequired".

This rule will be triggered whenever the serial number property changes. You should also trigger it in when the user request object is fetched (some people may do a check rules on all properties when fetching an object - if you do, it'd be checked - if you don't, you should do a checkrule on the serial number property - it'll trigger the rule handler).

The code below will obviously need to be debugged/tweaked, I just tried to whip something together...

We'll first create a simple class to serve as a result of the command to give us not only whether the serial number exists under another user request but also the list of user requests where this is the case:

[Serializable()]

public class SerialNumberCheckResult

{

private List<int> _userRequestIds = new List<int>();

/// <summary>

/// Gets the list of User Request Ids containing this Serial Number (may be empty).

/// </summary>

public List<int> UserRequestIds

{

get { return _userRequestIds; }

}

private bool _exists;

/// <summary>

/// Gets whether the serial number exists in the database under a different User Request

/// </summary>

public bool Exists

{

get { return _exists; }

}

internal SerialNumberCheckResult(List<int> userRequestIds, bool exists)

{

_userRequestIds = userRequestIds;

_exists = exists;

}

}

Then, within your UserRequest class, you'd have this inner private command class:

/// <summary>

/// This command is used to determine if a particular serial number exists in the database.

/// Also, what User Request IDs have that serial number

/// </summary>

[Serializable()]

private class SerialNumberExistsCommand : CommandBase

{

private int _serialNumberToCheck;

private SerialNumberCheckResult _commandResult;

public SerialNumberCheckResult CommandResult

{

get

{

return _commandResult;

}

}

public SerialNumberExistsCommand(int serialNumberToCheck)

{

_serialNumberToCheck = serialNumberToCheck;

}

 

protected override void DataPortal_Execute()

{

List<int> matchList = new List<int>();

using (SqlConnection cn = new SqlConnection(Database.ActiveDatabase))

{

cn.Open();

using (SqlCommand cm = cn.CreateCommand())

{

cm.CommandType = CommandType.StoredProcedure;

cm.CommandText = "getUserRequestsWithSerialNumber"; // Return all User Request Ids with a match

cm.Parameters.AddWithValue("@serialNumber", _serialNumberToCheck);

System.Data.SqlClient.SqlDataReader dr = cm.ExecuteReader();

while (dr.Read())

{

matchList.Add(dr["UserRequestId"]);

}

dr.Close();

}

}

_commandResult = new SerialNumberCheckResult(matchList, matchList.Count > 0);

}

}

I'd then have a static method in your User Request class to make this logic reusable

/// <summary>

/// Returns whether a specified Query exists in the database using the ID.

/// </summary>

public static SerialNumberCheckResult SerialNumberExists(int serialNumberToCheck)

{

SerialNumberExistsCommand existsCommand;

existsCommand = DataPortal.Execute<SerialNumberExistsCommand >(new SerialNumberExistsCommand (serialNumberToCheck));

return existsCommand.CommandResult;

}

And last but not least, your rule (by the way, I'd recommend using the strongly typed generic rules, but no biggie):

private static bool ValidCertificateSerialNumberRequired(object target, Csla.Validation.RuleArgs e)

{

SerialNumberCheckResult existsResult = UserRequest.SerialNumberExists(((UserRequest)target).SerialNumber);

if (existsResult.Exists)

{

e.Description = "Duplicated Serial Number!";

// Note, you can store the list of user requests conflicting

// in a class variable or list them in the error message.

// The list of problem children are in existsResult.UserRequestIds

return false;

}

else

{

return true;

}

}

Copyright (c) Marimer LLC