Reflectioning
12 отговора
439 прегледа
Reflectioning
Дайте някаква информация за reflectioning-a.
Къде точно може да намери той приложение?
И на някой налагало ли му се е да го използва, ако да, къде?
Къде точно може да намери той приложение?
И на някой налагало ли му се е да го използва, ако да, къде?
Re: Reflectioning
Reflection, a не Reflectioning
а) виж MSDN
б) да, често, за late binding и aspect-oriented дивотии
а) виж MSDN
б) да, често, за late binding и aspect-oriented дивотии
Редактирано от gruhal на
Обикновено
когато работя с ресурси (а при мене е постоянно !)
Това е обръщение към асемблито което се изпълнява в момента- например ;-)
"System.Reflection.Assembly.GetExecutingAssembly"
А това е конкретен (леко изменен) пример:
"m_objResourceManager = New ResourceManager("My_FrameworkResources", System.Reflection.Assembly.GetExecutingAssembly)"
UB40 !
Това е обръщение към асемблито което се изпълнява в момента- например ;-)
"System.Reflection.Assembly.GetExecutingAssembly"
А това е конкретен (леко изменен) пример:
"m_objResourceManager = New ResourceManager("My_FrameworkResources", System.Reflection.Assembly.GetExecutingAssembly)"
UB40 !
Re: Reflectioning
Reflection дава възможност на една програма динамично да анализира и открива елементи (като fields, methods, constructors, attributes) на типове (като класове, интерфейси, структури) във вече съществуващи асемблита, тъй като всеки компилатор който генерира CLR код, генерира и metadata която описва съответните елементи. Като получим тази информация, може просто да я покажем (decompilers, class browsers, object inspectors) или да инстанциираме и извикваме функционалност по много динамичен начин. Reflection не се ограничава само до анализ на съществуващ компилиран код. Може да се използва за генериране на нови типове изцяло в паметта и пак да ги извикваме като обикновен код. Например: код който изучава функционалноста на един интерфейс и му генерира динамично wrapper class (познато още като adapter или proxy). Това се използва в .NET remoting architecture, която на мене ми била супер мътна според много хора в този форум.
Ако тази идея за self-generating code се развие достатъчно завбъдеще, програмата може да стане автор на самата себе си и тя да решава как ще се изпълянва. Нещо като The Matrix

... така един алгоритъм може да се изгради динамично без изобщо да е писан от програмист или да се съхранява на сорс файл, примерно generic algorithms които сами си модифицират поведението за да намерят решение на трудна математическа задача.
На пръв поглед reflection може да изглежда трудно за разбиране или ненужно, обаче много tools като IntelliSense, Class View, Property Windows използват reflection особено усилено :)). Освен това .Net Framework го използва за object serialization, the .NET remoting architecture, and dynamic method dispatch. Всъщност виртуално всяка една .net програма използва reflection по много начини без даже програмиста да осъзнава за това.
Най-важен в цялата тази работа е класа Type. Той съдържа информация (metadata) за останалите типове. Но в същността си е просто един тип клас най-обикновен, малко абстрактен но пък ти позволява достъп то metadata-а на другите класове.
Как работи цялата тази работа:
Най-важното е че всеки тип наследяващ Object поддържа GetType метода, който ти връща обект от тип Type.
Type* Object::GetType()
Type класа има много методи, можеш да ги видиш в документацията, обаче ми е съвестно ако не се спра на най-важните:
Можеш да вземеш типа на даден клас чрез името му:
Type* Type::GetType(String* typeName)
или атрибутите му:
TypeAttributes Type::get_Attributes()
или базовия клас:
Type* Type::get_BaseType()
или всичките му методи:
MethodInfo* Type::GetMethods() []
и така нататък ...
За да рефлектираш определен тип, първо трябва да заредиш асемблито му в паметта. Това става най-лесно с Assembly класа.
Assembly* Assembly::LoadFrom(String* assemblyFile)
и после си го разглеждаш както искаш, примерно:
Type* Assembly::GetTypes() []
Когато го получиш този масив, може да рефлектираш всеки тип в него като му достъпваш: MethodInfo, FieldInfo, ParameterInfo и каквото там ти трябва.
Аз лично най-често ползвам reflection за custom attributes, interface wrappers, и stored procedures wrappers ... а за stored proc-овете направо се е разбило ... нали се сещаш как се викаха stored procs от ADO ... описваш всеки параметър един по един за всяка процедура ... и в една голяма бизнес система смятай за кво писане на конвеир става въпрос ... с reflection работата с stored procedures добива нови измерения


XM TurboCT .. where comfort meets power
Ако тази идея за self-generating code се развие достатъчно завбъдеще, програмата може да стане автор на самата себе си и тя да решава как ще се изпълянва. Нещо като The Matrix
На пръв поглед reflection може да изглежда трудно за разбиране или ненужно, обаче много tools като IntelliSense, Class View, Property Windows използват reflection особено усилено :)). Освен това .Net Framework го използва за object serialization, the .NET remoting architecture, and dynamic method dispatch. Всъщност виртуално всяка една .net програма използва reflection по много начини без даже програмиста да осъзнава за това.
Най-важен в цялата тази работа е класа Type. Той съдържа информация (metadata) за останалите типове. Но в същността си е просто един тип клас най-обикновен, малко абстрактен но пък ти позволява достъп то metadata-а на другите класове.
Как работи цялата тази работа:
Най-важното е че всеки тип наследяващ Object поддържа GetType метода, който ти връща обект от тип Type.
Type* Object::GetType()
Type класа има много методи, можеш да ги видиш в документацията, обаче ми е съвестно ако не се спра на най-важните:
Можеш да вземеш типа на даден клас чрез името му:
Type* Type::GetType(String* typeName)
или атрибутите му:
TypeAttributes Type::get_Attributes()
или базовия клас:
Type* Type::get_BaseType()
или всичките му методи:
MethodInfo* Type::GetMethods() []
и така нататък ...
За да рефлектираш определен тип, първо трябва да заредиш асемблито му в паметта. Това става най-лесно с Assembly класа.
Assembly* Assembly::LoadFrom(String* assemblyFile)
и после си го разглеждаш както искаш, примерно:
Type* Assembly::GetTypes() []
Когато го получиш този масив, може да рефлектираш всеки тип в него като му достъпваш: MethodInfo, FieldInfo, ParameterInfo и каквото там ти трябва.
Аз лично най-често ползвам reflection за custom attributes, interface wrappers, и stored procedures wrappers ... а за stored proc-овете направо се е разбило ... нали се сещаш как се викаха stored procs от ADO ... описваш всеки параметър един по един за всяка процедура ... и в една голяма бизнес система смятай за кво писане на конвеир става въпрос ... с reflection работата с stored procedures добива нови измерения
XM TurboCT .. where comfort meets power
Re: Reflectioning
я кажи как точно използваш рефлекшън са сторнати процедури (в SQL Server?), че не ми стана ясно?
Re: Reflectioning
не използва рефлекшън точно със самите сторнати процедури (предполагам), а им генерира параметер байндинга он дъ флай. Слагаш си къстъм атрибут, който ти казва кое пропърти на обекта към кое поле на таблицата се мапва и после през рефлекшън мяташ.
(сори за кирилицата ама ме мързи да превключвам)
(сори за кирилицата ама ме мързи да превключвам)
reflection в stored procedures
Главния недостатък на ползването на SQL stored procedures от С# код е това че трявба да напаснеш (match) прекалено много типове. И се налага да пишеш дълги wrapper functions. Ако ползваш stored procedures доста, знаеш колко е досадно да описваш всеки параметър: име, тип, размер и т.н. ... освен това ако промениш процедурата и пак трябва да ходиш да променяш кода в data service layer-a. Примерно имаш следната процедура:
CREATE PROCEDURE GetData @ID int
AS
SELECT * FROM DataTable WHERE [ID] = @ID
за да извикаш тази процедура обикновенно и правиш wrapper функция като тази:
Recordset GetData(Connection cn, int nID)
{
Command cm = new Command();
cm.ActiveConnection = cn;
cm.CommandText = "GetData";
cm.CommandType = adCmdStoredProc;
Parameters params = cm.Parameters;
params.Append(cm.CreateParameter("@RETURN_VALUE", adInteger, adParamReturnValue, 0);
params.Append(cm.CreateParameter("@ID", adInteger, adParamInput, 0);
params("@ID").Value = nID;
Recordset r = new Recordset();
r.CursorLocation = adUseClient;
r.Open ( cm, , adOpenStatic, adLockReadOnly );
r.ActiveConnection = null;
return r;
}
Този подход съвсем не е зле в ерата когато нямаше .NET, обаче сега като видиш половината ти екип през половината ден да пише такъв код и после половината му рипорт да е: "Емиии ... писах wrapper functions ..." и си викаш - отиде ми и производителност и deadlines и кво ли не ... и почваш да мъдриш некви гениални идеи ... примерно: да напишеш генератор който автоматично прави wrapper functions чрез SQLDMO ... или пък да правиш XML дефиниции на процедурите и после с XSLT (за което според някой хора в този форум аз изобщо не съм бил чувал) да генерираш кода на wrapper function-a.
Но първите идеи не е задължително да са винаги най-подходящите ...
Как reflection променя цялата тази картинка ???!!!
Лесно може да си представиш как чрез reflection може да обходиш параметрите на дадена функция и да създадеш SqlCommand обект с респективните SqlParameter обекти. Всичко което трябва да направиш е да дефинираш функции които да представляват твоите процедури и останалото ще се прави от една utility function която динамично ще създава и конфигурира SqlCommand обекти по време на изпълнение на програмата за да се обръща към базата данни. Примерно нещо такова:
sealed class SqlCommandGenerator
{
private SqlCommandGenerator() {}
public static SqlCommand GenerateCommand(SqlConnection connection,
MethodInfo method, object[] values)
{
SqlCommand command = new SqlCommand(method.Name, connection);
command.CommandType = CommandType.StoredProcedure;
ParameterInfo[] parameters = method.GetParameters();
for (int i = 1; i < parameters.Length; i++)
{
SqlParameter sqlParameter = new SqlParameter();
sqlParameter.ParameterName = "@" + parameters[ i ].Name;
sqlParameter.Value = values[ i ];
command.Parameters.Add(sqlParameter);
}
return command;
}
}
Като имаш обект от тип Connection, метаданните на метода и стойностите на параметрите GenerateCommand генерира и изпълнява SqlCommand обект все едно че си го писал като сорс. Даже няма нужда да да ползвам типа на параметъра да получа SqlDbType за да дам типа на SqlParametera, защото SqlParameter вече има такава функционалност като подаваш Value и го прави вместо тебе.
След това трябва да извикаш GenerateCommand от GetData и да използваш генерираната команда:
public static DataSet GetData(SqlConnection connection, int Id)
{
MethodInfo methodInfo =
typeof(myDatabase).GetMethod("GetData",
new Type[] { typeof(SqlConnection), typeof(int) });
SqlCommand command =
SqlCommandGenerator.GenerateCommand(connection,
methodInfo, new object[] { Id });
DataSet dataSet = new DataSet();
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
dataAdapter.Fill(dataSet);
return dataSet;
}
До тук добре ... обаче не е идеално защото пак ще ти се налага да правиш промени в кода ако се промени процедурата, примерно смениш името. Това лесно може да се реши ако се разходиш по стека и вземеш frame-а на въпросния метод. Наред с всички хубави неща в FCL, има еднa особено приятна функция, леко скрита ама това да не те притеснява, MehodBase.GetCurrentMethod. Тя ти връща MethodBase (MethodInfo или ConstructorInfo) за функцията която я вика. И става нещо такова:
public static DataSet GetData(SqlConnection connection, int Id)
{
SqlCommand command = SqlCommandGenerator.GenerateCommand
(connection, (MethodInfo) MethodBase.GetCurrentMethod(),
new object[] { Id });
DataSet dataSet = new DataSet();
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
dataAdapter.Fill(dataSet);
return dataSet;
}
горе долу това е идеята макар че има случаи когато имаш безименни параметри или пък типовете на параметрите са леко различни от тези на метода или пък NULL values ... ама и те си имат решение - Attributes ...

XM TurboCT .. where comfort meets power
CREATE PROCEDURE GetData @ID int
AS
SELECT * FROM DataTable WHERE [ID] = @ID
за да извикаш тази процедура обикновенно и правиш wrapper функция като тази:
Recordset GetData(Connection cn, int nID)
{
Command cm = new Command();
cm.ActiveConnection = cn;
cm.CommandText = "GetData";
cm.CommandType = adCmdStoredProc;
Parameters params = cm.Parameters;
params.Append(cm.CreateParameter("@RETURN_VALUE", adInteger, adParamReturnValue, 0);
params.Append(cm.CreateParameter("@ID", adInteger, adParamInput, 0);
params("@ID").Value = nID;
Recordset r = new Recordset();
r.CursorLocation = adUseClient;
r.Open ( cm, , adOpenStatic, adLockReadOnly );
r.ActiveConnection = null;
return r;
}
Този подход съвсем не е зле в ерата когато нямаше .NET, обаче сега като видиш половината ти екип през половината ден да пише такъв код и после половината му рипорт да е: "Емиии ... писах wrapper functions ..." и си викаш - отиде ми и производителност и deadlines и кво ли не ... и почваш да мъдриш некви гениални идеи ... примерно: да напишеш генератор който автоматично прави wrapper functions чрез SQLDMO ... или пък да правиш XML дефиниции на процедурите и после с XSLT (за което според някой хора в този форум аз изобщо не съм бил чувал) да генерираш кода на wrapper function-a.
Но първите идеи не е задължително да са винаги най-подходящите ...
Как reflection променя цялата тази картинка ???!!!
Лесно може да си представиш как чрез reflection може да обходиш параметрите на дадена функция и да създадеш SqlCommand обект с респективните SqlParameter обекти. Всичко което трябва да направиш е да дефинираш функции които да представляват твоите процедури и останалото ще се прави от една utility function която динамично ще създава и конфигурира SqlCommand обекти по време на изпълнение на програмата за да се обръща към базата данни. Примерно нещо такова:
sealed class SqlCommandGenerator
{
private SqlCommandGenerator() {}
public static SqlCommand GenerateCommand(SqlConnection connection,
MethodInfo method, object[] values)
{
SqlCommand command = new SqlCommand(method.Name, connection);
command.CommandType = CommandType.StoredProcedure;
ParameterInfo[] parameters = method.GetParameters();
for (int i = 1; i < parameters.Length; i++)
{
SqlParameter sqlParameter = new SqlParameter();
sqlParameter.ParameterName = "@" + parameters[ i ].Name;
sqlParameter.Value = values[ i ];
command.Parameters.Add(sqlParameter);
}
return command;
}
}
Като имаш обект от тип Connection, метаданните на метода и стойностите на параметрите GenerateCommand генерира и изпълнява SqlCommand обект все едно че си го писал като сорс. Даже няма нужда да да ползвам типа на параметъра да получа SqlDbType за да дам типа на SqlParametera, защото SqlParameter вече има такава функционалност като подаваш Value и го прави вместо тебе.
След това трябва да извикаш GenerateCommand от GetData и да използваш генерираната команда:
public static DataSet GetData(SqlConnection connection, int Id)
{
MethodInfo methodInfo =
typeof(myDatabase).GetMethod("GetData",
new Type[] { typeof(SqlConnection), typeof(int) });
SqlCommand command =
SqlCommandGenerator.GenerateCommand(connection,
methodInfo, new object[] { Id });
DataSet dataSet = new DataSet();
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
dataAdapter.Fill(dataSet);
return dataSet;
}
До тук добре ... обаче не е идеално защото пак ще ти се налага да правиш промени в кода ако се промени процедурата, примерно смениш името. Това лесно може да се реши ако се разходиш по стека и вземеш frame-а на въпросния метод. Наред с всички хубави неща в FCL, има еднa особено приятна функция, леко скрита ама това да не те притеснява, MehodBase.GetCurrentMethod. Тя ти връща MethodBase (MethodInfo или ConstructorInfo) за функцията която я вика. И става нещо такова:
public static DataSet GetData(SqlConnection connection, int Id)
{
SqlCommand command = SqlCommandGenerator.GenerateCommand
(connection, (MethodInfo) MethodBase.GetCurrentMethod(),
new object[] { Id });
DataSet dataSet = new DataSet();
SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
dataAdapter.Fill(dataSet);
return dataSet;
}
горе долу това е идеята макар че има случаи когато имаш безименни параметри или пък типовете на параметрите са леко различни от тези на метода или пък NULL values ... ама и те си имат решение - Attributes ...
XM TurboCT .. where comfort meets power
Re: reflection в stored procedures
Всъщност вече има доста добри комерсиални пакети (ORM.NET) примерно, които четат схемата от SQL Server-a и създават всички необходими wrapper класове за основните queries - Select, Update, Insert, Delete.
Аз лично ползвам безплатния LLBL Gen, потърси Гуугъл. Чете вече създадена база и генерира всички основни queries за всяка таблица във формат на сторнати процедури, плюс евентуалните joins като следи foreign keys. След това създава C# wrapper класове с обекти, представящи всяка таблица и съответните методи, които викат stored procedures layer. Доста е удобно, и макар че разбира се не хваща всички случаи, е повече от достатъчно като за начало.
А иначе идеята ти е повече от добра и интересна. Похвално е, че отделяш толкова време за такива постове, лошото е, че малко хора ги оценяват. Не се ядосвай, нивото е доста високо, и повечето пичове нямат идея за какво точно говориш. А иначе къде работиш, освен че си шеф на Соди-Девня :) ?
Аз лично ползвам безплатния LLBL Gen, потърси Гуугъл. Чете вече създадена база и генерира всички основни queries за всяка таблица във формат на сторнати процедури, плюс евентуалните joins като следи foreign keys. След това създава C# wrapper класове с обекти, представящи всяка таблица и съответните методи, които викат stored procedures layer. Доста е удобно, и макар че разбира се не хваща всички случаи, е повече от достатъчно като за начало.
А иначе идеята ти е повече от добра и интересна. Похвално е, че отделяш толкова време за такива постове, лошото е, че малко хора ги оценяват. Не се ядосвай, нивото е доста високо, и повечето пичове нямат идея за какво точно говориш. А иначе къде работиш, освен че си шеф на Соди-Девня :) ?
Re: reflection в stored procedures
Благодаря за изчерпателния отговор. Много хитро това бе!
А да ви питам намесваш ли някъде в цялата работа метода
SqlCommandBuilder.DeriveParameters
който изкарва имената, типовете и дали параметрите са input/output от базата?
Щото OLEDB Providera си иска параметрите по ред а пък SQL Providera си ги турси по имена. Въврви пиши независима обвивка после
.
А да ви питам намесваш ли някъде в цялата работа метода
SqlCommandBuilder.DeriveParameters
който изкарва имената, типовете и дали параметрите са input/output от базата?
Щото OLEDB Providera си иска параметрите по ред а пък SQL Providera си ги турси по имена. Въврви пиши независима обвивка после
Re: reflection в stored procedures
мдааа .... както казах тази идея не е напълно изчепателна тъй като не решава няколко проблема:
1. безименни параметри, тоест по ред
2. parameter's direction - input/output
3. NULL values
обаче те също си имат решение - Custom Attributes
сега точно нямам време да опиша цялата процедура надълго и широко щото следобед заминавам на море, а пък трявба да попривърша тука с работата че иначе не ме пускат
, ама като се върна ще пусна един пост и за тях щото си заслужава да се види
DeriveParameters не ги ползвам защото изискват допълнителен roundtrip към сървъра ... което ми се струва скъпичко
XM TurboCT .. where comfort meets power
1. безименни параметри, тоест по ред
2. parameter's direction - input/output
3. NULL values
обаче те също си имат решение - Custom Attributes
сега точно нямам време да опиша цялата процедура надълго и широко щото следобед заминавам на море, а пък трявба да попривърша тука с работата че иначе не ме пускат
DeriveParameters не ги ползвам защото изискват допълнителен roundtrip към сървъра ... което ми се струва скъпичко
XM TurboCT .. where comfort meets power
Re: reflection в stored procedures
За сторнатите процедури е по-добре да си ги издърпаме с мишката от Server Explorera, необходимия код бива генериран от Studioto.