2 // $Id: PgProfileProvider.cs 36 2007-11-24 09:44:42Z dna $
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // Copyright © 2006, 2007 Nauck IT KG http://www.nauck-it.de
26 // Daniel Nauck <d.nauck(at)nauck-it.de>
28 // Adapted to Sqlite by Marek Habersack <mhabersack@novell.com>
34 using System.Data.Common;
35 using System.Configuration;
36 using System.Configuration.Provider;
37 using System.Collections.Generic;
38 using System.Collections.Specialized;
39 using System.Diagnostics;
41 using System.Web.Hosting;
42 using System.Web.Util;
44 using Mono.Data.Sqlite;
46 namespace System.Web.Profile
48 internal class SqliteProfileProvider : ProfileProvider
50 private const string m_ProfilesTableName = "Profiles";
51 private const string m_ProfileDataTableName = "ProfileData";
52 private string m_ConnectionString = string.Empty;
54 private SerializationHelper m_serializationHelper = new SerializationHelper();
56 DbParameter AddParameter (DbCommand command, string parameterName)
58 return AddParameter (command, parameterName, null);
61 DbParameter AddParameter (DbCommand command, string parameterName, object parameterValue)
63 return AddParameter (command, parameterName, ParameterDirection.Input, parameterValue);
66 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, object parameterValue)
68 DbParameter dbp = command.CreateParameter ();
69 dbp.ParameterName = parameterName;
70 dbp.Value = parameterValue;
71 dbp.Direction = direction;
72 command.Parameters.Add (dbp);
76 DbParameter AddParameter (DbCommand command, string parameterName, ParameterDirection direction, DbType type, object parameterValue)
78 DbParameter dbp = command.CreateParameter ();
79 dbp.ParameterName = parameterName;
80 dbp.Value = parameterValue;
81 dbp.Direction = direction;
83 command.Parameters.Add (dbp);
88 /// System.Configuration.Provider.ProviderBase.Initialize Method
90 public override void Initialize(string name, NameValueCollection config)
92 // Initialize values from web.config.
94 throw new ArgumentNullException("Config", Properties.Resources.ErrArgumentNull);
96 if (string.IsNullOrEmpty(name))
97 name = Properties.Resources.ProfileProviderDefaultName;
99 if (string.IsNullOrEmpty(config["description"]))
101 config.Remove("description");
102 config.Add("description", Properties.Resources.ProfileProviderDefaultDescription);
105 // Initialize the abstract base class.
106 base.Initialize(name, config);
108 m_ApplicationName = GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
110 // Get connection string.
111 string connStrName = config["connectionStringName"];
113 if (string.IsNullOrEmpty(connStrName))
115 throw new ArgumentOutOfRangeException("ConnectionStringName", Properties.Resources.ErrArgumentNullOrEmpty);
119 ConnectionStringSettings ConnectionStringSettings = ConfigurationManager.ConnectionStrings[connStrName];
121 if (ConnectionStringSettings == null || string.IsNullOrEmpty(ConnectionStringSettings.ConnectionString.Trim()))
123 throw new ProviderException(Properties.Resources.ErrConnectionStringNullOrEmpty);
126 m_ConnectionString = ConnectionStringSettings.ConnectionString;
131 /// System.Web.Profile.ProfileProvider properties.
133 #region System.Web.Security.ProfileProvider properties
134 private string m_ApplicationName = string.Empty;
136 public override string ApplicationName
138 get { return m_ApplicationName; }
139 set { m_ApplicationName = value; }
144 /// System.Web.Profile.ProfileProvider methods.
146 #region System.Web.Security.ProfileProvider methods
149 /// ProfileProvider.DeleteInactiveProfiles
151 public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
153 throw new Exception("DeleteInactiveProfiles: The method or operation is not implemented.");
156 public override int DeleteProfiles(string[] usernames)
158 throw new Exception("DeleteProfiles1: The method or operation is not implemented.");
161 public override int DeleteProfiles(ProfileInfoCollection profiles)
163 throw new Exception("DeleteProfiles2: The method or operation is not implemented.");
166 public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
168 throw new Exception("FindInactiveProfilesByUserName: The method or operation is not implemented.");
171 public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
173 throw new Exception("FindProfilesByUserName: The method or operation is not implemented.");
176 public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
178 throw new Exception("GetAllInactiveProfiles: The method or operation is not implemented.");
181 public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords)
183 throw new Exception("GetAllProfiles: The method or operation is not implemented.");
186 public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
188 throw new Exception("GetNumberOfInactiveProfiles: The method or operation is not implemented.");
193 /// System.Configuration.SettingsProvider methods.
195 #region System.Web.Security.SettingsProvider methods
200 public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
202 SettingsPropertyValueCollection result = new SettingsPropertyValueCollection();
203 string username = (string)context["UserName"];
204 bool isAuthenticated = (bool)context["IsAuthenticated"];
205 Dictionary<string, object> databaseResult = new Dictionary<string, object>();
207 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
209 using (SqliteCommand dbCommand = dbConn.CreateCommand())
211 dbCommand.CommandText = string.Format("SELECT \"Name\", \"ValueString\", \"ValueBinary\" FROM \"{0}\" WHERE \"Profile\" = (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated)", m_ProfileDataTableName, m_ProfilesTableName);
213 AddParameter (dbCommand, "@Username", username);
214 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
215 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
222 using (SqliteDataReader reader = dbCommand.ExecuteReader())
224 while (reader.Read())
226 object resultData = null;
227 if(!reader.IsDBNull(1))
228 resultData = reader.GetValue(1);
229 else if(!reader.IsDBNull(2))
230 resultData = reader.GetValue(2);
232 databaseResult.Add(reader.GetString(0), resultData);
236 catch (SqliteException e)
238 Trace.WriteLine(e.ToString());
239 throw new ProviderException(Properties.Resources.ErrOperationAborted);
249 foreach (SettingsProperty item in collection)
251 if (item.SerializeAs == SettingsSerializeAs.ProviderSpecific)
253 if (item.PropertyType.IsPrimitive || item.PropertyType.Equals(typeof(string)))
254 item.SerializeAs = SettingsSerializeAs.String;
256 item.SerializeAs = SettingsSerializeAs.Xml;
259 SettingsPropertyValue itemValue = new SettingsPropertyValue(item);
261 if ((databaseResult.ContainsKey(item.Name)) && (databaseResult[item.Name] != null))
263 if(item.SerializeAs == SettingsSerializeAs.String)
264 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBase64((string)databaseResult[item.Name]);
266 else if (item.SerializeAs == SettingsSerializeAs.Xml)
267 itemValue.PropertyValue = m_serializationHelper.DeserializeFromXml((string)databaseResult[item.Name]);
269 else if (item.SerializeAs == SettingsSerializeAs.Binary)
270 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBinary((byte[])databaseResult[item.Name]);
272 itemValue.IsDirty = false;
273 result.Add(itemValue);
276 UpdateActivityDates(username, isAuthenticated, true);
281 public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
283 string username = (string)context["UserName"];
284 bool isAuthenticated = (bool)context["IsAuthenticated"];
286 if (collection.Count < 1)
289 if (!ProfileExists(username))
290 CreateProfileForUser(username, isAuthenticated);
292 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
294 using (SqliteCommand deleteCommand = dbConn.CreateCommand(),
295 insertCommand = dbConn.CreateCommand())
297 deleteCommand.CommandText = string.Format("DELETE FROM \"{0}\" WHERE \"Name\" = @Name AND \"Profile\" = (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated)", m_ProfileDataTableName, m_ProfilesTableName);
299 AddParameter (deleteCommand, "@Name");
300 AddParameter (deleteCommand, "@Username", username);
301 AddParameter (deleteCommand, "@ApplicationName", m_ApplicationName);
302 AddParameter (deleteCommand, "@IsAuthenticated", !isAuthenticated);
305 insertCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Profile\", \"Name\", \"ValueString\", \"ValueBinary\") VALUES (@pId, (SELECT \"pId\" FROM \"{1}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated), @Name, @ValueString, @ValueBinary)", m_ProfileDataTableName, m_ProfilesTableName);
307 AddParameter (insertCommand, "@pId");
308 AddParameter (insertCommand, "@Name");
309 AddParameter (insertCommand, "@ValueString");
310 insertCommand.Parameters["@ValueString"].IsNullable = true;
311 AddParameter (insertCommand, "@ValueBinary");
312 insertCommand.Parameters["@ValueBinary"].IsNullable = true;
313 AddParameter (insertCommand, "@Username", username);
314 AddParameter (insertCommand, "@ApplicationName", m_ApplicationName);
315 AddParameter (insertCommand, "@IsAuthenticated", !isAuthenticated);
317 SqliteTransaction dbTrans = null;
322 deleteCommand.Prepare();
323 insertCommand.Prepare();
325 using (dbTrans = dbConn.BeginTransaction())
328 foreach (SettingsPropertyValue item in collection)
333 deleteCommand.Parameters["@Name"].Value = item.Name;
335 insertCommand.Parameters["@pId"].Value = Guid.NewGuid().ToString();
336 insertCommand.Parameters["@Name"].Value = item.Name;
338 if (item.Property.SerializeAs == SettingsSerializeAs.String)
340 insertCommand.Parameters["@ValueString"].Value = m_serializationHelper.SerializeToBase64(item.PropertyValue);
341 insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
343 else if (item.Property.SerializeAs == SettingsSerializeAs.Xml)
345 item.SerializedValue = m_serializationHelper.SerializeToXml(item.PropertyValue);
346 insertCommand.Parameters["@ValueString"].Value = item.SerializedValue;
347 insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
349 else if (item.Property.SerializeAs == SettingsSerializeAs.Binary)
351 item.SerializedValue = m_serializationHelper.SerializeToBinary(item.PropertyValue);
352 insertCommand.Parameters["@ValueString"].Value = DBNull.Value; //string.Empty;//DBNull.Value;
353 insertCommand.Parameters["@ValueBinary"].Value = item.SerializedValue;
356 deleteCommand.ExecuteNonQuery();
357 insertCommand.ExecuteNonQuery();
360 UpdateActivityDates(username, isAuthenticated, false);
362 // Attempt to commit the transaction
366 catch (SqliteException e)
368 Trace.WriteLine(e.ToString());
372 // Attempt to roll back the transaction
373 Trace.WriteLine(Properties.Resources.LogRollbackAttempt);
376 catch (SqliteException re)
379 Trace.WriteLine(Properties.Resources.ErrRollbackFailed);
380 Trace.WriteLine(re.ToString());
383 throw new ProviderException(Properties.Resources.ErrOperationAborted);
395 #region private methods
397 /// Create a empty user profile
399 /// <param name="username"></param>
400 /// <param name="isAuthenticated"></param>
401 private void CreateProfileForUser(string username, bool isAuthenticated)
403 if (ProfileExists(username))
405 throw new ProviderException(string.Format(Properties.Resources.ErrProfileAlreadyExist, username));
408 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
410 using (SqliteCommand dbCommand = dbConn.CreateCommand())
412 dbCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Username\", \"ApplicationName\", \"IsAnonymous\", \"LastActivityDate\", \"LastUpdatedDate\") Values (@pId, @Username, @ApplicationName, @IsAuthenticated, @LastActivityDate, @LastUpdatedDate)", m_ProfilesTableName);
414 AddParameter (dbCommand, "@pId", Guid.NewGuid().ToString());
415 AddParameter (dbCommand, "@Username", username);
416 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
417 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
418 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
419 AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
426 dbCommand.ExecuteNonQuery();
428 catch (SqliteException e)
430 Trace.WriteLine(e.ToString());
431 throw new ProviderException(Properties.Resources.ErrOperationAborted);
443 private bool ProfileExists(string username)
445 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
447 using (SqliteCommand dbCommand = dbConn.CreateCommand())
449 dbCommand.CommandText = string.Format("SELECT COUNT(*) FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_ProfilesTableName);
451 AddParameter (dbCommand, "@Username", username);
452 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
460 Int32.TryParse(dbCommand.ExecuteScalar().ToString(), out numRecs);
465 catch (SqliteException e)
467 Trace.WriteLine(e.ToString());
468 throw new ProviderException(Properties.Resources.ErrOperationAborted);
482 /// Updates the LastActivityDate and LastUpdatedDate values when profile properties are accessed by the
483 /// GetPropertyValues and SetPropertyValues methods.
484 /// Passing true as the activityOnly parameter will update only the LastActivityDate.
486 /// <param name="username"></param>
487 /// <param name="isAuthenticated"></param>
488 /// <param name="activityOnly"></param>
489 private void UpdateActivityDates(string username, bool isAuthenticated, bool activityOnly)
491 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
493 using (SqliteCommand dbCommand = dbConn.CreateCommand())
497 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
499 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
503 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate, \"LastUpdatedDate\" = @LastUpdatedDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
505 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
506 AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
509 AddParameter (dbCommand, "@Username", username);
510 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
511 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
518 dbCommand.ExecuteNonQuery();
520 catch (SqliteException e)
522 Trace.WriteLine(e.ToString());
523 throw new ProviderException(Properties.Resources.ErrOperationAborted);
535 /// A helper function to retrieve config values from the configuration file.
537 /// <param name="configValue"></param>
538 /// <param name="defaultValue"></param>
539 /// <returns></returns>
540 private string GetConfigValue(string configValue, string defaultValue)
542 if (string.IsNullOrEmpty(configValue))