views:

524

answers:

4

Hi! I need to intercept the Save method, do some validations, alter some properties and then let it go again normally.

How can I do this?

Thanks! Alex

A: 

You can use the partial class mechanism to create your own version of Save which does validation and calls the SubSonic generated Save. e.g.

namespace YourNameSpace
{
   public partial class YourTable : IActiveRecord
   { 
       public void MySave()
       {
           // insert your validation here
           this.Save()
       }
   }
}
sparks
Hi mate, thanks for the reply.This isn't really a good option because it's easy to forget there's a custom save and use the ActiveRecord default.I don't need an overload, I need something like a OnSave(object sender, CancelEventArgs e) method where I can validate, change or even cancel the save process independently of the save overload I'm using.I was asking because I could be missing something but if not I'll have to implement if myself.
AlexCode
A: 

Each ActiveRecord class comes with a partial "OnSave()" method so all you need to do is create a partial and then override the partial OnSave() - just like in Linq To Sql:

public partial class MyClass{
   partial OnSave(){
   //magic
   }

}
Rob Conery
Hi Rob, thanks for the reply.The thing is that the partial method available is OnSaved() that indicates that is called after the Save() instructions.When is this actually fired, before of after the actual Save()?Thanks!Alex
AlexCode
AlexCode
+3  A: 

I would recommend adding the following partial methods to be fired before their actual action:

OnSave(CancelEventArgs e); 
OnAdd(CancelEventArgs e); 
OnUpdate(CancelEventArgs e); 
OnDelete(CancelEventArgs e);

This isn't an event but I would use CancelEventArgs anyway, it's friendly, people know it and know how to use it and with it the actual action can be canceled from the partial methods.

These two should be added too to the list of the existing ones that fire after their actual action:

OnAdded(); 
OnUpdated();

I don't like that OnAdded() name but if Add was adopted instead of Insert then we must stick with it.

And that's it... With these partials I think we cover all the befores and afters of the actual data persistence methods giving us a greater flexibility to do whatever we want we our data.

I can implement this but I'm always afraid of touching the tt files because future updates will wipe off all my custom changes! :)

Thanks! Alex

AlexCode
+1  A: 

In 2.x, there was a BeforeInsert and BeforeUpdate methods you could override.

I think you need to add an OnSaving method in the ActiveRecord.tt file. Place this next to the OnSaved() sig:

  partial void OnSaving();

Then update your ActiveRecord.tt file. 2 changes that I see:

Update the Update method

    public void Update(IDataProvider provider)
    {      
    <#if(tbl.Columns.Any(x=>x.Name=="ModifiedBy")){#>
        if(String.IsNullOrEmpty(this.ModifiedBy))
            this.ModifiedBy=Environment.UserName;
     <#}#>
    <#if(tbl.Columns.Any(x=>x.Name=="ModifiedOn")){#>
        this.ModifiedOn=DateTime.Now;
     <#}#>

       **OnSaving();**

        if(this._dirtyColumns.Count>0)
            _repo.Update(this,provider);
        OnSaved();
   }

and then update the Add method

public void Add(IDataProvider provider)
{ 
<#if(tbl.Columns.Any(x=>x.Name=="CreatedOn")){#>

            this.CreatedOn=DateTime.Now;
<#}#>
<#if(tbl.Columns.Any(x=>x.Name=="CreatedBy")){#>
            if(String.IsNullOrEmpty(this.CreatedBy))
                this.CreatedBy=Environment.UserName;
<#}#>
<#if(tbl.Columns.Any(x=>x.Name=="ModifiedOn")){#>
            this.ModifiedOn=DateTime.Now;
<#}#>
<#if(tbl.Columns.Any(x=>x.Name=="ModifiedBy")){#>
            if(String.IsNullOrEmpty(this.ModifiedBy))
                this.ModifiedBy=Environment.UserName;
<#}#>

            **OnSaving();**            

            var key=KeyValue();
            if(key==null){
                var newKey=_repo.Add(this,provider);
                this.SetKeyValue(newKey);
            }else{
                _repo.Add(this,provider);
            }
            SetIsNew(false);
            OnSaved();
}

Finally, you need to use your partial classes to override the OnSaving() method to update the values. I'm doing something very similar since I'm not using the convention of modifiedby and createdon (I have underscores there).

Jim W
Yeah... that's almost it.I just haven't done it yet because I'm "afraid' of future subsonic tt updates.How are you handling your custom changes?Another thing is that I need to be able to cancel the changes before the actual action, therefore I need the CancelEventArgs on the On* methods.
AlexCode
You don't really have a choice. If you need that, then you're going to have to edit the tt files. I'd like to see the OnSaving added to the default templates, too (something similar was there in the previous release, so there's a chance it might get accepted). You can post a issue at SubSonic github site (under the subsonic-3/templates project). It might get reviewed and added in a later version. I don't like editing the tt files either (I'd rather stick to just making partial classes when possible), but if you need it to work, then you're going to have to do it.
Jim W
Since you need to cancel it, did you look for an Validate method? It used to exist in 2.x and I think it was called before the Insert/Update commands started. I think there was a way for you to cancel the insert/update by setting it to invalid. I'm new to 3, so I only have the previous release to compare it to.
Jim W