My ASP.NET application runs fine in local instance of VS 2012 but not in VS 2015. It is throwing
Systems.Collections.Generic.KeyNotFoundException occurred in mscorlib.dll but not handled in user code
The application also runs fine when deployed on the server but issue occurs only in local machine - NHibernate session key being null and breaks on the following line.
var sessionInitializer = map[sessionFactory]
My code is pasted below and I am following similar implementation as in the following link (I have everything up until the IWindsorInstaller section from the link) -
http://nhibernate.info/blog/2011/03/02/effective-nhibernate-session-management-for-web-apps.html
public class LazyNHWebSessionContext : ICurrentSessionContext
{
private readonly ISessionFactoryImplementor factory;
private const string CurrentSessionContextKey =
"NHibernateCurrentSession";
public LazyNHWebSessionContext(ISessionFactoryImplementor factory)
{
this.factory = factory;
}
/// <summary>
/// Retrieve the current session for the session factory.
/// </summary>
/// <returns></returns>
public ISession CurrentSession()
{
Lazy<ISession> initializer;
var currentSessionFactoryMap = GetCurrentFactoryMap();
if (currentSessionFactoryMap == null ||
!currentSessionFactoryMap.TryGetValue(factory, out initializer))
{
return null;
}
return initializer.Value;
}
/// <summary>
/// Bind a new sessionInitializer to the context of the sessionFactory.
/// </summary>
/// <param name="sessionInitializer"></param>
/// <param name="sessionFactory"></param>
public static void Bind(Lazy<ISession> sessionInitializer, ISessionFactory sessionFactory)
{
var map = GetCurrentFactoryMap();
map[sessionFactory] = sessionInitializer;
}
/// <summary>
/// Unbind the current session of the session factory.
/// </summary>
/// <param name="sessionFactory"></param>
/// <returns></returns>
public static ISession UnBind(ISessionFactory sessionFactory)
{
var map = GetCurrentFactoryMap();
var sessionInitializer = map[sessionFactory];
map[sessionFactory] = null;
if (sessionInitializer == null || !sessionInitializer.IsValueCreated) return null;
return sessionInitializer.Value;
}
/// <summary>
/// Provides the CurrentMap of SessionFactories.
/// If there is no map create/store and return a new one.
/// </summary>
/// <returns></returns>
private static IDictionary<ISessionFactory, Lazy<ISession>> GetCurrentFactoryMap()
{
//var contextItem = HttpContext.Current.Items[CurrentSessionContextKey];
if (HttpContext.Current == null) return null;
var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<ISession>>)
HttpContext.Current.Items[CurrentSessionContextKey];
if (currentFactoryMap == null)
{
currentFactoryMap = new Dictionary<ISessionFactory, Lazy<ISession>>();
HttpContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap;
}
return currentFactoryMap;
}
}
Related
I'm still really new to coding and trying to make my first WPF application. I have been trying to implement sqlite for the past like 3 days now.
I am using VS 2019. I have downloaded the SQLite Toolbox and am following the instructions here - https://github.com/ErikEJ/SqlCeToolbox/wiki/EF6-workflow-with-SQLite-DDEX-provider. I did a full installation. Was I supposed to install it in my project directory? Because now I just have a bunch of files and nothing seems to have changed in Studio. I tried using Install.exe, but it returned a "confirm option not enabled" error. Looking at a similar question, I tried putting the files in an external folder in my project, and then installed the System.Data.SQLite.EF6.dll to my GAC using the VS Dev Console. The toolbox doesn't see any changes, and does not recognize the dll and I'm having a hard time finding reliable information for my version. Thanks for any help getting pointed in the right direction!
I'm not sure why you mention toolbox as far as I'm aware you access the SQLite functionality programmatically.
The NuGet packages that I recently used for a Xamarin project (SQLite-net-pcl by Frank Krueger and supporting libraries) allowed me to use pretty simple object mapping without recourse to SQL like strings.
I use a lot of interfaces in my code, but here's my all access database class
:
public class AllAccessDataTableBaseSqLite<T> : IDataAccessRead<T>, IDataAccessWrite<T>,IDataAccessExpressionSearch<T>, IDataAccessDelete<T> where T: IDataRecord, new()
{
//Note that this static value will only apply to those classes based on the same generic type e.g. all DataTableBase<User> instances etc.
public static SQLiteAsyncConnection DBConnection;
/// <summary>
/// Lock object to prevent multi-thread interruption of code segment.
/// </summary>
public static readonly object CollisionLock = new object();
/// <summary>
/// Constructor
/// </summary>
public AllAccessDataTableBaseSqLite()
{
lock (CollisionLock)
{
if (DBConnection != null)
{
DBConnection.CreateTableAsync<T>().Wait();
return;
}
try
{
string directory;
if (DeviceInfo.Platform != DevicePlatform.Unknown)
{
directory = FileSystem.AppDataDirectory;
}
else
{
directory = "DataStore";
var directoryInfo = Directory.CreateDirectory(directory);
directory = directoryInfo.FullName;
}
var path = Path.Combine(directory, $"{typeof(T).Name}.db");
if (!File.Exists(path))
{
using var fileStream = File.Create(path);
fileStream.Close();
}
DBConnection = new SQLiteAsyncConnection(path);
DBConnection.CreateTableAsync<T>().Wait();
}
catch (Exception ex)
{
if (ex is UnauthorizedAccessException)
{
}
}
}
}
/// <summary>
/// Create the data table
/// </summary>
/// <returns></returns>
public async Task<CreateTableResult> CreateTableAsync()
{
if (DBConnection != null)
{
return await DBConnection.CreateTableAsync<T>();
}
return CreateTableResult.Migrated;
}
/// <summary>
/// Create a new record entry
/// </summary>
/// <param name="entity">Data entity to enter</param>
/// <param name="user">Current User information</param>
/// <returns>New entry record if successful</returns>
public async Task<T> CreateAsync(T entity, IUserRecord user)
{
if (entity == null)
{
return default(T);
}
if (DBConnection == null)
{
return default(T);
}
entity.CreatedDate = DateTime.UtcNow;
entity.CreatedByUserId = user.Id;
entity.Id = 0;
try
{
await DBConnection.InsertAsync(entity);
}
catch (SQLiteException e)
{
if (e.Message == "Constraint")
{
throw new InvalidConstraintException(e.Message, e.InnerException);
}
}
var result = entity;
return result;
}
/// <summary>
/// Update a collection of new entities of type T to the data table.
/// All entities should be present within the data table
/// </summary>
/// <param name="entityList">Entity collection</param>
/// <param name="user">user making the change</param>
/// <returns>ID of entities successfully updated or added</returns>
public async Task<int> UpdateAllAsync(IEnumerable<T> entityList, IUserRecord user)
{
var result = 0;
foreach (var t in entityList)
{
if (null != await UpdateAsync(t, user))
{
result++ ;
}
}
return result;
}
/// <summary>
/// Obtain the data record with the given Id
/// </summary>
/// <param name="id">Id value to select the record by</param>
/// <returns>A valid record if found otherwise null</returns>
public async Task<T> GetById(int id)
{
if (DBConnection == null)
{
return default(T);
}
return await DBConnection.Table<T>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
/// <summary>
/// This function returns all database entries that are not marked deleted or changed
/// Warning: The data set may be very large
/// </summary>
/// <returns>A list of entries</returns>
public async Task<List<T>> GetAll()
{
if (DBConnection != null)
{
return await DBConnection.Table<T>().Where(x=>x.ChangedDate==default && x.DeletedDate==default)
.ToListAsync();
}
return new List<T>();
}
/// <inheritdoc />
public async Task<List<T>> GetAllHistoric() => await DBConnection.Table<T>().ToListAsync();
/// <summary>
/// This function is used to update the supplied record entry within the database.
/// If the supplied record does not have a non-zero value Id field it is assumed to be a
/// new record to be inserted into the database.
/// </summary>
/// <param name="entity">Record to update</param>
/// <param name="user">User performing the action</param>
/// <returns></returns>
public async Task<T> UpdateAsync(T entity, IUserRecord user)
{
if (DBConnection == null)
{
return default(T);
}
if (entity == null)
{
return default(T);
}
var newRecord = (T) ((entity) as BaseRecord<T>)?.Clone();
if (null == newRecord)
{
return default(T);
}
//if Id is zero assume that the record is new and to be added
if (newRecord.Id == 0)
{
if (user != null)
{
newRecord.CreatedByUserId = user.Id;
}
newRecord.CreatedDate = DateTime.UtcNow;
newRecord.Id = await DBConnection.InsertAsync(newRecord);
return newRecord;
}
// Id is not zero and thus a new record should be created linked to the old record.
var oldRecord = await GetById(newRecord.Id);
oldRecord.ChangedDate = DateTime.UtcNow;
if (user != null)
{
oldRecord.ChangedByUserId = user.Id;
}
try
{
var result = await DBConnection.UpdateAsync(oldRecord);
}
catch (Exception e)
{
Debug.WriteLine($"UpdateAsync {e.Message}");
}
newRecord.PreviousRecordId = oldRecord.Id;
newRecord.Id = 0;
return await CreateAsync(newRecord, user);
}
/// <inheritdoc />
public async Task<int> DeleteAsync(T entity)
{
if (DBConnection == null)
{
return -1;
}
return await DBConnection.DeleteAsync(entity);
}
/// <inheritdoc />
public async Task DeleteAll()
{
await DBConnection.DropTableAsync<T>();
await CreateTableAsync();
}
/// <inheritdoc />
public async Task<PagedResult<T>> GetAllPagedResult(int recordId, uint maxResults = 100)
{
if (DBConnection == null)
{
return null;
}
List<T> list;
if (maxResults == 0)
{
list = await GetAll();
}
else
{
list = await DBConnection.Table<T>().Where(x => (x.Id >= recordId && x.ChangedDate == default && x.DeletedDate == default)).ToListAsync();
if (list.Count() > maxResults)
{
list = list.GetRange(0, (int) maxResults);
}
}
return new PagedResult<T>(list, list.Count());
}
/// <inheritdoc />
public async Task<IEnumerable<T>> FindAsyncOrdered<TValue>(Expression<Func<T, bool>> predicate = null,
Expression<Func<T, TValue>> orderBy = null)
{
var query = DBConnection.Table<T>();
if (predicate != null)
{
query = query.Where(predicate);
}
if (orderBy != null)
{
query = query.OrderBy<TValue>(orderBy);
}
return await query.ToListAsync();
}
/// <inheritdoc />
public async Task<T> FindFirst(Expression<Func<T, bool>> predicate) => await DBConnection.FindAsync(predicate);
}
I'm using data classes based upon:
public interface IDataRecord
{
/// <summary>
/// Identifier for record
/// </summary>
int Id { get; set; }
/// <summary>
/// Link to previous version of record
/// </summary>
int PreviousRecordId { get; set; }
/// <summary>
/// User Identity that made the change
/// </summary>
int ChangedByUserId { get; set; }
/// <summary>
/// Date when the data record was last changed
/// </summary>
DateTime ChangedDate { get; set; }
/// <summary>
/// Identity of User that deleted the record
/// </summary>
int DeletedByUserId { get; set; }
/// <summary>
/// Date when the data record was deleted
/// </summary>
DateTime DeletedDate { get; set; }
/// <summary>
/// Identity of User that created the record
/// </summary>
int CreatedByUserId { get; set; }
/// <summary>
/// Date when the data record was added
/// </summary>
DateTime CreatedDate { get; set; }
object Clone();
}
Obviously you don't have to use this, but put simply for my application implementation each type of data record is stored in its own data file (thus 1 table per file) and this is created at the beginning in the constructor.
The SQLite db connection is created using the data file path.
Table is created using the dbconnection
edit
I've had a closer look at your code.
Points to note are:
You don't appear to be creating a table.
Access to non-app folders is restricted if you chose to create a UWP rather than base WPF project - be aware of folder access permissions when running apps especially in release mode.
I am new to Mock testing and i have created a Unit Test using RhinoMock.
First stub is running proper but second is throwing Null Exception, code is as below.
[TestMethod]
public void TestMethod1()
{
SetUp(); //bind List ftsController and ftsEreceiptNumberPrefix
MockRepository mocks = new MockRepository();
var ftsUnitOfWork = mocks.StrictMock<IFtsUnitOfWork>();
ftsUnitOfWork.Stub(x => x.Repository<FtsController>().Get()).Return(ftsController.AsQueryable());
ftsUnitOfWork.Stub(x =>
{
if (x.Repository<FtsPrefix>() == null)
{
throw new ArgumentNullException("Input cannot be null");
}
x.Repository<FtsPrefix>().Get();
}).Return(ftsPrefix.AsQueryable());
;
BoGeneratePrefix generateEreceiptPrefix = new BoGeneratePrefix (logger,ftsUnitOfWork);
generatePrefix.ProcessJob(null);
}
ProcessJob :
public class BoGeneratePrefix : IBoDataPurging
{
#region private Variables
private readonly ILogger _logger;
private readonly IUnitOfWork _ftsUnitOfWork;
private readonly string _defaultTraceCategory;
#endregion
#region BoGeneratePrefix Constructor
/// <summary>
/// Initialized class oject BoGeneratePrefix
/// </summary>
/// <param name="logger"></param>
/// <param name="ftsUoW"></param>
public BoGeneratePrefix(ILogger logger, IFtsUnitOfWork ftsUoW)
{
_defaultTraceCategory = GetType().Name;
_logger = logger;
_ftsUnitOfWork = ftsUoW;
}
public void ProcessJob(Configuration hconfiguration)
{
try
{
var lstController = _ftsUnitOfWork.Repository<FtsController>().Get().Select(x => x. Id).Distinct().AsNoTracking();
foreach (var Id in lstController.ToList())
{
var prefix = _ftsUnitOfWork.Repository<FtsPrefix>()
.Get(x => x. Id == Id
&& x.Status == Constants.NotPrefix).Count();
var ignoreForLowerSafePoint =
_ftsUnitOfWork.Repository<ConfigurationParameter>()
.Get(y => y.ParamType == Constants.IgnoreForLowerSafePoint &&
y.ParamName == Id)
.AsNoTracking()
.Select(t => t.ParamValues).SingleOrDefault();
var currentTime = DateTime.ParseExact(DateTime.Now.ToString(Constants.HHmmssTime),
Constants.HHmmssTime,
System.Globalization.CultureInfo.InvariantCulture).TimeOfDay;
if ( !( hconfiguration.StartTime <= currentTime
&& hconfiguration.EndTime >= currentTime))
{
return;
}
}
}
catch (Exception ex)
{
throw;
}
}
repository :
public interface IRepository<TEntity> : IDisposable where TEntity : class
{
/// <summary>
/// Delete the record from the database by accepting the input parameter as id
/// </summary>
/// <param name="id">Parameter id is used to delete record from the database</param>
void Delete(object id);
/// <summary>
/// Delete the enity
/// </summary>
/// <param name="entityToDelete"></param>
void Delete(TEntity entityToDelete);
/// <summary>
/// Get Queryable by passing the Expression
/// </summary>
/// <param name="filter">this is used for filter parameter/Expression of query</param>
/// <param name="orderBy">Passing condition of orderBy for query</param>
/// <param name="includeProperties">Properties to be included in to the query while generating the query on filter/order by</param>
/// <returns>Entity type Queryable i.e. creates query depend upon accepted entity type as input parameter</returns>
#pragma warning disable S2360
IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "");
#pragma warning restore S2360
/// <summary>
/// Get Queryable by accepting the input parameter as modifiedSinceTimestamp
/// </summary>
/// <param name="modifiedSinceTimestamp"> modifiedSinceTimestamp </param>
/// <returns></returns>
IQueryable<TEntity> GetModifiedSince(long modifiedSinceTimestamp);
/// <summary>
/// Provides information of an entity by accepting the input parameters
/// </summary>
/// <param name="id">it gives the enity on the id condition </param>
/// <returns>Returns Entity </returns>
TEntity GetById(params object[] id);
/// <summary>
/// Inserts all changes made in this context as a Focus business transaction and enlists it for data insertion.
/// </summary>
/// <param name="entity">Entity i.e. object of generic type </param>
void Insert(TEntity entity);
/// <summary>
/// Gives information about which entity to update
/// </summary>
/// <param name="entityToUpdate">information about to update an enity</param>
void Update(TEntity entityToUpdate);
/// <summary>
///
/// </summary>
/// <param name="query"></param>
/// <param name="parameters"></param>
/// <returns></returns>
IEnumerable<TEntity> ExecWithStoreProcedure(string query, params object[] parameters);
/// <summary>
///
/// </summary>
/// <param name="entityToUpdate"></param>
void Detach(TEntity entityToUpdate);
}
UnitofWork -
public class FtsUnitOfWork : IFtsUnitOfWork
{
#region local variables
private readonly ILogger _logger;
private IFtsDbProvider _ftsDbProvider;
private readonly string _defaultTraceCategory;
private DbContext _context;
private Hashtable _repositories;
#endregion local variables
/// <summary>
/// Constructor for Fts Unit Of Work
/// </summary>
/// <param name="logger"></param>
/// <param name="ftsDbProvider"></param>
public FtsUnitOfWork(ILogger logger, IFtsDbProvider ftsDbProvider)
{
_defaultTraceCategory = GetType().Name;
_logger = logger;
_logger.WriteTrace("FtsUnitOfWork: Initializing", _defaultTraceCategory);
_ftsDbProvider = ftsDbProvider;
_context = new FtsDbContext(_logger, "FtsDbStore", _ftsDbProvider.GetFtsDbCompiledModel());
_logger.WriteTrace("FtsUnitOfWork: Initialized", _defaultTraceCategory);
}
/// <summary>
/// Saves all changes made in this context as a Focus business transaction and enlists it for data consolidation.
/// </summary>
/// <returns></returns>
public virtual int Save()
{
try
{
return _context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
throw new Exception("Optimistic_Concurrency_Check");
}
}
/// <summary>
///
/// </summary>
/// <param name="focusDtTransactionId"></param>
/// <returns></returns>
public int Save(Guid focusDtTransactionId)
{
return _context.SaveChanges();
}
/// <summary>
/// Generic Repository this function creates the Repository by accepting the parameter as Entity type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IRepository<T> Repository<T>() where T : class
{
if (_repositories == null)
{
_repositories = new Hashtable();
}
var type = typeof(T).Name;
if (!_repositories.ContainsKey(type))
{
var repositoryType = typeof(Repository<>);
var repositoryInstance =
Activator.CreateInstance(repositoryType
.MakeGenericType(typeof(T)), _context);
_repositories.Add(type, repositoryInstance);
}
return (IRepository<T>)_repositories[type];
}
/// <summary>
///
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <param name="sqlParameters"></param>
/// <param name="spName"></param>
/// <returns></returns>
public List<T1> GetMany<T1>(SqlParameter[] sqlParameters, string spName)
{
return _context.Database.SqlQuery<T1>(spName, sqlParameters).ToList();
}
/// <summary>
///
/// </summary>
/// <param name="isolationLevel"></param>
/// <returns></returns>
public DbContextTransaction GetDbContextTransaction(IsolationLevel isolationLevel)
{
return _context.Database.BeginTransaction(isolationLevel);
}
#region IDisposable Support
private bool _disposedValue; // To detect redundant calls
/// <summary>
///
/// </summary>
/// <param name="disposing"></param>
public virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_logger.WriteTrace("FtsUnitOfWork: Disposing", _defaultTraceCategory);
_ftsDbProvider = null;
_repositories = null;
this._context.Dispose();
this._context = null;
_logger.WriteTrace("FtsUnitOfWork: Disposed", _defaultTraceCategory);
}
_disposedValue = true;
}
}
/// <summary>
///
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
If i commented out first stub then second stub work properly otherwise throws below error :
An exception of type 'System.ArgumentNullException' occurred in Fts.Services.Housekeeping.Tests.dll but was not handled in user code
Additional information: Value cannot be null.
My silverlight project has references to the RIA Services Silverlight Client and my .web application has access to RIAServices.Server.
I can't seem to find a good tutorial to learn how to connect these. Once I understand how to get the data from my NumberGenerator method. I can pick it up from there. I have three questions that I need some help with.
First, am I setting up this project correctly? I never done a project with RIA before.
Secondly what reference do I need to use ServiceContract(), FaultContract, and OperationContract? Most examples show that they are in the System.ServiceModel library. In this case with using the external library RIA Services Silverlight Client that is not the case. It throws a error saying I am missing a reference. What other libraries would have those three in it?
My last question is what URI do you use in the SystemDomainContext? Where I found the code at used this MyFirstRIAApplication-Web-EmployeeDomainService.svc but in my case I don't have anything that has a .svc extension. Would I use the .xap or .aspx?
Here is my Silverlight code
Here is my NumberGenerator Silverlight UserControl Page Code
public partial class NumberGenerator : UserControl
{
public NumberGenerator()
{
InitializeComponent();
GenerateNumber();
}
private void GenerateButton_Click(object sender, RoutedEventArgs e)
{
GenerateNumber();
}
private void GenerateNumber()
{
int result = 0
SystemClientServices systemclientservices = SystemClientServices.Instance;
result = systemclientservices.GenerateNumber();
NumberLabel.Content = result;
}
}
Here is my System Client Services class
private readonly static SystemClientServices _instance = new SystemClientServices();
private SystemDomainContext _domainContext = new SystemDomainContext();
private SystemClientServices() { }
public static SystemClientServices Instance
{
get
{
return _instance;
}
}
public int GenerateNumber()
{
//Code goes here to get the information from the Domainservices
LoadOperation load = this._domainContext.Load(this._domainContext.GetNumberGeneratorQuery(), LoadBehavior.KeepCurrent, false);
return Convert.ToInt32(load.Entities);
}
Here is my NumberGenerator class on the silverlight local project
public sealed partial class NumberGenerator : Entity
{
private static readonly NumberGenerator _instance = new NumberGenerator();
public int NumberGenerated { get; set; }
public NumberGenerator()
{
NumberGenerated = 0;
}
public static NumberGenerator Instance
{
get
{
return _instance;
}
}
}
Here is System Domain Conext class on the silverlight local project
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel.DomainServices;
using System.ServiceModel.DomainServices.Client;
using System.ServiceModel.DomainServices.Client.ApplicationServices;
using System.ServiceModel.Web;
using System.ServiceModel;
public sealed partial class SystemDomainConext : DomainContext
{
#region Extensibility Method Definitions
/// <summary>
/// This method is invoked from the constructor once initialization is complete and
/// can be used for further object setup.
/// </summary>
partial void OnCreated();
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="EmployeeDomainContext"/> class.
/// </summary>
public SystemDomainConext() : this(new WebDomainClient<ISystemDomainServiceContract>(new Uri("MyFirstRIAApplication-Web-EmployeeDomainService.svc", UriKind.Relative)))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EmployeeDomainContext"/> class with the specified service URI.
/// </summary>
/// <param name="serviceUri">The EmployeeDomainService service URI.</param>
public SystemDomainConext(Uri serviceUri) : this(new WebDomainClient<ISystemDomainServiceContract>(serviceUri))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EmployeeDomainContext"/> class with the specified <paramref name="domainClient"/>.
/// </summary>
/// <param name="domainClient">The DomainClient instance to use for this DomainContext.</param>
public SystemDomainConext(DomainClient domainClient) : base(domainClient)
{
this.OnCreated();
}
/// <summary>
/// Gets the set of <see cref="Employee"/> entity instances that have been loaded into this <see cref="EmployeeDomainContext"/> instance.
/// </summary>
public EntitySet<NumberGenerator> GeneratedNumber
{
get
{
return base.EntityContainer.GetEntitySet<NumberGenerator>();
}
}
/// <summary>
/// Gets an EntityQuery instance that can be used to load <see cref="Employee"/> entity instances using the 'GetEmployee' query.
/// </summary>
/// <returns>An EntityQuery that can be loaded to retrieve <see cref="Employee"/> entity instances.</returns>
public EntityQuery<NumberGenerator> GetNumberGeneratorQuery()
{
this.ValidateMethod("GetGeneratedNumber", null);
return base.CreateQuery<NumberGenerator>("GetNumberGenerator", null, false, true);
}
/// <summary>
/// Creates a new EntityContainer for this DomainContext's EntitySets.
/// </summary>
/// <returns>A new container instance.</returns>
protected override EntityContainer CreateEntityContainer()
{
return new NumberGeneratorDomainContextEntityContainer();
}
/// <summary>
/// Service contract for the 'EmployeeDomainService' DomainService.
/// </summary>
[ServiceContract()]
public interface ISystemDomainServiceContract
{
/// <summary>
/// Asynchronously invokes the 'GetEmployee' operation.
/// </summary>
/// <param name="callback">Callback to invoke on completion.</param>
/// <param name="asyncState">Optional state object.</param>
/// <returns>An IAsyncResult that can be used to monitor the request.</returns>
[FaultContract(typeof(DomainServiceFault), Action="http://tempuri.org/EmployeeDomainService/GetEmployeeDomainServiceFault", Name="DomainServiceFault", Namespace="DomainServices")]
[OperationContract(AsyncPattern=true, Action="http://tempuri.org/EmployeeDomainService/GetEmployee", ReplyAction="http://tempuri.org/EmployeeDomainService/GetEmployeeResponse")]
[WebGet()]
IAsyncResult GetGeneratedNumber(AsyncCallback callback, object asyncState);
/// <summary>
/// Completes the asynchronous operation begun by 'BeginGetEmployee'.
/// </summary>
/// <param name="result">The IAsyncResult returned from 'BeginGetEmployee'.</param>
/// <returns>The 'QueryResult' returned from the 'GetEmployee' operation.</returns>
QueryResult<NumberGenerator> EndGetGeneratedNumber(IAsyncResult result);
/// <summary>
/// Asynchronously invokes the 'SubmitChanges' operation.
/// </summary>
/// <param name="changeSet">The change-set to submit.</param>
/// <param name="callback">Callback to invoke on completion.</param>
/// <param name="asyncState">Optional state object.</param>
/// <returns>An IAsyncResult that can be used to monitor the request.</returns>
[FaultContract(typeof(DomainServiceFault), Action="http://tempuri.org/EmployeeDomainService/SubmitChangesDomainServiceFault", Name="DomainServiceFault", Namespace="DomainServices")]
[OperationContract(AsyncPattern=true, Action="http://tempuri.org/EmployeeDomainService/SubmitChanges", ReplyAction="http://tempuri.org/EmployeeDomainService/SubmitChangesResponse")]
IAsyncResult BeginSubmitChanges(IEnumerable<ChangeSetEntry> changeSet, AsyncCallback callback, object asyncState);
/// <summary>
/// Completes the asynchronous operation begun by 'BeginSubmitChanges'.
/// </summary>
/// <param name="result">The IAsyncResult returned from 'BeginSubmitChanges'.</param>
/// <returns>The collection of change-set entry elements returned from 'SubmitChanges'.</returns>
IEnumerable<ChangeSetEntry> EndSubmitChanges(IAsyncResult result);
}
internal sealed class NumberGeneratorDomainContextEntityContainer : EntityContainer
{
public NumberGeneratorDomainContextEntityContainer()
{
this.CreateEntitySet<NumberGenerator>(EntitySetOperations.Edit);
}
}
}
Here is my System Domain Services class that is on the Silverlight.web
[EnableClientAccess()]
public class SystemDomainServices : DomainService
{
private NumberGenerator numberGenerator = NumberGenerator.Instance;
public int NumberGenerate()
{
return numberGenerator.NumberGenerated;
}
}
Here is the NumberGenerator class on the silverlight.web
private static readonly NumberGenerator _instance = new NumberGenerator();
[Key]
public int NumberGenerated { get; set; }
public NumberGenerator()
{
NumberGenerated = GenerateNumber();
}
public static NumberGenerator Instance
{
get
{
return _instance;
}
}
public int GenerateNumber()
{
string db_date = "";
int db_num = 0;
string todaysdate = "";
int temp_num = db_num;
int result = 0;
using (SqlConnection connection = new SqlConnection(DBConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("SELECT * FROM table", connection))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
db_date = reader.GetString(0);
db_num = (int)(reader.GetSqlInt32(1));
todaysdate = DateTime.Now.ToString("yMMdd");
temp_num = db_num;
}
reader.Close();
}
if (todaysdate != db_date)
{
using (SqlCommand dateUpate = new SqlCommand("UPDATE table SET tsDate='" + todaysdate + "' WHERE tsDate='" + db_date + "'", connection))
{
dateUpate.ExecuteNonQuery();
}
db_num = 0;
db_date = todaysdate;
}
db_num++;
using (SqlCommand numUpate = new SqlCommand("UPDATE table SET tsNum='" + db_num + "' WHERE tsNum='" + temp_num + "'", connection))
{
numUpate.ExecuteNonQuery();
}
result = Convert.ToInt32(db_date + db_num.ToString().PadLeft(3, '0'));
connection.Close();
connection.Dispose();
}
return result;
}
}
The answer to question two you might be have to go to Tools at the top, NuGet Package Manager, Package Management Console, and then type the following command to install the package for those three functions.
PM> Install-Package RIAServices.Silverlight.DomainDataSource
I'm using Entity Framework in my application with ChangeTracking Entities so I can have an Audit Log in my database for every Add, Modification, Delete (ColumnName, OriginalValue, NewValue, etc).
There are some functions that I'm not going to use Entity Framework because is very slow, for example bulk insert / update and other scenarios, instead I will user ADO.NET with Stored Procedures.
In order to keep tracking changes on both Entity Framework and ADO.NET I was thinking on use a Base Class for custom classes that will be populated from DataReaders and will need to track changes on those classes so I came up with:
public class NotifyPropertyChangeObject : INotifyPropertyChanged
{
/// <summary>
/// Track changes or not.
/// If we're working with DTOs and we fill up the DTO in the DAL we should not be tracking changes.
/// </summary>
private bool trackChanges = false;
/// <summary>
/// Changes to the object
/// </summary>
public List<TrackChange> Changes { get; private set; }
/// <summary>
/// Is the object dirty or not?
/// </summary>
public bool IsDirty
{
get { return Changes.Count > 0; }
set { ; }
}
/// <summary>
/// Event required for INotifyPropertyChanged
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// This constructor will initialize the change tracking
/// </summary>
public NotifyPropertyChangeObject()
{
// Change tracking default
trackChanges = true;
// New change tracking dictionary
Changes = new List<TrackChange>();
}
/// <summary>
/// Reset the object to non-dirty
/// </summary>
public void Reset()
{
Changes.Clear();
}
/// <summary>
/// Start tracking changes
/// </summary>
public void StartTracking()
{
trackChanges = true;
}
/// <summary>
/// Stop tracking changes
/// </summary>
public void StopTracking()
{
trackChanges = false;
}
/// <summary>
/// Change the property if required and throw event
/// </summary>
/// <param name="variable"></param>
/// <param name="property"></param>
/// <param name="value"></param>
public void ApplyPropertyChange<T, F>(ref F field, Expression<Func<T, object>> property, F value)
{
// Only do this if the value changes
if (field == null || !field.Equals(value))
{
// Get the property
var propertyExpression = GetMemberExpression(property);
if (propertyExpression == null)
throw new InvalidOperationException("You must specify a property");
// Property name
string propertyName = propertyExpression.Member.Name;
// If change tracking is enabled, we can track the changes...
if (trackChanges)
{
// Change tracking
var track = Changes.Where(c => c.Name == propertyName).FirstOrDefault();
if (track == null)
{
track = new TrackChange();
track.Name = propertyName;
track.Original = field;
track.Value = value;
Changes.Add(track);
}
else
{
track.Name = propertyName;
track.Original = field;
track.Value = value;
}
//Changes[propertyName] = value;
// Notify change
NotifyPropertyChanged(propertyName);
}
// Set the value
field = value;
}
}
/// <summary>
/// Get member expression
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
public MemberExpression GetMemberExpression<T>(Expression<Func<T, object>> expression)
{
// Default expression
MemberExpression memberExpression = null;
// Convert
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
// Member access
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
// Not a member access
if (memberExpression == null)
throw new ArgumentException("Not a member access", "expression");
// Return the member expression
return memberExpression;
}
/// <summary>
/// The property has changed
/// </summary>
/// <param name="propertyName"></param>
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Convert the changes to an XML string
/// </summary>
/// <returns></returns>
public string ChangesToXml()
{
// Prepare base objects
XDeclaration declaration = new XDeclaration("1.0", Encoding.UTF8.HeaderName, String.Empty);
XElement root = new XElement("Changes");
// Create document
XDocument document = new XDocument(declaration, root);
// Add changes to the document
// TODO: If it's an object, maybe do some other things
//foreach (KeyValuePair<string, object> change in Changes)
// root.Add(new XElement(change.Key, change.Value));
// Get the XML
return document.Document.ToString();
}
}
public class TrackChange
{
public string Name { get; set; }
public object Original { get; set; }
public object Value { get; set; }
}
That way I can keep my Audit Log functionality on both Data Context.
When saving data using ADO.NET I will have a function that will convert my Changes into Stored Procedures parameters so I can save into my AuditLog table.
Does anyone know if this is a good alternative?
Here's the situation:
/// <summary>
/// A business logic class.
/// </summary>
public class BusinessClassWithInterceptor : BusinessClass, IBusinessClass
{
/// <summary>
/// Initializes a new instance of the <see cref="BusinessClassWithoutInterceptor"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
public BusinessClassWithInterceptor(Logger logger)
: base(logger)
{
}
/// <summary>
/// Displays all cows.
/// </summary>
public void DisplayAllCows()
{
this.Logger.Write("Displaying all cows:");
var repository = new CowRepository();
foreach (CowEntity cow in repository.GetAllCows())
{
this.Logger.Write(" " + cow);
}
}
/// <summary>
/// Inserts a normande.
/// </summary>
public void InsertNormande(int id, string name)
{
this.DisplayAllCows();
var repository = new CowRepository();
repository.InsertCow(new CowEntity { Id = id, Name = name, Origin = CowOrigin.Normandie });
}
}
With castle windsor, this class is configured to be intercepted with this interceptor:
/// <summary>
/// Interceptor for logging business methods.
/// </summary>
public class BusinessLogInterceptor : IInterceptor
{
/// <summary>
/// Intercepts the specified invocation.
/// </summary>
/// <param name="invocation">The invocation.</param>
public void Intercept(IInvocation invocation)
{
Logger logger = ((IBusinessClass)invocation.InvocationTarget).Logger;
var parameters = new StringBuilder();
ParameterInfo[] methodParameters = invocation.Method.GetParameters();
for (int index = 0; index < methodParameters.Length; index++)
{
parameters.AppendFormat("{0} = {1}", methodParameters[index].Name, invocation.Arguments[index]);
if (index < methodParameters.Length - 1)
{
parameters.Append(", ");
}
}
logger.Format("Calling {0}( {1} )", invocation.Method.Name, parameters.ToString());
invocation.Proceed();
logger.Format("Exiting {0}", invocation.Method.Name);
}
}
The issue takes place during the call to InsertNormande.
The call to InsertNormande is well intercepted, but the call to DisplayAllCows in InsertNormande is not intercepted...
It really bothers me.
Is there a way to achieve interception in this scenario ?
I don't think there's an easy way of doing it... method calls that are internal to the class can't be intercepted, since they don't go through the proxy.
You could achieve logging of all methods by other means, such as an AOP framework like PostSharp