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 const string m_ProfilesTableName = "Profiles";
51 const string m_ProfileDataTableName = "ProfileData";
52 string m_ConnectionString = string.Empty;
54 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);
77 /// System.Configuration.Provider.ProviderBase.Initialize Method
79 public override void Initialize(string name, NameValueCollection config)
81 // Initialize values from web.config.
83 throw new ArgumentNullException("Config", Properties.Resources.ErrArgumentNull);
85 if (string.IsNullOrEmpty(name))
86 name = Properties.Resources.ProfileProviderDefaultName;
88 if (string.IsNullOrEmpty(config["description"]))
90 config.Remove("description");
91 config.Add("description", Properties.Resources.ProfileProviderDefaultDescription);
94 // Initialize the abstract base class.
95 base.Initialize(name, config);
97 m_ApplicationName = GetConfigValue(config["applicationName"], HostingEnvironment.ApplicationVirtualPath);
99 // Get connection string.
100 string connStrName = config["connectionStringName"];
102 if (string.IsNullOrEmpty(connStrName))
104 throw new ArgumentOutOfRangeException("ConnectionStringName", Properties.Resources.ErrArgumentNullOrEmpty);
108 ConnectionStringSettings ConnectionStringSettings = ConfigurationManager.ConnectionStrings[connStrName];
110 if (ConnectionStringSettings == null || string.IsNullOrEmpty(ConnectionStringSettings.ConnectionString.Trim()))
112 throw new ProviderException(Properties.Resources.ErrConnectionStringNullOrEmpty);
115 m_ConnectionString = ConnectionStringSettings.ConnectionString;
120 /// System.Web.Profile.ProfileProvider properties.
122 #region System.Web.Security.ProfileProvider properties
123 string m_ApplicationName = string.Empty;
125 public override string ApplicationName
127 get { return m_ApplicationName; }
128 set { m_ApplicationName = value; }
133 /// System.Web.Profile.ProfileProvider methods.
135 #region System.Web.Security.ProfileProvider methods
138 /// ProfileProvider.DeleteInactiveProfiles
140 public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
142 throw new Exception("DeleteInactiveProfiles: The method or operation is not implemented.");
145 public override int DeleteProfiles(string[] usernames)
147 throw new Exception("DeleteProfiles1: The method or operation is not implemented.");
150 public override int DeleteProfiles(ProfileInfoCollection profiles)
152 throw new Exception("DeleteProfiles2: The method or operation is not implemented.");
155 public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
157 throw new Exception("FindInactiveProfilesByUserName: The method or operation is not implemented.");
160 public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
162 throw new Exception("FindProfilesByUserName: The method or operation is not implemented.");
165 public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
167 throw new Exception("GetAllInactiveProfiles: The method or operation is not implemented.");
170 public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords)
172 throw new Exception("GetAllProfiles: The method or operation is not implemented.");
175 public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
177 throw new Exception("GetNumberOfInactiveProfiles: The method or operation is not implemented.");
182 /// System.Configuration.SettingsProvider methods.
184 #region System.Web.Security.SettingsProvider methods
189 public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
191 SettingsPropertyValueCollection result = new SettingsPropertyValueCollection();
192 string username = (string)context["UserName"];
193 bool isAuthenticated = (bool)context["IsAuthenticated"];
194 Dictionary<string, object> databaseResult = new Dictionary<string, object>();
196 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
198 using (SqliteCommand dbCommand = dbConn.CreateCommand())
200 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);
202 AddParameter (dbCommand, "@Username", username);
203 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
204 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
211 using (SqliteDataReader reader = dbCommand.ExecuteReader())
213 while (reader.Read())
215 object resultData = null;
216 if(!reader.IsDBNull(1))
217 resultData = reader.GetValue(1);
218 else if(!reader.IsDBNull(2))
219 resultData = reader.GetValue(2);
221 databaseResult.Add(reader.GetString(0), resultData);
225 catch (SqliteException e)
227 Trace.WriteLine(e.ToString());
228 throw new ProviderException(Properties.Resources.ErrOperationAborted);
238 foreach (SettingsProperty item in collection)
240 if (item.SerializeAs == SettingsSerializeAs.ProviderSpecific)
242 if (item.PropertyType.IsPrimitive || item.PropertyType.Equals(typeof(string)))
243 item.SerializeAs = SettingsSerializeAs.String;
245 item.SerializeAs = SettingsSerializeAs.Xml;
248 SettingsPropertyValue itemValue = new SettingsPropertyValue(item);
250 if ((databaseResult.ContainsKey(item.Name)) && (databaseResult[item.Name] != null))
252 if(item.SerializeAs == SettingsSerializeAs.String)
253 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBase64((string)databaseResult[item.Name]);
255 else if (item.SerializeAs == SettingsSerializeAs.Xml)
256 itemValue.PropertyValue = m_serializationHelper.DeserializeFromXml((string)databaseResult[item.Name]);
258 else if (item.SerializeAs == SettingsSerializeAs.Binary)
259 itemValue.PropertyValue = m_serializationHelper.DeserializeFromBinary((byte[])databaseResult[item.Name]);
261 itemValue.IsDirty = false;
262 result.Add(itemValue);
265 UpdateActivityDates(username, isAuthenticated, true);
270 public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
272 string username = (string)context["UserName"];
273 bool isAuthenticated = (bool)context["IsAuthenticated"];
275 if (collection.Count < 1)
278 if (!ProfileExists(username))
279 CreateProfileForUser(username, isAuthenticated);
281 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
283 using (SqliteCommand deleteCommand = dbConn.CreateCommand(),
284 insertCommand = dbConn.CreateCommand())
286 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);
288 AddParameter (deleteCommand, "@Name");
289 AddParameter (deleteCommand, "@Username", username);
290 AddParameter (deleteCommand, "@ApplicationName", m_ApplicationName);
291 AddParameter (deleteCommand, "@IsAuthenticated", !isAuthenticated);
294 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);
296 AddParameter (insertCommand, "@pId");
297 AddParameter (insertCommand, "@Name");
298 AddParameter (insertCommand, "@ValueString");
299 insertCommand.Parameters["@ValueString"].IsNullable = true;
300 AddParameter (insertCommand, "@ValueBinary");
301 insertCommand.Parameters["@ValueBinary"].IsNullable = true;
302 AddParameter (insertCommand, "@Username", username);
303 AddParameter (insertCommand, "@ApplicationName", m_ApplicationName);
304 AddParameter (insertCommand, "@IsAuthenticated", !isAuthenticated);
306 SqliteTransaction dbTrans = null;
311 deleteCommand.Prepare();
312 insertCommand.Prepare();
314 using (dbTrans = dbConn.BeginTransaction())
317 foreach (SettingsPropertyValue item in collection)
322 deleteCommand.Parameters["@Name"].Value = item.Name;
324 insertCommand.Parameters["@pId"].Value = Guid.NewGuid().ToString();
325 insertCommand.Parameters["@Name"].Value = item.Name;
327 if (item.Property.SerializeAs == SettingsSerializeAs.String)
329 insertCommand.Parameters["@ValueString"].Value = m_serializationHelper.SerializeToBase64(item.PropertyValue);
330 insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
332 else if (item.Property.SerializeAs == SettingsSerializeAs.Xml)
334 item.SerializedValue = m_serializationHelper.SerializeToXml(item.PropertyValue);
335 insertCommand.Parameters["@ValueString"].Value = item.SerializedValue;
336 insertCommand.Parameters["@ValueBinary"].Value = DBNull.Value; //new byte[0];//DBNull.Value;
338 else if (item.Property.SerializeAs == SettingsSerializeAs.Binary)
340 item.SerializedValue = m_serializationHelper.SerializeToBinary(item.PropertyValue);
341 insertCommand.Parameters["@ValueString"].Value = DBNull.Value; //string.Empty;//DBNull.Value;
342 insertCommand.Parameters["@ValueBinary"].Value = item.SerializedValue;
345 deleteCommand.ExecuteNonQuery();
346 insertCommand.ExecuteNonQuery();
349 UpdateActivityDates(username, isAuthenticated, false);
351 // Attempt to commit the transaction
355 catch (SqliteException e)
357 Trace.WriteLine(e.ToString());
361 // Attempt to roll back the transaction
362 Trace.WriteLine(Properties.Resources.LogRollbackAttempt);
365 catch (SqliteException re)
368 Trace.WriteLine(Properties.Resources.ErrRollbackFailed);
369 Trace.WriteLine(re.ToString());
372 throw new ProviderException(Properties.Resources.ErrOperationAborted);
384 #region private methods
386 /// Create a empty user profile
388 /// <param name="username"></param>
389 /// <param name="isAuthenticated"></param>
390 void CreateProfileForUser(string username, bool isAuthenticated)
392 if (ProfileExists(username))
394 throw new ProviderException(string.Format(Properties.Resources.ErrProfileAlreadyExist, username));
397 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
399 using (SqliteCommand dbCommand = dbConn.CreateCommand())
401 dbCommand.CommandText = string.Format("INSERT INTO \"{0}\" (\"pId\", \"Username\", \"ApplicationName\", \"IsAnonymous\", \"LastActivityDate\", \"LastUpdatedDate\") Values (@pId, @Username, @ApplicationName, @IsAuthenticated, @LastActivityDate, @LastUpdatedDate)", m_ProfilesTableName);
403 AddParameter (dbCommand, "@pId", Guid.NewGuid().ToString());
404 AddParameter (dbCommand, "@Username", username);
405 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
406 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
407 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
408 AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
415 dbCommand.ExecuteNonQuery();
417 catch (SqliteException e)
419 Trace.WriteLine(e.ToString());
420 throw new ProviderException(Properties.Resources.ErrOperationAborted);
432 bool ProfileExists(string username)
434 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
436 using (SqliteCommand dbCommand = dbConn.CreateCommand())
438 dbCommand.CommandText = string.Format("SELECT COUNT(*) FROM \"{0}\" WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName", m_ProfilesTableName);
440 AddParameter (dbCommand, "@Username", username);
441 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
449 Int32.TryParse(dbCommand.ExecuteScalar().ToString(), out numRecs);
454 catch (SqliteException e)
456 Trace.WriteLine(e.ToString());
457 throw new ProviderException(Properties.Resources.ErrOperationAborted);
471 /// Updates the LastActivityDate and LastUpdatedDate values when profile properties are accessed by the
472 /// GetPropertyValues and SetPropertyValues methods.
473 /// Passing true as the activityOnly parameter will update only the LastActivityDate.
475 /// <param name="username"></param>
476 /// <param name="isAuthenticated"></param>
477 /// <param name="activityOnly"></param>
478 void UpdateActivityDates(string username, bool isAuthenticated, bool activityOnly)
480 using (SqliteConnection dbConn = new SqliteConnection(m_ConnectionString))
482 using (SqliteCommand dbCommand = dbConn.CreateCommand())
486 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
488 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
492 dbCommand.CommandText = string.Format("UPDATE \"{0}\" SET \"LastActivityDate\" = @LastActivityDate, \"LastUpdatedDate\" = @LastUpdatedDate WHERE \"Username\" = @Username AND \"ApplicationName\" = @ApplicationName AND \"IsAnonymous\" = @IsAuthenticated", m_ProfilesTableName);
494 AddParameter (dbCommand, "@LastActivityDate", DateTime.Now);
495 AddParameter (dbCommand, "@LastUpdatedDate", DateTime.Now);
498 AddParameter (dbCommand, "@Username", username);
499 AddParameter (dbCommand, "@ApplicationName", m_ApplicationName);
500 AddParameter (dbCommand, "@IsAuthenticated", !isAuthenticated);
507 dbCommand.ExecuteNonQuery();
509 catch (SqliteException e)
511 Trace.WriteLine(e.ToString());
512 throw new ProviderException(Properties.Resources.ErrOperationAborted);
524 /// A helper function to retrieve config values from the configuration file.
526 /// <param name="configValue"></param>
527 /// <param name="defaultValue"></param>
528 /// <returns></returns>
529 string GetConfigValue(string configValue, string defaultValue)
531 if (string.IsNullOrEmpty(configValue))