views:

43

answers:

3

Hi Guys

Im using subsonic 2.2

I tried asking this question another way but didnt get the answer i was looking for.

Basically i ususally include validation at page level or in my code behind for my user controls or aspx pages. However i haev seen some small bits of info advising this can be done within partial classes generated from subsonic.

So my question is, where do i put these, are there particular events i add my validation / business logic into such as inserting, or updating. - If so, and validation isnt met, how do i stop the insert or update. And if anyone has a code example of how this looks it would be great to start me off.

Any info greatly appreciated.

A: 

First you should create a partial class for you DAL object you want to use. In my project I have a folder Generated where the generated classes live in and I have another folder Extended.

Let's say you have a Subsonic generated class Product. Create a new file Product.cs in your Extended (or whatever) folder an create a partial class Product and ensure that the namespace matches the subsonic generated classes namespace.

namespace Your.Namespace.DAL
{
    public partial class Product
    {
    }
}

Now you have the ability to extend the product class. The interesting part ist that subsonic offers some methods to override.

namespace Your.Namespace.DAL
{
    public partial class Product
    {

        public override bool Validate()
        {

            ValidateColumnSettings();

            if (string.IsNullOrEmpty(this.ProductName))
                this.Errors.Add("ProductName cannot be empty");

            return Errors.Count == 0;
        }

        // another way
        protected override void BeforeValidate()
        {
            if (string.IsNullOrEmpty(this.ProductName))
                throw new Exception("ProductName cannot be empty");
        }

        protected override void BeforeInsert()
        {
           this.ProductUUID = Guid.NewGuid().ToString();
        }

        protected override void BeforeUpdate()
        {
           this.Total = this.Net + this.Tax;
        }

        protected override void AfterCommit()
        {
            DB.Update<ProductSales>()
                  .Set(ProductSales.ProductName).EqualTo(this.ProductName)
                  .Where(ProductSales.ProductId).IsEqualTo(this.ProductId)
                  .Execute();
        }

    }
}
SchlaWiener
A: 

Hi Schlawiener

Thanks for the response, but can you confirm this for me as im alittle confused, if your validating the column (ProductName) value within validate() or the beforevalidate() is string empty or NULL, doesnt this mean that the insert / update has already been actioned, as otherwise it wouldnt know that youve tried to insert or update a null value from the UI / aspx fields within the page to the column??

Also, within asp.net insert or updating events we use e.cancel = true to stop the insert update, if beforevalidate failes does it automatically stop the action to insert or update?

If this is the case, isnt it eaiser to add page level validation to stop the insert or update being fired in the first place.

I guess im alittle confused at the lifecyle for these methods and when they come into play

Dan
I added another post with some more detailed explanations
SchlaWiener
A: 

In response to Dan's question:

First, have a look here: http://github.com/subsonic/SubSonic-2.0/blob/master/SubSonic/ActiveRecord/ActiveRecord.cs

In this file lives the whole logic I showed in my other post.

  • Validate: Is called during Save(), if Validate() returns false an exception is thrown. Get's only called if the Property ValidateWhenSaving (which is a constant so you have to recompile SubSonic to change it) is true (default)

  • BeforeValidate: Is called during Save() when ValidateWhenSaving is true. Does nothing by default

  • BeforeInsert: Is called during Save() if the record is new. Does nothing by default.

  • BeforeUpdate: Is called during Save() if the record is new. Does nothing by default.

  • AfterCommit: Is called after sucessfully inserting/updating a record. Does nothing by default.

In my Validate() example, I first let the default ValidatColumnSettings() method run, which will add errors like "Maximum String lenght exceeded for column ProductName" if product name is longer than the value defined in the database. Then I add another errorstring if ProductName is empty and return false if the overall error count is bigger than zero.

This will throw an exception during Save() so you can't store the record in the DB.

I would suggest you call Validate() yourself and if it returns false you display the elements of this.Errors at the bottom of the page (the easy way) or (more elegant) you create a Dictionary<string, string> where the key is the columnname and the value is the reason.

    private Dictionary<string, string> CustomErrors = new Dictionary<string, string>
    protected override bool Validate()
    {

        this.CustomErrors.Clear();
        ValidateColumnSettings();

        if (string.IsNullOrEmpty(this.ProductName))
            this.CustomErrors.Add(this.Columns.ProductName, "cannot be empty");

        if (this.UnitPrice < 0)
            this.CustomErrors.Add(this.Columns.UnitPrice, "has to be 0 or bigger");

        return this.CustomErrors.Count == 0 && Errors.Count == 0;
    }

Then if Validate() returns false you can add the reason directly besides/below the right field in your webpage.

If Validate() returns true you can safely call Save() but keep in mind that Save() could throw other errors during persistance like "Dublicate Key ...";

SchlaWiener