Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc
Titulní Blogy Fotky Soubory

Jakublog

Hrátky s vlastnostmi II ( ASP.NET )

Pokračování minulého příspěvku - využití "property" třídy v prvcích ASP.NET.

Následující kód je jednoduchá předělávka třídy Property<T> do prostředí ASP.NET, kde se stav objektu uchovává ve ViewState.

public class WebProperty<T> : IWebProperty {
    #region Constants
    const string CACHE_KEY = "WebProperty.InitWebProperties.Cache";
    #endregion

    #region Fields
    private StateBag viewState;
    private string propertyKey;
    #endregion

    #region Properties
    public T Value {
    get {
            if ( this.viewState == null ){ throw new NullReferenceException("ViewState can not be null."); }
            object ret = this.viewState[this.propertyKey];
            if ( ret == null ) {
                return default( T );
            }
            return (T)ret;
        }
        set { this.viewState[this.propertyKey] = value; }
    }
    object IWebProperty.Value {
        get { return this.Value; }
        set { this.Value = (T)value; }
    }
    public StateBag ViewState{
        get{ return this.viewState; }
        set{ this.viewState = value; }
    }
    #endregion

    #region Constructors
    public WebProperty(string propertyKey) : this(propertyKey, null) {
    }
    public WebProperty(string propertyKey, StateBag viewState){
        if ( propertyKey == null ) { throw new ArgumentNullException( "propertyKey" ); }
        if ( propertyKey.Length == 0 ) { throw new ArgumentException( "Parameter 'propertyKey' must be longer than 0.", "propertyKey" ); }

        this.propertyKey = propertyKey;
        this.viewState = viewState;
    }
    #endregion

    #region Static methods
    // ... potrebne staticke metody ukazu nize
    #endregion
    }

V UserControlu pak takovouto vlastnost muzeme nadefinovat takto:

public partial class WebUserControl : System.Web.UI.UserControl{
    protected readonly WebProperty<string> NejakyText;
    public WebUserControl() {
        WebProperty<object>.InitWebProperties( this, this.ViewState, true ); // toto volani bude za chvili vysvetleno
    }
}

Protoze se kazda instance tridy WebProperty<T> musí odvolávat do ViewState nějakým klíčem, a nikdo jistě nechce tyto klíče vypisovat ručně ( alespoň já ne :) ), tak třída WebProperty<T> obsahuje statickou metodu InitWebProperties(), která nainicializuje tyto proměnné/vlastnosti za nás.
Předává se jí instance, která proměnné obsahuje, instance třídy StateBag, která bude použita pro načítání/ukládání hodnot a nakonec přepínač, zda si informace pro inicializaci proměných tohoto typu třídy má uložit do cache.

Kód, pro statických metod třídy WebProperty<T>:

    private static FieldInfo[] GetPropertyFields( Type componentType, bool useCache ) {
    object obj = HttpContext.Current.Cache[CACHE_KEY];

    Dictionary<Type, FieldInfo[]> cache;
        if ( obj is Dictionary<Type, FieldInfo[]> ) {
            cache = (Dictionary<Type, FieldInfo[]>)obj;
        }
        else {
            cache = new Dictionary<Type, FieldInfo[]>();
        }

        FieldInfo[] fields;
        if ( useCache == false || cache.ContainsKey( componentType ) ) {
            fields = (FieldInfo[])cache[componentType];
        }
        else {
            Type basePropertyType = typeof( IWebProperty );
            string strBasePropertyType = basePropertyType.FullName;

            FieldInfo[] tmpFields = componentType.GetFields( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
            List<FieldInfo> lst = new List<FieldInfo>();
            foreach ( FieldInfo field in tmpFields ) {
                Type fieldType = field.FieldType;
                Type tmpType = fieldType.GetInterface( strBasePropertyType, false );
                if ( tmpType == basePropertyType ) {
                    lst.Add( field );
                }
            }

            fields = lst.ToArray();
        }

        if ( useCache ) {
            cache[componentType] = fields;
            HttpContext.Current.Cache[CACHE_KEY] = cache;
        }

        return fields;
    }
    public static void InitWebProperties( object instance, StateBag viewState ) {
        InitWebProperties( instance, viewState, false );
    }
    public static void InitWebProperties( object instance, StateBag viewState, bool useCache ) {
        if ( viewState == null ) { throw new ArgumentNullException( "viewState" ); }

        Type mainType = instance.GetType();

        FieldInfo[] fields = GetPropertyFields( mainType, useCache );

        foreach ( FieldInfo field in fields ) {
            string strID = "WebProperty_{0}" + field.Name;

            IWebProperty property;
            try {
                property = (IWebProperty)Activator.CreateInstance( field.FieldType, strID );
            }
            catch ( Exception exc ) {
                string msg = string.Format("Type '{0}' can not be created.", strID);
                throw new TypeLoadException( msg, exc );             }
            property.ViewState = viewState;

            field.SetValue( instance, property );
        }
    }


Poslední ( třetí ) část bude o použití "property" třídy ve WinForms + automatická podpora práce s PropertiesWindow ( to se pravděpodobně bude týkat i asp.net ).
Zveřejněno Thursday, February 02, 2006 6:51 PM by Jakublog

Komentář

 

jachymko napsal:

jenom takovou pripominku ke castovani: kod

try {
x = (T)Neco();
} catch(InvalidCastException) {
}

je zbytecne pomalej v pripade ze selze. zvlast pokud vyjimku chces zachytit a jenom thrownout jinou. zasada je hazet vyjimek co nejmin, tohle by se dalo krasne prepsat na

x = (Neco() as T);
if(x == null) throw MyException();

coz by teoreticky mohlo byt dvakrat rychlejsi. navic mi tenhle kod prijde i na pohled prehlednejsi, pac ho nehyzdi zadny zbytecny try {}...
February 6, 2006 4:36 PM
 

jachymko napsal:

a taky si nejsem uplne jistej jestli je dobrej napad takhle zneuzit TypeLoadException, kterou iirc haze framework sam o sobe. tady by se mi vic libila naka vlastni exception, idealne zdedena od ArgumentException, nebo tak neco. ale rozhodne NE systemova vyjimka
February 6, 2006 4:46 PM
 

jachymko napsal:

a posledni vec, nepouzival bych dementni funkci GetInterface, ktera bere string. opet zbytecna prace, vytvori se string, musi se najit Type s timto jmenem a pak se muze porovnavat. funkce IsAssignableFrom splni ucel mnohem lip:

typeof(IWebProperty).IsAssignableFrom(fieldType)
February 6, 2006 4:49 PM
 

Jakub Müller napsal:

x = (Neco() as T);
if(x == null) throw MyException();

To nemuzu pouzit, pac operator "as" nelze pouzivat na hodnotovy typy.

btw.

try {
property = (IWebProperty)Activator.CreateInstance( field.FieldType, strID );
}
catch ( Exception exc ) {
string msg = string.Format("Type '{0}' can not be created.", strID);
throw new TypeLoadException( msg, exc );
}

Neni to same jako

try {
x = (T)Neco();
} catch(InvalidCastException) {
}

pri vytvareni muze nastat i jina vyjimka.

typeof(IWebProperty).IsAssignableFrom(fieldType) tam pouziju a pro priste zapamatuju ;)
February 6, 2006 8:33 PM
 

jachymko napsal:

Activator.CreateInstance vraci valuetypes zaboxovany v objectu, takze ti nic nebrani to pomoci operatoru as precastovat na IWebProperty, coz je stejne jako ostatni interfacy ref type.

nemyslel jsem ze je ten kod stejnej, byl to jenom priklad. kazdopade stojim si za tim ze uzivatelskej kod by rozhodne nemel nikdy hazet nic co je zdedeny od SystemException nebo to hazi framework v jinejch situacich.

jak pomuzes tomu kodu kterej to zavolal identifikovat zdroj chyby, tim ze puvodni vyjimku zabalis do chyby KTERA SE NESTALA?

http://msdn2.microsoft.com/library/system.typeloadexception.aspx
TypeLoadException is thrown when the common language runtime cannot find the assembly, the type within the assembly, or cannot load the type.
February 7, 2006 2:06 PM
 

Augi napsal:

As sice nejde použít, protože může jít o hodnotový typ, ale je možné provést kontrolu na typ pomocí is.
September 18, 2006 7:40 PM
Neregistrovaní uživatele nemužou přidávat komentáře.
Powered by Community Server (Personal Edition), by Telligent Systems