Displaying Windows Active Directory thumbnailPhoto in asp:net Search App - asp.net

I've set up a WebApplication to search the Active Directory of our organization. The Query (dynamic SQL wrapping OpenQuery) returns also the hexadecimal data for the thumpnailPhoto.
When I'm displaying the image I get a 40% success on all pictures displayed. The "bad" ones are usually fine in the first 15% starting from the top and then stop with black stripes. Sometimes there is picture data repeated in stripes... Some profile pictures are not displayed at all, while others are perfect. There must be different image formats, but not having access to the storage positions of these images I do not get any further.
This is what I do:
I bind the data from the query to a GridView.
protected void FillGrid(Object sender, EventArgs e)
{
SqlConnection objConn = new SqlConnection("Data Source=XXXXXXXXXX");
SqlDataAdapter adapter = new SqlDataAdapter();
SqlCommand objCommand = new SqlCommand(#"declare #SQL nvarchar(4000)
declare #Asterisc nvarchar(1)
declare #Sub nvarchar(12)
set #SearchName = CASE WHEN #SearchName = '' THEN '*' ELSE #SearchName END
set #SearchSurename = CASE WHEN #SearchSurename = '' THEN '*' ELSE #SearchSurename END
set #Asterisc = '*'
set #Sub = CASE WHEN #Subsidiary = '*' THEN '' ELSE (#Subsidiary+', OU=') END
set #SQL ='SELECT TOP (100) PERCENT samAccountName AS UserAccount, givenName AS FirstName, sn AS LastName, department, title AS Position,
physicaldeliveryofficename AS Office, extensionAttribute1 AS PersonnelID, initials, mail AS email, telephonenumber AS Phone, extensionattribute5 AS Extension,
mobile, extensionattribute3 AS MobileExt, thumbnailPhoto
FROM OPENQUERY(ADSI, ''SELECT samAccountName, givenName, sn, legacyExchangeDN, department, title, physicaldeliveryofficename, extensionAttribute1, distinguishedName, initials,
mail, telephonenumber, extensionattribute5, mobile, extensionattribute3, thumbnailPhoto
FROM ''''LDAP://OU=' + #Sub + 'XXXX, DC=XXXX,DC=XXXX,DC=XXXX''''
WHERE objectClass=''''Person'''' AND objectClass = ''''User'''' AND givenName = ''''' + #SearchName + ''''' AND samAccountName = ''''' + #SearchSurename + ''''' AND extensionAttribute1 = ''''' + #Asterisc + ''''' '') AS AD_Users
ORDER BY UserAccount'
exec (#SQL)", objConn);
objCommand.Parameters.Add("#SearchName", SqlDbType.NVarChar).Value = TextBoxSearchName.Text;
objCommand.Parameters.Add("#SearchSurename", SqlDbType.NVarChar).Value = TextBoxSearchSureName.Text;
objCommand.Parameters.Add("#Subsidiary", SqlDbType.NVarChar).Value = DDSubsidiary.Text;
DataTable t = new DataTable();
adapter.SelectCommand = objCommand;
objConn.Open();
adapter.Fill(t);
objConn.Close();
GridView.DataSource = t;
GridView.DataBind();
}
Then during the RowDataBound event I encode the data like this and bind that data to an asp:image control:
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView dr = (DataRowView)e.Row.DataItem;
if(!String.IsNullOrEmpty(Convert.ToString(dr["thumbnailPhoto"])))
{
string imageUrl = "data:image/jpg;base64," + Convert.ToBase64String((byte[])dr["thumbnailPhoto"]);
(e.Row.FindControl("Image1") as Image).ImageUrl = imageUrl;
}
}
}
What problem am I facing here and how to deal with it?
I just discovered that those pictures that are displayed partially or with stripes are all pictures that are stored in a bigger 600x600 format, while the good ones have a 96x96 format. That leads me to my suspicion that something is going wrong when retrieving the thumbprintPhoto in it's hexacecimal format through SQL.
As Gabriel suggested, I tried to use the DirectorySearcher. The thumbprintPhoto was displayed correctly but it took me more than 10 seconds to retrieve 44 AD entries: Here is my code
protected void SearchAD(object sender, EventArgs e)
{
string Name = TextBoxSearchFirstName.Text;
Name = Name.Replace("*", "") + "*";
DirectorySearcher dsSearcher = new DirectorySearcher();
dsSearcher.Filter = "(&(objectClass=user) (sn=" + Name + "))";
results = dsSearcher.FindAll();
DataTable t = new DataTable("ActiveDir");
t.Columns.Add(new DataColumn("SecondName", System.Type.GetType("System.String")));
t.Columns.Add(new DataColumn("FirstName", System.Type.GetType("System.String")));
t.Columns.Add(new DataColumn("UserID", System.Type.GetType("System.String")));
t.Columns.Add(new DataColumn("data", System.Type.GetType("System.Byte[]")));
if (results != null)
{
foreach (SearchResult searchResult in results)
{
DataRow myRow;
myRow = t.NewRow();
myRow[0] = searchResult.GetDirectoryEntry().Properties["sn"].Value;
myRow[1] = searchResult.GetDirectoryEntry().Properties["givenName"].Value;
myRow[2] = searchResult.GetDirectoryEntry().Properties["samAccountName"].Value;
myRow[3] = searchResult.GetDirectoryEntry().Properties["thumbnailPhoto"].Value;
t.Rows.Add(myRow);
}
}
GridView1.DataSource = t;
GridView1.DataBind();
}
Solved: Gabriel tuned the AD Search code and now it's faster than the SQL Query.
In order to display the AD Thumnail Profil pictures without using a handler do the decoding during the GridViews OnRowDataBound Event:
protected void OnRowDataBoundAdUser(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView dr = (DataRowView)e.Row.DataItem;
if (!String.IsNullOrEmpty(Convert.ToString(dr["data"])))
{
byte[] data = dr["data"] as byte[];
MemoryStream s = new MemoryStream(data);
byte[] imageBytes = s.ToArray();
string base64String = Convert.ToBase64String(imageBytes);
string imageUrl = "data:image/jpg;base64," + base64String;
(e.Row.FindControl("Image1") as System.Web.UI.WebControls.Image).ImageUrl = imageUrl;
}
}
}

I see why it's taking so long. DirectorySearcher and DirectoryEntry can be a little sneaky with how many times it reaches out to AD. You have a lot more network requests going on there than you need.
In your code, you're going out to AD once for the search. Then for each result, you're using GetDirectoryEntry(). Then DirectoryEntry is going out to AD again the first time you use Properties. Worse still, as soon as you use Properties, it downloads every single attribute that has a value, even though you're only actually using 4 of them. That's a whole lot of useless network traffic.
You can avoid this by using Properties collection in the search results, which already has all the attributes (by default it gets every attribute that has a value).
But you can do even better: If you set the PropertiesToLoad property of DirectorySearcher then it will only return the values you want to use. This way, you have one network request for everything you want, and only what you want.
protected void SearchAD(object sender, EventArgs e)
{
string Name = TextBoxSearchFirstName.Text;
Name = Name.Replace("*", "") + "*";
var dsSearcher = new DirectorySearcher {
Filter = "(&(objectClass=user) (sn=" + Name + "))"
};
dsSearcher.PropertiesToLoad.Add("sn");
dsSearcher.PropertiesToLoad.Add("givenName");
dsSearcher.PropertiesToLoad.Add("samAccountName");
dsSearcher.PropertiesToLoad.Add("thumbnailPhoto");
using (var results = dsSearcher.FindAll()) {
var t = new DataTable("ActiveDir");
t.Columns.Add(new DataColumn("SecondName", typeof(string)));
t.Columns.Add(new DataColumn("FirstName", typeof(string)));
t.Columns.Add(new DataColumn("UserID", typeof(string)));
t.Columns.Add(new DataColumn("data", typeof(byte[])));
foreach (SearchResult searchResult in results) {
var myRow = t.NewRow();
myRow[0] = searchResult.Properties.Contains("sn") ? searchResult.Properties["sn"][0] : null;
myRow[1] = searchResult.Properties.Contains("givenName") ? searchResult.Properties["givenName"][0] : null;
myRow[2] = searchResult.Properties.Contains("samAccountName") ? searchResult.Properties["samAccountName"][0] : null;
myRow[3] = searchResult.Properties.Contains("thumbnailPhoto") ? searchResult.Properties["thumbnailPhoto"][0] : null;
t.Rows.Add(myRow);
}
}
GridView1.DataSource = t;
GridView1.DataBind();
}
A few notes:
The calls to Contains are needed because if the attribute is empty, then it doesn't exist in the Properties collection at all.
The search results returns everything as an array, regardless of whether it is or not in AD. Thus, the [0] is needed for every attribute.
You'll notice I put a using statement around the search results. This is needed to prevent memory leaks, according to the documentation of FindAll().

Related

access database wont update asp.net [duplicate]

my access database wont update with this code. what seems to be the problem?
i have tried a lot of methods for updating my access database with no sucsess
please guys some help.
protected void Btnupdate_Click(object sender, EventArgs e)
{
foreach (RepeaterItem RI in rptEdit.Items)
{
Label id = RI.FindControl("Pid") as Label;
TextBox prdname = RI.FindControl("prdname") as TextBox;
TextBox prdprice = RI.FindControl("prdprice") as TextBox;
TextBox prdshortdesc = RI.FindControl("prdshortdesc") as TextBox;
TextBox prdtype = RI.FindControl("prdtype") as TextBox;
TextBox prdbrand = RI.FindControl("prdbrand") as TextBox;
int updated;
string connection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\\Table.accdb";
using (var conn = new OleDbConnection(connection))
{
conn.Open();
string updSql = #"UPDATE ProductList SET
Pname = '" + prdname.Text + "' WHERE Pid = ?";
using (var cmd = new OleDbCommand(updSql, conn))
{
cmd.Parameters.Add("#Pname", OleDbType.VarChar).Value = prdname.Text;
updated = cmd.ExecuteNonQuery();
conn.Dispose();
conn.Close();
}
}
}
}
Just use the ? style parameters in your SQL.
string sql = #"UPDATE ProductList SET Pname = ? WHERE Pid = ?";
Then just make sure you add your parameters in the same order in your code that they appear in the SQL.
cmd.Parameters.Add(prdName.Text);
cmd.Parameters.Add(int.Parse(id.Text));
You need to make sure the type of the variable being added in C# matches the type in the DB (in terms of text or number). Then it can be properly quoted or not as needed.

Can't update access database threw asp.net

my access database wont update with this code. what seems to be the problem?
i have tried a lot of methods for updating my access database with no sucsess
please guys some help.
protected void Btnupdate_Click(object sender, EventArgs e)
{
foreach (RepeaterItem RI in rptEdit.Items)
{
Label id = RI.FindControl("Pid") as Label;
TextBox prdname = RI.FindControl("prdname") as TextBox;
TextBox prdprice = RI.FindControl("prdprice") as TextBox;
TextBox prdshortdesc = RI.FindControl("prdshortdesc") as TextBox;
TextBox prdtype = RI.FindControl("prdtype") as TextBox;
TextBox prdbrand = RI.FindControl("prdbrand") as TextBox;
int updated;
string connection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\\Table.accdb";
using (var conn = new OleDbConnection(connection))
{
conn.Open();
string updSql = #"UPDATE ProductList SET
Pname = '" + prdname.Text + "' WHERE Pid = ?";
using (var cmd = new OleDbCommand(updSql, conn))
{
cmd.Parameters.Add("#Pname", OleDbType.VarChar).Value = prdname.Text;
updated = cmd.ExecuteNonQuery();
conn.Dispose();
conn.Close();
}
}
}
}
Just use the ? style parameters in your SQL.
string sql = #"UPDATE ProductList SET Pname = ? WHERE Pid = ?";
Then just make sure you add your parameters in the same order in your code that they appear in the SQL.
cmd.Parameters.Add(prdName.Text);
cmd.Parameters.Add(int.Parse(id.Text));
You need to make sure the type of the variable being added in C# matches the type in the DB (in terms of text or number). Then it can be properly quoted or not as needed.

Value cannot be null in querystring

I have 2 link buttons on my page for each product.1 of them is delete that product and the other is redirect it by query string to the other page to Edit that product.
hereprotected void dlMusic_ItemCommand(object source, DataListCommandEventArgs e)
{
int id = Convert.ToInt32(e.CommandArgument);
if (e.CommandName == "EditItem")
{
Response.Redirect("~/Admin/EditMusic.aspx?id=" + id);
}
else if (e.CommandName == "DeleteItem")
{
SqlCommand cmd = new SqlCommand("", Connection);
cmd.CommandText = "DELETE FROM MusicTable WHERE MusicId=#id";
cmd.Parameters.AddWithValue("#id", id);
Connection.Open();
cmd.ExecuteNonQuery();
Connection.Close();
LoadData();
}
}
Delete button worked correctly but on edit I have problem.
protected void Page_Load(object sender, EventArgs e)
{
int id = int.Parse(Request.QueryString["id"]);
SqlDataAdapter da = new SqlDataAdapter("", Connection);
DataTable dt = new DataTable();
da.SelectCommand.CommandText = "SELECT * FROM MusicTable WHERE MusicId=#id";
da.SelectCommand.Parameters.AddWithValue("#id", id);
da.Fill(dt);
string name = dt.Rows[0]["MusicName"].ToString();
string signame = dt.Rows[0]["SingerName"].ToString();
string prodname = dt.Rows[0]["ProducerName"].ToString();
string albname = dt.Rows[0]["AlbumeName"].ToString();
string des = dt.Rows[0]["Description"].ToString();
string cover = dt.Rows[0]["Cover"].ToString();
txtMusicName.Text = name;
txtSingerName.Text = signame;
txtProducerName.Text = prodname;
txtAlbumeName.Text = albname;
coverImg.ImageUrl = "~/images/" + cover;
txtDes.InnerText = des;
}
It works correctly until requested by query string and the error come is
Additional information: Value cannot be null.
Thanks in advance
From your comment, it is apparent that field "id" is not part of your QueryString.
Please check your URL when the Edit Page is loaded by the browser (you can see it if you put a breakpoint and switch to the browser window).
If you think your URL is correct, please post a screenshot of your loading browser.
Another idea (quite desperate, though), change "id" in whatever else (i.e. "myid")
Response.Redirect("~/Admin/EditMusic.aspx?myid=" + id);
and
int id = int.Parse(Request.QueryString["myid"]);

how to auto login after registration in asp.net

I want to login automatically after registration by using a session like Session["ud"] , but I don't know where should I put it.
public partial class index : System.Web.UI.Page
{
SqlConnection cnn = new SqlConnection(ConfigurationManager.AppSettings["dbpath"]);
protected void btnSave_Click(object sender, EventArgs e)
{
long idx;
SqlCommand cmd = new SqlCommand();
cmd.Connection = cnn;
cmd.CommandText = "Insert into tblUser (UInfo,UEmail,UName,UPass, UGender) Values (#P1,#P2,#P3,#P4,#P5) select ##Identity";
cmd.Parameters.AddWithValue("#P1", txtInfo.Text);
cmd.Parameters.AddWithValue("#P2", txtEmail.Text);
cmd.Parameters.AddWithValue("#P3", txtUserName.Text);
cmd.Parameters.AddWithValue("#P4", txtPass.Text);
cmd.Parameters.AddWithValue("#P5", rdbMale.Checked);
cnn.Open();
idx = Convert.ToInt64(cmd.ExecuteScalar()); // i think here we can do something
cnn.Close();
here we want to upload the image of user and it works correctly
string fn = "";
if (FileUpload1.HasFile == true)
{
fn = FileUpload1.FileName;
string des = Server.MapPath("\\UserImg\\") + idx.ToString() + ".jpg";
FileUpload1.PostedFile.SaveAs(des);
SqlCommand cmdUpdate = new SqlCommand();
cmdUpdate.Connection = cnn;
cmdUpdate.CommandText = "Update tblUser Set UImg=#P5 where UId=#P0";
cmdUpdate.Parameters.AddWithValue("#P5", idx.ToString() + ".jpg");
cmdUpdate.Parameters.AddWithValue("#P0", idx);
cnn.Open();
cmdUpdate.ExecuteNonQuery();
cnn.Close();
}
Response.Redirect("Profile.aspx");
}
}
once you have entered data into in sql database you will get id of new user here
idx = Convert.ToInt64(cmd.ExecuteScalar()); // i think here we can do something
Once you get the id assign it to your session
idx = Convert.ToInt64(cmd.ExecuteScalar()); // i think here we can do something
cnn.Close();
Session["ud"]=idx;
once you have assigned session ,you just have to redirect to required page and validate Session variable if it's null or not.
i hope on Profile.aspx page you are checking for same session variable.
Profile.aspx.cs--on page load
if (Session["ud"] != null)
{
//successfull login
}
else
{
//redirect to login page
}

Employee ID reflected in created by column instead of display name

I wrote a program to import data from excel sheet to sharepoint 2007 list. Around 11000 data gets imported. I have used the below code. My query is I wanted to put "employee's display name" in "created by" column. And im providing the same in excel sheet n in the code. But after the data gets imported I see that few employees data has reflected the created by column with their names. But for few it reflects EMPID only or EMPID + name. I debug the code It takes the right string to display but i did not understand y it gives such results. Also i am running the prog on my machine n not the server, so is it bcoz of this. As I am using test server and then only will deploy to production.
Code:
protected void btnImport_Click(object sender, EventArgs e)
{
using (SPSite site = new SPSite("URL"))
{
using (SPWeb web = site.OpenWeb())
{
webapp = web.Site.WebApplication;
webapp.FormDigestSettings.Enabled = false;
SPList list = web.Lists["List name"];
string connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\\Test.xlsx;Extended Properties=Excel 12.0";
OleDbConnection oledbConn = new OleDbConnection(connString);
oledbConn.Open();
OleDbCommand cmd = new OleDbCommand("SELECT * FROM [Sheet1$]", oledbConn);//contents from sheet1 is selected
OleDbDataAdapter oleda = new OleDbDataAdapter();
oleda.SelectCommand = cmd;
DataSet ds = new DataSet();
oleda.Fill(ds, "Employees");
DataTable dt = ds.Tables["Employees"];
DataView dv = new DataView(dt);
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite elevatedSite = new SPSite("URL"))
{
elevatedRootWeb = elevatedSite.OpenWeb();
}
});
foreach (DataRowView drv in dv)
{
EMPID = drv["Emp id"].ToString();
DispName = drv["Name"].ToString();
Title = drv["Title"].ToString();
getid = new SPQuery();
getid.Query = "<Where><Eq><FieldRef Name=’EMPID’ /><Value Type='Text'>" + EMPID + "</Value></Eq></Where><OrderBy><FieldRef Name='ID'/></OrderBy>";
check = list.GetItems(getid).GetDataTable();
if (check == null)
{
try
{
elevatedRootWeb.AllowUnsafeUpdates = true;
UserItem = list.Items.Add();
UserItem["Emp id"] = EMPID;
UserItem["Title"] = Title;
test = elevatedRootWeb.EnsureUser(PSNumber).ID + ";#" + DispName;
UserItem["Author"] = test;
UserItem.Update();
list.Update();
count++;
elevatedRootWeb.AllowUnsafeUpdates = false;
using (StreamWriter w = File.AppendText("D:\\Errorlog_SP2010.txt"))
{
Log(PSNumber + "Inserted successfully", w);
w.Close();
}
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("<script>alert('Exception on adding item " + ex.Message + "')</script>");
using (StreamWriter w = File.AppendText("D:\\Errorlog_SP2010.txt"))
{
Log(ex.ToString()+ PSNumber, w);
w.Close();
}
}
}
check the "_catalogs/users/simple.aspx" list to see if the user on that site has thier information stored correctly.
This is important as the Author field takes it's value from what is displayed in this list, not exactly what you set.
See this question on how to fix this.

Resources