How do I setup a list of test organizations with my new DBContext? - entity-framework

I have an application with uses multiple contexts for EF, some are code first, some server first, and all are dynamic.
I have a class that I call to get these contexts in my app.
Each context implements an interface such as:
public interface IDatabaseContextSwitcher
{
IVGSContext GetDatabase(string organization);
IVGSContext GetDatabase(Guid organizationGuid, string email);
IVGSServerConn GetServerDatabase(string databaseName);
IAuthContext GetAuthorizationDatabase();
}
I therefore has a class that implments the instances of these interfaces in the application. (VGSContext, VGSServerConn, and AuthContext).
I am trying to test these in my test project. I have made new classes from the interfaces with the plan to plug in some DbSets into these new classes and then test that my controllers do the correct thing.
However, I can't seem to figure out how to initialize the DBSets.
For example the following dies on Add:
public AuthContextForTesting()
{
Organizations.Add(new Organization()
{OrganizationName = "Test1", PK_Organization = Guid.Parse("34CE4F83-B3C9-421B-B1F3-42BBCDA9A004")});
var cnt = Organizations.Count();
}
public DbSet<Organization> Organizations { get; set; }
I tried to initialize the DBSet with:
Organizations=new DbSet();
But it gives an error that this is not allowed due to permissions.
How do I set up my initial dbsets in code for my tests?

To be able to do this, you first have to derive a class from DBSet. Sadly, my app uses both Core EF and EF 6 so I had to create 2 classes.
EF 6 Class
public class FakeDbSet<T> : System.Data.Entity.DbSet<T>, IDbSet<T> where T : class
{
List<T> _data;
public FakeDbSet()
{
_data = new List<T>();
}
public override T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public override T Add(T item)
{
_data.Add(item);
return item;
}
public override T Remove(T item)
{
_data.Remove(item);
return item;
}
public override T Attach(T item)
{
return null;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public override T Create()
{
return Activator.CreateInstance<T>();
}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public List<T> Local
{
get { return _data; }
}
public override IEnumerable<T> AddRange(IEnumerable<T> entities)
{
_data.AddRange(entities);
return _data;
}
public override IEnumerable<T> RemoveRange(IEnumerable<T> entities)
{
for (int i = entities.Count() - 1; i >= 0; i--)
{
T entity = entities.ElementAt(i);
if (_data.Contains(entity))
{
Remove(entity);
}
}
return this;
}
Type IQueryable.ElementType
{
get { return _data.AsQueryable().ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _data.AsQueryable().Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _data.AsQueryable().Provider; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
The EF Core had to include some interfaces to work.
public class FakeCoreDbSet<T> : Microsoft.EntityFrameworkCore.DbSet<T> , IQueryable, IEnumerable<T> where T : class
{
List<T> _data;
public FakeCoreDbSet()
{
_data = new List<T>();
}
public override T Find(params object[] keyValues)
{
throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
}
public override EntityEntry<T> Add(T item)
{
_data.Add(item);
//return item;
return null;
}
public override EntityEntry<T> Remove(T item)
{
_data.Remove(item);
//return item;
return null;
}
public override EntityEntry<T> Attach(T item)
{
return null;
}
public T Detach(T item)
{
_data.Remove(item);
return item;
}
public IList GetList()
{
return _data.ToList();
}
//public override T Create()
//{
// return Activator.CreateInstance<T>();
//}
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
{
return Activator.CreateInstance<TDerivedEntity>();
}
public List<T> Local
{
get { return _data; }
}
public override void AddRange(IEnumerable<T> entities)
{
_data.AddRange(entities);
//return _data;
}
public override void RemoveRange(IEnumerable<T> entities)
{
for (int i = entities.Count() - 1; i >= 0; i--)
{
T entity = entities.ElementAt(i);
if (_data.Contains(entity))
{
Remove(entity);
}
}
// this;
}
Type IQueryable.ElementType
{
get { return _data.AsQueryable().ElementType; }
}
System.Linq.Expressions.Expression IQueryable.Expression
{
get { return _data.AsQueryable().Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _data.AsQueryable().Provider; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
Once these were created I could use MockObjects to get to them.
Note _dbContextSwitcher is the class created with a Moq that calls the different databases.
var vgsdatabase = new Mock<IVGSContext>();
var settings=new FakeCoreDbSet<Setting>();
settings.Add(new Setting()
{
SettingID = "OrgPrivacy",
PK_Setting = Guid.NewGuid(),
UserID = "",
Value = "No"
});
vgsdatabase.Setup(s => s.Setting).Returns(settings);
_dbcontextSwitcher.Setup(s => s.GetDatabase(It.IsAny<string>())).Returns(vgsdatabase.Object);

Related

Xamarin.Forms MVVM How to load bindable Picker with data from async process in ViewModel?

In my ViewModel i want o load the Picker source RegionName data from an Azure Region table. I extract data from table in an async method but the Picker displays an empty List even after ObservableCollection or List has changed or crashes.
When using PropertyChanged on the ListRegion list itself the app crashes.
In my models:
public class Region
{
public string Id { get; set; }
public string RegionName { get; set; }
}
In my RegisterPage.xaml:
<Picker SelectedIndex="{Binding RegionsSelectedIndex, Mode=TwoWay}"
ItemsSource="{Binding Regions}"
Margin="0,15,0,0"
Title="Select a region">
</Picker>
in my RegisterPage.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RegisterPage : ContentPage
{
RegisterViewModel registerVM;
public RegisterPage ()
{
InitializeComponent ();
registerVM = new RegisterViewModel();
this.BindingContext = registerVM;
}
protected override void OnAppearing()
{
base.OnAppearing();
}
in my RegisterPageViewModel:
public class RegisterViewModel: INotifyPropertyChanged
{
ApiServices _apiServices = new ApiServices();
public RegisterViewModel()
{
initializePickerAsync();
}
async private void initializePickerAsync()
{
try
{
var regionsList = await App.MobileService.GetTable<Models.Region>().ToListAsync();
List<string> regionsStringList = new List<string>();
foreach (Models.Region reg in regionsList)
{
regionsStringList.Add(reg.RegionName);
}
Regions = regionsStringList;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/*
private RegisterViewModel (ObservableCollection<Models.Region> regionData)
{
ObservableCollection<string> regionDataAsStringList = new ObservableCollection<string>();
foreach (Models.Region r in regionData)
{
regionDataAsStringList.Add(r.RegionName);
}
this.Regions = regionDataAsStringList;
}
async public static Task<RegisterViewModel> BuildViewModelAsync()
{
ObservableCollection<Models.Region> tmpRegionData = new ObservableCollection<Models.Region>(await App.MobileService.GetTable<Models.Region>().ToListAsync());
return new RegisterViewModel(tmpRegionData);
}
*/
int regionsSelectedIndex = 0;
public int RegionsSelectedIndex
{
get
{
return regionsSelectedIndex;
}
set
{
if (regionsSelectedIndex != value)
{
regionsSelectedIndex = value;
OnPropertyChanged(nameof(RegionsSelectedIndex));
if (regionsSelectedIndex >= 0)
{
Region = Regions[regionsSelectedIndex];
}
}
}
}
// public ObservableCollection<Region> Regions {get;set}
public List<string> Regions
{
get
{
return Regions;
}
set
{
if (Regions != value)
{
Regions = value;
OnPropertyChanged(nameof(Regions));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
you seem to be doing a lot of unnecessary creating and assigning of different lists of data. You should be able to create your ObservableCollection ONCE and then add your data to it, something like this
in your ViewModel
ObservableCollection<Region> Regions { get; set; }
public RegisterViewModel()
{
Regions = new ObservableCollection<Region>();
}
public async void GetData()
{
var regionsList = await App.MobileService.GetTable<Models.Region>().ToListAsync();
foreach (Models.Region reg in regionsList)
{
Regions.Add(reg);
}
}
in your page
protected async override void OnAppearing()
{
base.OnAppearing();
await registerVM.GetData();
}

Best way to handle complex entities (relational) with Generic CRUD functions

I have tried using this generic functions to insert-update Entities but I always thought that maybe I am doing this totally wrong so therefore I would like to have your opinions/suggestions.
These are my Insert & Update functions:
public static bool Insert<T>(T item) where T : class
{
using (ApplicationDbContext ctx = new ApplicationDbContext())
{
try
{
ctx.Set<T>().Add(item);
ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// ...
}
}
}
public static bool Update<T>(T item) where T : class
{
using (ApplicationDbContext ctx = new ApplicationDbContext())
{
try
{
Type itemType = item.GetType();
// switch statement to perform actions according which type we are working on
ctx.SaveChanges();
return true;
}
catch (Exception ex)
{
// ...
}
}
}
I have learned that i can use ctx.Entry(item).State = EntityState.Modified; and I have seen so many ways of inserting-updating entities that I am very curious on what is the easiest most manageable way of performing CRUD actions ?
I know about the repository pattern and so on but i don't have much experience with interfaces or I don't seem to fully understand whats used so I prefer not to use it till I fully get it.
my approach for that is to use IRepository pattern to wrap CRUD and to make dependencies injection easier in my application, here an example on how i do it:
Define your contract like following:
(i am simplifying the example and admitting that all your tables have an integer id -i mean it is not guid or string or whatever- )
public interface IGenericRepository<TEntity> where TEntity : class
{
#region ReadOnlyRepository
TEntity GetById(int id);
ICollection<TEntity> GetAll();
ICollection<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includeProperties);
ICollection<TEntity> Query(Expression<Func<TEntity, bool>> expression, params Expression<Func<TEntity, object>>[] includeProperties);
PagedModel<TEntity> Query(Expression<Func<TEntity, bool>> expression, SortOptions sortOptions, PaginateOptions paginateOptions, params Expression<Func<TEntity, object>>[] includeProperties);
int Max(Expression<Func<TEntity, int>> expression);
#endregion
#region PersistRepository
bool Add(TEntity entity);
bool AddRange(IEnumerable<TEntity> items);
bool Update(TEntity entity);
bool Delete(TEntity entity);
bool DeleteById(int id);
#endregion
}
and then the implementation:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
#region Fields
protected DbContext CurrentContext { get; private set; }
protected DbSet<TEntity> EntitySet { get; private set; }
#endregion
#region Ctor
public GenericRepository(DbContext context)
{
CurrentContext = context;
EntitySet = CurrentContext.Set<TEntity>();
}
#endregion
#region IReadOnlyRepository Implementation
public virtual TEntity GetById(int id)
{
try
{
//use your logging method (log 4 net used here)
DomainEventSource.Log.Info(string.Format("getting entity {0} with id {1}", typeof(TEntity).Name, id));
return EntitySet.Find(id); //dbcontext manipulation
}
catch (Exception exception)
{
/// example of error handling
DomainEventSource.Log.Error(exception.Message);
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);// this is specific error formatting class you can do somthing like that to fit your needs
}
}
public virtual ICollection<TEntity> GetAll()
{
try
{
return EntitySet.ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
public virtual ICollection<TEntity> GetAll(params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = LoadProperties(includeProperties);
return query.ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
public virtual ICollection<TEntity> Query(Expression<Func<TEntity, bool>> expression, params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = LoadProperties(includeProperties);
return query.Where(expression).ToList();
}
catch (Exception exception)
{
//... Do whatever you want
}
}
// returning paged results for example
public PagedModel<TEntity> Query(Expression<Func<TEntity, bool>> expression,SortOptions sortOptions, PaginateOptions paginateOptions, params Expression<Func<TEntity, object>>[] includeProperties)
{
try
{
var query = EntitySet.AsQueryable().Where(expression);
var count = query.Count();
//Unfortunatly includes can't be covered with a UT and Mocked DbSets...
if (includeProperties.Length != 0)
query = includeProperties.Aggregate(query, (current, prop) => current.Include(prop));
if (paginateOptions == null || paginateOptions.PageSize <= 0 || paginateOptions.CurrentPage <= 0)
return new PagedModel<TEntity> // specific pagination model, you can define yours
{
Results = query.ToList(),
TotalNumberOfRecords = count
};
if (sortOptions != null)
query = query.OrderByPropertyOrField(sortOptions.OrderByProperty, sortOptions.IsAscending);
var skipAmount = paginateOptions.PageSize * (paginateOptions.CurrentPage - 1);
query = query.Skip(skipAmount).Take(paginateOptions.PageSize);
return new PagedModel<TEntity>
{
Results = query.ToList(),
TotalNumberOfRecords = count,
CurrentPage = paginateOptions.CurrentPage,
TotalNumberOfPages = (count / paginateOptions.PageSize) + (count % paginateOptions.PageSize == 0 ? 0 : 1)
};
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
}
#endregion
#region IPersistRepository Repository
public bool Add(TEntity entity)
{
try
{
// you can do some extention methods here to set up creation date when inserting or createdBy etc...
EntitySet.Add(entity);
return true;
}
catch (Exception exception)
{
//DomainEventSource.Log.Failure(ex.Message);
//or
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
}
public bool AddRange(IEnumerable<TEntity> items)
{
try
{
foreach (var entity in items)
{
Add(entity);
}
}
catch (Exception exception)
{
//DomainEventSource.Log.Failure(ex.Message);
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool Update(TEntity entity)
{
try
{
CurrentContext.Entry(entity).State = EntityState.Modified;
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool Delete(TEntity entity)
{
try
{
if (CurrentContext.Entry(entity).State == EntityState.Detached)
{
EntitySet.Attach(entity);
}
EntitySet.Remove(entity);
}
catch (Exception exception)
{
var errors = new List<ExceptionDetail> { new ExceptionDetail { ErrorMessage = exception.Message } };
throw new ServerException(errors);
}
return true;
}
public bool DeleteById(TKey id)
{
var entityToDelete = GetById(id);
return Delete(entityToDelete);
}
#endregion
#region Loading dependancies Utilities
private IQueryable<TEntity> LoadProperties(IEnumerable<Expression<Func<TEntity, object>>> includeProperties)
{
return includeProperties.Aggregate<Expression<Func<TEntity, object>>, IQueryable<TEntity>>(EntitySet, (current, includeProperty) => current.Include(includeProperty));
}
#endregion
}
I am admitting that your model classes are already created and decorated.
After this , you need to create your entityRepository like following : this is an example of managing entity called Ticket.cs
public class TicketRepository : GenericRepository<Ticket>, ITicketRepository
{
// the EntityRepository classes are made in case you have some ticket specific methods that doesn't
//have to be in generic repository
public TicketRepository(DbContext context)
: base(context)
{
}
// Add specific generic ticket methods here (not business methods-business methods will come later-)
}
After this comes the UnitOfWork class which allows us to unify entry to the database context and provides us an instance of repositories on demand using dependency injection
public class UnitOfwork : IUnitOfWork
{
#region Fields
protected DbContext CurrentContext { get; private set; }
private ITicketRepository _tickets;
#endregion
#region ctor
public UnitOfwork(DbContext context)
{
CurrentContext = context;
}
#endregion
#region UnitOfWorkBaseImplementation
public void Commit()
{
try
{
CurrentContext.SaveChanges();
}
catch (Exception e)
{
/// catch
}
}
public void Rollback()
{
foreach (var entry in CurrentContext.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Detached:
break;
case EntityState.Unchanged:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
#region complete RollBack()
private void RejectScalarChanges()
{
foreach (var entry in CurrentContext.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
case EntityState.Deleted:
entry.State = EntityState.Modified; //Revert changes made to deleted entity.
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Detached:
break;
case EntityState.Unchanged:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private void RejectNavigationChanges()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var deletedRelationships = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).Where(e => e.IsRelationship && !this.RelationshipContainsKeyEntry(e));
var addedRelationships = objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Where(e => e.IsRelationship);
foreach (var relationship in addedRelationships)
relationship.Delete();
foreach (var relationship in deletedRelationships)
relationship.ChangeState(EntityState.Unchanged);
}
private bool RelationshipContainsKeyEntry(System.Data.Entity.Core.Objects.ObjectStateEntry stateEntry)
{
//prevent exception: "Cannot change state of a relationship if one of the ends of the relationship is a KeyEntry"
//I haven't been able to find the conditions under which this happens, but it sometimes does.
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var keys = new[] { stateEntry.OriginalValues[0], stateEntry.OriginalValues[1] };
return keys.Any(key => objectContext.ObjectStateManager.GetObjectStateEntry(key).Entity == null);
}
#endregion
public void Dispose()
{
if (CurrentContext != null)
{
CurrentContext.Dispose();
}
}
#endregion
#region properties
public ITicketRepository Tickets
{
get { return _tickets ?? (_tickets = new TicketRepository(CurrentContext)); }
}
#endregion
}
Now for the last part we move to our business service layer and make a ServiceBase class which will be implemented by all business services
public class ServiceBase : IServiceBase
{
private bool _disposed;
#region IServiceBase Implementation
[Dependency]
public IUnitOfWork UnitOfWork { protected get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
var disposableUow = UnitOfWork as IDisposable;
if (disposableUow != null)
disposableUow.Dispose();
}
_disposed = true;
}
#endregion
}
and finally one example of business service class and how to use your CRUD and play with your business rules (i am using properties injection which is not the best to do so i suggest to change it and use constructor injection instead)
public class TicketService : ServiceBase, ITicketService
{
#region fields
private IUserService _userService;
private IAuthorizationService _authorizationService;
#endregion
#region Properties
[Dependency]
public IAuthorizationService AuthorizationService
{
set { _authorizationService = value; }
}
[Dependency]
public IUserService UserService
{
set { _userService = value; }
}
public List<ExceptionDetail> Errors { get; set; }
#endregion
#region Ctor
public TicketService()
{
Errors = new List<ExceptionDetail>();
}
#endregion
#region IServiceBase Implementation
/// <summary>
/// desc
/// </summary>
/// <returns>array of TicketAnomalie</returns>
public ICollection<Ticket> GetAll()
{
return UnitOfWork.Tickets.GetAll();
}
/// <summary>
/// desc
/// </summary>
/// <param name="id"></param>
/// <returns>TicketAnomalie</returns>
public Ticket GetTicketById(int id)
{
return UnitOfWork.Tickets.GetById(id);
}
/// <summary>
/// description here
/// </summary>
/// <returns>Collection of Ticket</returns>
public ICollection<Ticket> GetAllTicketsWithDependencies()
{
return UnitOfWork.Tickets.Query(tick => true, tick => tick.Anomalies);
}
/// <summary>
/// description here
/// </summary>
/// <param name="id"></param>
/// <returns>Ticket</returns>
public Ticket GetTicketWithDependencies(int id)
{
return UnitOfWork.Tickets.Query(tick => tick.Id == id, tick => tick.Anomalies).SingleOrDefault();
}
/// <summary>
/// Add new ticket to DB
/// </summary>
/// <param name="anomalieId"></param>
/// <returns>Boolean</returns>
public bool Add(int anomalieId)
{
var anomalie = UnitOfWork.Anomalies.Query(ano => ano.Id.Equals(anomalieId), ano => ano.Tickets).FirstOrDefault();
var currentUser = WacContext.Current;
var superv = _userService.GetSupervisorUserProfile();
var sup = superv.FirstOrDefault();
if (anomalie != null)
{
var anomalies = new List<Anomalie>();
var anom = UnitOfWork.Anomalies.GetById(anomalieId);
anomalies.Add(anom);
if (anomalie.Tickets.Count == 0 && sup != null)
{
var ticket = new Ticket
{
User = sup.Id,
CreatedBy = currentUser.GivenName,
Anomalies = anomalies,
Path = UnitOfWork.SearchCriterias.GetById(anom.ParcoursId),
ContactPoint = UnitOfWork.ContactPoints.GetById(anom.ContactPointId)
};
UnitOfWork.Tickets.Add(ticket);
UnitOfWork.Commit();
}
}
else
{
Errors.Add(AnomaliesExceptions.AnoNullException);
}
if (Errors.Count != 0) throw new BusinessException(Errors);
return true;
}
public bool Update(Ticket ticket)
{
if (ticket == null)
{
Errors.Add(AnomaliesExceptions.AnoNullException);
}
else
if (!Exists(ticket.Id))
{
Errors.Add(AnomaliesExceptions.AnoToUpdateNotExistException);
}
if (Errors.Count != 0) throw new BusinessException(Errors);
UnitOfWork.Tickets.Update(ticket);
UnitOfWork.Commit();
return true;
}
public bool Exists(int ticketId)
{
var operationDbEntity =
UnitOfWork.Tickets.Query(t => t.Id.Equals(ticketId)).ToList();
return operationDbEntity.Count != 0;
}
#endregion
#region Business Implementation
//play with your buiness :)
#endregion
}
Finally,
i suggest that you redo this using asynchronous methods (async await since it allows a better management of service pools in the web server)
Note that this is my own way of managing my CRUD with EF and Unity. you can find a lot of other implementations that can inspire you.
Hope this helps,

Unspecified value parameters :: type[T] Scala

I have the below function and am getting an error on the second line saying Unspecified value parameters :: type[T], Type mismatch, expected: Command[NotInferedT], actual: DeletePrivilegeMappingCmd.
execute() takes in a parameter of Command[T] and DeletePrivilegeMappingCmd extends that Command[T] requirement.
I figured out based on the error I need to add a [Object] to the signature, but I'm not sure what Type I have to include inside the []. Adding any Object gets rid of the error, but I'm assuming there will be a runtime error if the correct type is not used.
What type am I supposed to add?
Error message signature
def deleteGroupPrivilegeMapping(privilegeId: String, groupId: String) {
commandExecutor.execute(new DeletePrivilegeMappingCmd(privilegeId, null, groupId))
}
Updated message signature
def deleteGroupPrivilegeMapping(privilegeId: String, groupId: String) {
commandExecutor.execute(new DeletePrivilegeMappingCmd(privilegeId, null, groupId)[String][String])
}
EDIT:
Signature for Command and execute
public interface Command<T> extends BaseCommand<T, CommandContext> {
T execute(CommandContext commandContext);
}
Implementation for DeletePrivilegeMappingCmd
public class DeletePrivilegeCmd implements Command<Void>, Serializable {
private static final long serialVersionUID = 1L;
protected String id;
public DeletePrivilegeCmd(String id) {
if (id == null) {
throw new FlowableIllegalArgumentException("id is null");
}
this.id = id;
}
public Void execute(CommandContext commandContext) {
commandContext.gePrivilegeMappingEntityManager().deleteByPrivilegeId(id);
commandContext.getPrivilegeEntityManager().delete(id);
return null;
}
}
Command Context Definition
public class CommandContext extends AbstractCommandContext {
private static Logger log = LoggerFactory.getLogger(CommandContext.class);
protected ProcessEngineConfigurationImpl processEngineConfiguration;
protected FailedJobCommandFactory failedJobCommandFactory;
protected FlowableEngineAgenda agenda;
protected Map<String, ExecutionEntity> involvedExecutions = new HashMap(1);
protected LinkedList<Object> resultStack = new LinkedList();
public CommandContext(Command<?> command, ProcessEngineConfigurationImpl processEngineConfiguration) {
super(command);
this.processEngineConfiguration = processEngineConfiguration;
this.failedJobCommandFactory = processEngineConfiguration.getFailedJobCommandFactory();
this.sessionFactories = processEngineConfiguration.getSessionFactories();
this.agenda = processEngineConfiguration.getAgendaFactory().createAgenda(this);
}
protected void logException() {
if(!(this.exception instanceof JobNotFoundException) && !(this.exception instanceof FlowableTaskAlreadyClaimedException)) {
super.logException();
} else {
log.info("Error while closing command context", this.exception);
}
}
public void addCloseListener(CommandContextCloseListener commandContextCloseListener) {
if(this.closeListeners == null) {
this.closeListeners = new ArrayList(1);
}
this.closeListeners.add(commandContextCloseListener);
}
public DbSqlSession getDbSqlSession() {
return (DbSqlSession)this.getSession(DbSqlSession.class);
}
public EntityCache getEntityCache() {
return (EntityCache)this.getSession(EntityCache.class);
}
public DeploymentEntityManager getDeploymentEntityManager() {
return this.processEngineConfiguration.getDeploymentEntityManager();
}
public ResourceEntityManager getResourceEntityManager() {
return this.processEngineConfiguration.getResourceEntityManager();
}
public ByteArrayEntityManager getByteArrayEntityManager() {
return this.processEngineConfiguration.getByteArrayEntityManager();
}
public ProcessDefinitionEntityManager getProcessDefinitionEntityManager() {
return this.processEngineConfiguration.getProcessDefinitionEntityManager();
}
public ModelEntityManager getModelEntityManager() {
return this.processEngineConfiguration.getModelEntityManager();
}
public ProcessDefinitionInfoEntityManager getProcessDefinitionInfoEntityManager() {
return this.processEngineConfiguration.getProcessDefinitionInfoEntityManager();
}
public ExecutionEntityManager getExecutionEntityManager() {
return this.processEngineConfiguration.getExecutionEntityManager();
}
public TaskEntityManager getTaskEntityManager() {
return this.processEngineConfiguration.getTaskEntityManager();
}
public IdentityLinkEntityManager getIdentityLinkEntityManager() {
return this.processEngineConfiguration.getIdentityLinkEntityManager();
}
public VariableInstanceEntityManager getVariableInstanceEntityManager() {
return this.processEngineConfiguration.getVariableInstanceEntityManager();
}
public HistoricProcessInstanceEntityManager getHistoricProcessInstanceEntityManager() {
return this.processEngineConfiguration.getHistoricProcessInstanceEntityManager();
}
public HistoricDetailEntityManager getHistoricDetailEntityManager() {
return this.processEngineConfiguration.getHistoricDetailEntityManager();
}
public HistoricVariableInstanceEntityManager getHistoricVariableInstanceEntityManager() {
return this.processEngineConfiguration.getHistoricVariableInstanceEntityManager();
}
public HistoricActivityInstanceEntityManager getHistoricActivityInstanceEntityManager() {
return this.processEngineConfiguration.getHistoricActivityInstanceEntityManager();
}
public HistoricTaskInstanceEntityManager getHistoricTaskInstanceEntityManager() {
return this.processEngineConfiguration.getHistoricTaskInstanceEntityManager();
}
public HistoricIdentityLinkEntityManager getHistoricIdentityLinkEntityManager() {
return this.processEngineConfiguration.getHistoricIdentityLinkEntityManager();
}
public EventLogEntryEntityManager getEventLogEntryEntityManager() {
return this.processEngineConfiguration.getEventLogEntryEntityManager();
}
public JobEntityManager getJobEntityManager() {
return this.processEngineConfiguration.getJobEntityManager();
}
public TimerJobEntityManager getTimerJobEntityManager() {
return this.processEngineConfiguration.getTimerJobEntityManager();
}
public SuspendedJobEntityManager getSuspendedJobEntityManager() {
return this.processEngineConfiguration.getSuspendedJobEntityManager();
}
public DeadLetterJobEntityManager getDeadLetterJobEntityManager() {
return this.processEngineConfiguration.getDeadLetterJobEntityManager();
}
public AttachmentEntityManager getAttachmentEntityManager() {
return this.processEngineConfiguration.getAttachmentEntityManager();
}
public TableDataManager getTableDataManager() {
return this.processEngineConfiguration.getTableDataManager();
}
public CommentEntityManager getCommentEntityManager() {
return this.processEngineConfiguration.getCommentEntityManager();
}
public PropertyEntityManager getPropertyEntityManager() {
return this.processEngineConfiguration.getPropertyEntityManager();
}
public EventSubscriptionEntityManager getEventSubscriptionEntityManager() {
return this.processEngineConfiguration.getEventSubscriptionEntityManager();
}
public HistoryManager getHistoryManager() {
return this.processEngineConfiguration.getHistoryManager();
}
public JobManager getJobManager() {
return this.processEngineConfiguration.getJobManager();
}
public void addInvolvedExecution(ExecutionEntity executionEntity) {
if(executionEntity.getId() != null) {
this.involvedExecutions.put(executionEntity.getId(), executionEntity);
}
}
public boolean hasInvolvedExecutions() {
return this.involvedExecutions.size() > 0;
}
public Collection<ExecutionEntity> getInvolvedExecutions() {
return this.involvedExecutions.values();
}
public FailedJobCommandFactory getFailedJobCommandFactory() {
return this.failedJobCommandFactory;
}
public ProcessEngineConfigurationImpl getProcessEngineConfiguration() {
return this.processEngineConfiguration;
}
public FlowableEventDispatcher getEventDispatcher() {
return this.processEngineConfiguration.getEventDispatcher();
}
public FlowableEngineAgenda getAgenda() {
return this.agenda;
}
public Object getResult() {
return this.resultStack.pollLast();
}
public void setResult(Object result) {
this.resultStack.add(result);
}
}

Unit testing generic repository

I'm pretty new to unit testing and I'm having some problems with regards, to unit testing a generic repository in my application. I've implemented the unit of work pattern in my ASP.NET MVC application. My classes look like this:
public class UnitOfWork : IUnitOfWork
{
private bool disposed = false;
private IGenericRepository<Shop> _shopRespository;
public UnitOfWork(PosContext context)
{
this.Context = context;
}
public PosContext Context { get; private set; }
public IGenericRepository<Shop> ShopRepository
{
get
{
return this._shopRespository ?? (this._shopRespository = new GenericRepository<Shop>(this.Context));
}
}
public void SaveChanges()
{
this.Context.SaveChanges();
}
public void Dispose()
{
this.Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.Context.Dispose();
}
this.disposed = true;
}
}
}
public class PosContext : DbContext, IPosContext
{
public DbSet<Shop> Shops { get; private set; }
}
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
private readonly PosContext context;
private readonly DbSet<T> dbSet;
public GenericRepository(PosContext context)
{
this.context = context;
this.dbSet = context.Set<T>();
}
public virtual IEnumerable<T> Get(
Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
string includeProperties = "")
{
IQueryable<T> query = this.dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual T Find(object id)
{
return this.dbSet.Find(id);
}
public virtual void Add(T entity)
{
this.dbSet.Add(entity);
}
public virtual void Remove(object id)
{
T entityToDelete = this.dbSet.Find(id);
this.Remove(entityToDelete);
}
public virtual void Remove(T entityToDelete)
{
if (this.context.Entry(entityToDelete).State == EntityState.Detached)
{
this.dbSet.Attach(entityToDelete);
}
this.dbSet.Remove(entityToDelete);
}
public virtual void Update(T entityToUpdate)
{
this.dbSet.Attach(entityToUpdate);
this.context.Entry(entityToUpdate).State = EntityState.Modified;
}
I'm using NUnit and FakeItEasy to write my unit tests. In my set up function, I create a UnitIfWork object with a fake PosContext object. I then populate the context with a few Shop objects.
[SetUp]
public void SetUp()
{
this.unitOfWork = new UnitOfWork(A.Fake<PosContext>());
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 1, Name = "Test name1" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 2, Name = "Test name2" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 3, Name = "Test name3" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 4, Name = "Test name4" });
this.unitOfWork.ShopRepository.Add(new Shop() { Id = 5, Name = "Test name5" });
this.Controller = new ShopController(this.unitOfWork);
}
It works fine when I test the Find-method of the GenericRepository. The correct Shop object is returned and I can assert that it works fine:
[TestCase]
public void DetailsReturnsCorrectShop()
{
// Arrange
int testId = 1;
// Act
Shop shop = this.unitOfWork.ShopRepository.Find(testId);
ViewResult result = this.Controller.Details(testId) as ViewResult;
// Assert
Shop returnedShop = (Shop)result.Model;
Assert.AreEqual(testId, returnedShop.Id);
}
But when I want to test that the Get-method returns all shops from the repository, if I do not give any filter params, I get an empty list back. I can't figure out why?
[TestCase]
public void IndexReturnsListOfShops()
{
// Arrange
// Act
ViewResult result = this.Controller.Index() as ViewResult;
// Assert
List<Shop> returnedShops = (List<Shop>)result.Model;
Assert.AreEqual(5, returnedShops.Count);
}
The ShopController looks like this:
public class ShopController : Controller
{
private readonly IUnitOfWork unitOfWork;
public ShopController(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
// GET: /Shop/
public ActionResult Index()
{
return View(this.unitOfWork.ShopRepository.Get());
}
// GET: /Shop/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Shop shop = this.unitOfWork.ShopRepository.Find(id);
if (shop == null)
{
return HttpNotFound();
}
return View(shop);
}
}
Can you help me figure out why I get an empty list back from the Get-method?

Using EF4 MVC2, Repository and Unit of Work Patterns, Issue with updates to data

I am using MVC2 with Entity Framework 4 and am trying to implement a Repository and UnitofWork pattern. My Adds and Deletes work fine, however when the edit is called _context.SaveChanges() does save the new changes to the database. I have stepped through the code in debug and see the new values are in the object being passed to the Edit function in the controller, but when the commit is called, nothing is updated.See my code below: Thanks for your help.
Here is my IRepository
namespace EventScheduling.DataModel.Custom
{
public interface IRepository<T>
{
void Add(T newEntity);
void Remove(T entity);
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
IQueryable<T> FindAll();
}
}
SQLRepository Implementation
namespace EventScheduling.DataModel.Custom
{
public class SQLRepository<T>:IRepository<T> where T : class
{
protected ObjectSet<T> _objectSet;
public SQLRepository(ObjectContext context)
{
_objectSet = context.CreateObjectSet<T>();
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate){
return _objectSet.Where(predicate);
}
public void Add(T newEntity)
{
_objectSet.AddObject(newEntity);
}
public void Remove(T entity)
{
_objectSet.DeleteObject(entity);
}
public IQueryable<T> FindAll()
{
return _objectSet;
}
}
}
Unit of Work Implementation
namespace EventScheduling.DataModel.Custom
{
public interface IUnitOfWork
{
IRepository<utility_event> utility_event { get; }
IRepository<event_activity> event_activity { get; }
IRepository<employee> employee{ get; }
IRepository<activity_resource> activity_resource { get; }
IRepository<Elmah_Error> Elmah_Error { get; }
IRepository< location> location { get; }
IRepository<location_station> location_station { get; }
IRepository<registration_type> registration_type { get; }
IRepository< resource> resource { get; }
IRepository<shift> shift { get; }
IRepository<shift_person> shift_person{ get; }
IRepository<event_type> event_type { get; }
IRepository<status> status { get; }
void Commit();
}
}
SqlUnitOfWork Implementation
namespace EventScheduling.DataModel.Custom
{
public class SqlUnitOfWork: IUnitOfWork
{
readonly ObjectContext _context;
const String ConnectionStringName = "EventEntities";
public SqlUnitOfWork()
{
var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString;
_context = new ObjectContext(connectionString);
_context.ContextOptions.LazyLoadingEnabled = true;
}
public IRepository<utility_event> utility_event
{
get {
if (_utilityEvent == null)
{
_utilityEvent = new SQLRepository<utility_event>(_context);
}
return _utilityEvent;
}
}
public IRepository<event_activity> event_activity
{
get
{
if (_eventActivities == null)
{
_eventActivities = new SQLRepository<event_activity>(_context);
}
return _eventActivities;
}
}
public IRepository<employee> employee
{
get
{
if (_employees == null)
{
_employees = new SQLRepository<employee>(_context);
}
return _employees;
}
}
public IRepository<activity_resource> activity_resource
{
get
{
if (_activityResources == null)
{
_activityResources = new SQLRepository<activity_resource>(_context);
}
return _activityResources;
}
}
public IRepository<location> location
{
get
{
if (_locations == null)
{
_locations = new SQLRepository<location>(_context);
}
return _locations;
}
}
public IRepository<location_station> location_station
{
get
{
if (_locationStations == null)
{
_locationStations = new SQLRepository<location_station>(_context);
}
return _locationStations;
}
}
public IRepository<registration_type> registration_type
{
get
{
if (_registrationTypes == null)
{
_registrationTypes = new SQLRepository<registration_type>(_context);
}
return _registrationTypes;
}
}
public IRepository<resource> resource
{
get
{
if (_resources == null)
{
_resources = new SQLRepository<resource>(_context);
}
return _resources;
}
}
public IRepository<shift> shift
{
get
{
if (_shifts == null)
{
_shifts = new SQLRepository<shift>(_context);
}
return _shifts;
}
}
public IRepository<shift_person> shift_person
{
get
{
if (_shiftPersons == null)
{
_shiftPersons = new SQLRepository<shift_person>(_context);
}
return _shiftPersons;
}
}
public IRepository<Elmah_Error> Elmah_Error
{
get
{
if (_ElmahError == null)
{
_ElmahError = new SQLRepository<Elmah_Error>(_context);
}
return _ElmahError;
}
}
public IRepository<event_type> event_type
{
get
{
if (_eventTypes == null)
{
_eventTypes = new SQLRepository<event_type>(_context);
}
return _eventTypes;
}
}
public IRepository<status> status
{
get
{
if (_status == null)
{
_status = new SQLRepository<status>(_context);
}
return _status;
}
}
public void Commit()
{
_context.SaveChanges();
}
SQLRepository<utility_event> _utilityEvent = null;
SQLRepository<event_activity> _eventActivities = null;
SQLRepository<employee> _employees = null;
SQLRepository<activity_resource> _activityResources = null;
SQLRepository<Elmah_Error> _ElmahError = null;
SQLRepository<location> _locations = null;
SQLRepository<location_station> _locationStations = null;
SQLRepository<registration_type> _registrationTypes = null;
SQLRepository<resource> _resources = null;
SQLRepository<shift> _shifts = null;
SQLRepository<shift_person> _shiftPersons = null;
SQLRepository<event_type> _eventTypes = null;
SQLRepository<status> _status = null;
}
}
Controller Edit Implementation
public ActionResult Edit(int id, shift e)
{
if (!ModelState.IsValid)
{
return View(e);
//return to view
}
else
{
e.shift_begin = (DateTime)e.shift_date.Value.Add(e.shift_begin.Value.TimeOfDay);
e.shift_end = (DateTime)e.shift_date.Value.Add(e.shift_end.Value.TimeOfDay);
_unitOfWork.Commit();
return RedirectToAction("Details", "EventActivity", new { id = e.activity_id });
}
}
When you want to update entity in EF you must first Attach entity to ObjectContext instance or instance of related ObjectSet and use ObjectStateManager.ChangeObjectState to set state of entity to EntityState.Modified.
Other possibility is to load entity first from database and merge changes directly into this entity.
Check link in comment for examples or try to use search box. This question is one of the most frequent in EF tag.