Web-Tor gav mig denna http://www.sitepoint.com/article/921/1
den gav mig mcket bra info.... tack för den Web-Tor :bire
Vill nån annan ge mig mer info är det bara att posta vidare...
Mvh icaaq
15 svar · 823 visningar · startad av icaaq
Hur skulle ni gjort en gästbok med paging?
Jag sitter och kollar igenom de olika alternativen och jag tror att det smidigaste är en repeater med en hemmasnickrad pagingmodul....
men jag vill gärna ha lite synpunkter på hur ni har/skulle göra
Mvh icaaq
Web-Tor gav mig denna http://www.sitepoint.com/article/921/1
den gav mig mcket bra info.... tack för den Web-Tor :bire
Vill nån annan ge mig mer info är det bara att posta vidare...
Mvh icaaq
Klart användbar, den tackar vi för.
En av mina första asp.net apps var denna: http://www.mrwize.nu/corallrevets/guestbook/index.aspx och då vart det en egen hemma-snickrad paging mot en repeater.
Ska nog kika igenom den koden och bygga om mot tipset ovan :e
cya,
PatrikB
JAg tänkte använda mig av koden som fanns i artikeln men jag vet inte vilket sätt jag ska föra över datan från mitt datalayer till datalisten...... vad är att föredra en array eller ??
Mvh icaaq
Själv hämtar jag alla data i stort sett med DataReaders som jag sedan loopar igenom.
I varje loop initierar jag ett nytt object med data från databasen och sedan lägger jag till det objectet till en ObjectCollection som oftast har ärvt egenskaper av ArrayList eller HashTable ... eller implementerar IList etc. som sedan returneras istället för DataSets etc.
Även om DataSets är "best practice" enligt MS så använder sig den "klassen" oxå av DataReaders i botten för att populera sig.
Kika in Jimmy Nilssons artiklar om DataContainers på https://www.informit.com
cya,
PatrikB
'## -- i BisDataAccessLayer --
Public Function GetClientList() As ClientCollection
Dim Query As String
Dim _clientList As New ClientCollection()
Query = "SELECT * FROM Clients ORDER BY Client_Name ASC"
Dim _dr As SqlDataReader = MyNamespace.MyWrapper.GetDataReader(Query)
While _dr.Read()
Dim _client As New ClientInfo()
_client.ClientID = _dr("Client_ClientID")
_client.Name = _dr("Client_Name")
_clientList.Add(_client)
End While
Return _clientList
End Function
'## -- i BisLayer --
Public Class ClientCollection
Inherits ArrayList
Public Sub New()
End Sub
End Class
Public Class ClientInfo
Private m_clientID As Integer
Private m_clientName As String
Public Property ClientID() As Integer
Get
Return m_clientID
End Get
Set(ByVal Value As Integer)
m_clientID = Value
End Set
End Property
Public Property Name() As String
Get
Return m_clientName
End Get
Set(ByVal Value As String)
m_clientName = Value
End Set
End Property
Public Sub New()
End Sub
Public Function ListClients() As ClientCollection
Dim _bdal As New BisDataAccessLayer()
Return _bdal.GetClientList()
End Function
End Class
Sedan där du ska accessa, sätta datasource och binda data:
Dim _clientInfo As New ClientInfo()
Dim _pds As New PagedDataSource()
_pds.DataSource = _clientInfo.ListClients
_pds.BindData()
ett litet exempel, och kan säkert göras bättre ;) .... funkar bra för mig :e
editerat:
Man kan ju slå ihop BisDataAccessLayer.GetClientList med BisLayer.ListClients .... dvs endast ha: Design / BisLayer / Database
/editerat
cya,
PatrikB
Är en ArrayList flerdimensionell?
Vad menar du med "flerdimensionell"?
Jag ser en ArrayList som en lite mer avancerad array.
Kika i SDK'n och jämför de olika egenskaperna för Array klassen och ArrayList klassen
cya,
PatrikB
SDK skrev:
An Array can have multiple dimensions, while an ArrayList always has exactly one dimension.
Vad jag tänker på är att kan jag lägga flera värden i samma post? För i varje loop som DataReadern gör så har jag ju namn, epost, url och medelande som jag vill lägga till i samma post. Om ni förstår vad jag menar.....
Mvh icaaq
Han lägger in värdena i en class som han sen lägger till arraylisten alias classen ClientCollection. Och på så sätt får han in flera värden i en post i arraylisten.
Jepp,
det är ju det jag gör .... indirekt så att säga.
I varje loop initierar jag ett nytt objekt med olika egenskaper (metoder). Det är det objektet som man sedan lägger till i arraylisten så att varje index i arrayen är ett bis-objekt så att säga med alla dess egenskaper och metoder intakta :e
cya,
PatrikB
Egna klasser och kollection mot datasets är en debatterad fråga och jag med många med mig (inkls PatrikB) föredra klasser. Det finns många fördelar (och vissa nackdelar) och just paging och arraylist är riktigt enkelt eftersom du kan ta ut den delmängd från din arraylist.
Du säger på vilken post den skall starta och hur många poster som skall ut. Och vips så har du fått en paging method. Du kan även i arraylisten sortera dina poster. Så arraylisten utgör en hyfsat bra grund för en collectionklass, det finns tillfällen då HashTable är bättre.
En sak som inte tagits upp här är möjligheten med .NET Reflextion att skapa en DataManager klass som har en funktion som heter LoadData(object oObject) som oberoende av vilken klass du skickar in fyller ditt object med data. Det kräver dock att du skapar egna Attributs och läser på om Reflextions, men när du väl har gjort den så behöver du aldrig mer skriva kod för att fylla dina objekt med data. Du sätter bara Attributs på din variabler i de olika klasserna. Otroligt smidigt. Tycker verkligen det är nog som man skall lära sig då man slipper skriva en massa kod när man väl fått sin DataManager att fungera som man vill.
- M
Gladh skrev:
En sak som inte tagits upp här är möjligheten med .NET Reflextion att skapa en DataManager klass som har en funktion som heter LoadData(object oObject) som oberoende av vilken klass du skickar in fyller ditt object med data. Det kräver dock att du skapar egna Attributs och läser på om Reflextions, men när du väl har gjort den så behöver du aldrig mer skriva kod för att fylla dina objekt med data. Du sätter bara Attributs på din variabler i de olika klasserna. Otroligt smidigt. Tycker verkligen det är nog som man skall lära sig då man slipper skriva en massa kod när man väl fått sin DataManager att fungera som man vill.
- M
Det skulle jag vilja läsa lite mer om. Vart hittar man mer info om detta?
Har försökt åstadkomma liknande, och även skapa åtkomst av data oberoende av dbms.
Just att fylla alla object med data typ - LoadData(Value As Object) - skulle underlätta en hel del.
Kan du visa lite kod?
cya,
PatrikB
cya,
PatrikB
Visst skulle jag kunna det det är dock en hel del. Du behöver en del Attribute klasser och sedan en DataManager och sedan en DataAccess. Du behöver också en BaseCollection-class.
Oberoendet från DBMS kan lösas med hjälp av Interfacen istället för SqlDataReader/OleDbReader kan man använda sig av IDataReader. Professional ADO.NET från wrox har ett kapitel om hur de löser en DataAccess klass som är oberoende av DBMS.
Här kommer lite kod hur du kan lösa det med en LoadData() funktion i en DataManager. Nu visar jag endast hur du kan fylla ett singel objekt med en StoreProcedur.
Attribute för StoreProcedure namnet:
using System;
namespace Test
{
/// <summary>
/// Summary description for SqlParameterLoadAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class StoreProcedureAttribute : Attribute
{
#region public methods ---------------------------------------------------------------------------------
public StoreProcedureAttribute(){}
#endregion
}
}
Attribute för databasfältet:
using System;
namespace Test
{
/// <summary>
/// Summary description for SqlParameterLoadAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class DatabasFieldAttribute : Attribute
{
#region variables --------------------------------------------------------------------------------------
private string m_DatabasFieldName;
#endregion
#region property ---------------------------------------------------------------------------------------
public string FieldName{
get{return m_DatabasFieldName;}
}
#endregion
#region public methods ---------------------------------------------------------------------------------
public DatabasFieldAttribute(string sDatabasFieldName){
m_DatabasFieldName = sDatabasFieldName;
}
#endregion
}
}
Sedan kod för ditt objekt där du sätter attributen StoreProcedure och Databasfield på members:
using System;
using System.Data.SqlTypes;
namespace Test
{
/// <summary>
/// Summary description for COrder.
/// </summary>
public class COrder : BaseClass
{
#region variables --------------------------------------------------------------------------------------
[StoreProcedure()]
private string m_StoreProcedureName
[DatabasField("UsedFromTurn")]
private int m_UsedFromTurn;
[DatabasField("OrderName")]
private string m_OrderName;
#endregion
#region property ---------------------------------------------------------------------------------------
public int UsedFromTurn{
get{return m_UsedFromTurn;}
set{m_UsedFromTurn = value;}
}
public string OrderName{
get{return m_OrderName;}
set{m_OrderName = value;}
}
#endregion
#region constructors -----------------------------------------------------------------------------------
private COrder(){}
public COrder(string storeProcedureName){
m_StoreProcedureName = storeProcedureName;
}
#endregion
}
}
Och sedan sist DataManger klassen, här har jag plockat bort en del kod eftersom vårt exempel inte använder sig av all kod som jag har i min klass. Det finns dock methoder för att skapa SqlParameters, läsa SqlParameters, spara data...
using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Reflection;
namespace Test
{
/// <summary>
/// Summary description for DataManager.
/// </summary>
public sealed class DataManager
{
#region constructors -----------------------------------------------------------------------------------
private DataManager(){}
#endregion
#region private methods --------------------------------------------------------------------------------
private static void BindDataToObject(object oObject, SqlDataReader oDataReader){
//-- get all fields for this object
foreach (FieldInfo fieldInfo in oObject.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetField | BindingFlags.Default))
{
//-- check every field if they have a Attribute named 'DatabasFieldAttribute' and set the value to this field
if(Attribute.IsDefined(fieldInfo, typeof(DatabasFieldAttribute), true))
{
string sFieldName = ((DatabasFieldAttribute)fieldInfo.GetCustomAttributes(typeof(DatabasFieldAttribute),false)[0]).FieldName;
fieldInfo.SetValue(oObject, oDataReader[sFieldName]);
}
}
}
private static string GetStoreProcedureName(object oObject){
//-- get all fields for this object
foreach (FieldInfo fieldInfo in oObject.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetField | BindingFlags.Default)){
//-- check every field if they have a Attribute named 'StoreProcedureAttributes'.
if(Attribute.IsDefined(fieldInfo, typeof(StoreProcedureAttribute), true))
return fieldInfo.GetValue(oObject).ToString();
}
throw new Exception("Can't find any field with information about StoreProcedureName");
}
#endregion
#region public methods ---------------------------------------------------------------------------------
public static void LoadData(object oObject){
//-- declare variables
SqlDataReader oDataReader = null;
string sStoreProcedureName = GetStoreProcedureName(oObject);
SqlCommand oSqlCommand = new SqlCommand();
//-- init commandobject
oSqlCommand.CommandType = CommandType.StoredProcedure;
oSqlCommand.CommandText = sStoreProcedureName;
//-- create parameters for this object
CreateSqlParameters(oSqlCommand, oObject, StoreProcedureAction.Load);
try{
//-- Read data from databas
oDataReader = DataAccess.DataAccess.ExecuteReader(oSqlCommand);
//-- bind data to object
while(oDataReader.Read())
BindDataToObject(oObject, oDataReader);
}
catch(Exception exception){
throw new Exception("Error", exception);
}finally{
//-- close reader
if(oDataReader != null)
oDataReader.Close();
}
}
public static void LoadData(BaseCollection oObject){
//-- declare variables
Type tItemType = ((ItemObjectTypeAttribute) oObject.GetType().GetCustomAttributes(typeof(ItemObjectTypeAttribute),true)[0]).Type;
string sStoreProcedureName = GetStoreProcedureName(oObject);
SqlDataReader oDataReader = null;
SqlCommand oSqlCommand = new SqlCommand();
//-- init commandobject
oSqlCommand.CommandType = CommandType.StoredProcedure;
oSqlCommand.CommandText = sStoreProcedureName;
//-- create parameters for this object
CreateSqlParameters(oSqlCommand, oObject, StoreProcedureAction.Load);
try{
//-- Read data from databas
oDataReader = DataAccess.DataAccess.ExecuteReader(oSqlCommand);
while(oDataReader.Read()){
object tmpObject = Activator.CreateInstance(tItemType, true);
BindDataToObject(tmpObject, oDataReader);
oObject.Add(tmpObject);
}
}catch(Exception exception){
throw new Exception("Error", exception);
}finally{
//-- close reader
if(oDataReader != null)
oDataReader.Close();
}
}
#endregion
}
}
Det fungerar så här. Först skapar man sig egna Attributeklasser som du sätter på dina fält/funktioner/klasser. Dessa Attribute definerar om ett fält skall fyllas med värde från databasen och vad fältet i databasen heter: [DatabasField("ID")]
Samma fungerar för Attributet StoreProcedure, fast här har jag valt att man kan skicka in detta från mitt affärslager så jag kan kalla på olika SP om jag vill detta. Man kan lika gärna skriva in Namnet på SP som man gör med Databasfältet, men då kan man endast ha en SP till sin klass.
Sedan i objektet så definerar man sina fält och sätter de Attribute på fältet som man vill ha, du kan ha många Attribute på ett fält, ibland samma Attribute (måste dock skapas med AllowMultiple = True).
Sedan kommer vi till DataManager. Här finns nu LoadData(object oObject) vilket fyller ett objekt, och LoadData(BaseCollection oObject) som fyller ett CollectionObject.
I LoadData så börjar jag med att hämta Namnet på SP och jag vet att jag skall leta efter ett fält som har Attributet [StoreProcedureNamn] och när jag har hittat det fältet (loopar igenom alla fält för klassen) så hämtar jag värdet i det fältet med GetValue().
Sedan så skapar jag en massa Parameters (vilket inte visas här) sedan hämtar jag en DataReader från min DataAccess-klass som har hand om alla DataAccess. Detta DataReader object använder jag när jag sedan skall fylla mitt objekt med data.
I DataBindToObject() så loopar jag igenom alla fält som klassen har och kollar om de har attributet [DatabasField()] definerad, om det har det, så hämtar jag ut värdet från dataReadern och sätter detta till mitt fält med SetValue().
Det är en start, läs på om Attributes och testa dig fram med funktionen GetType() som finns till alla objekt, så hittar du massor med smått och gott.
- M