xUnit with Moq. Test CRUD operation (generic dataservice) Entity Framework Core 6 - entity-framework-6

I am trying to test my project using ef 6. here is the situation: I have the mold class and have some generic services like GetAll,GetById,Create,Update, Delete. trying to test these methods using xUnit. Stuck in mocking.
public partial class TblPtsVMold :IMoldObject
{
public int MoldId { get; set; }
public string? MoldType { get; set; }
public string? Plant { get; set; }
}
public partial class PTSProdContext : DbContext
{
private IConfiguration _configuration { get; set; }
public PTSProdContext()
{
}
public PTSProdContext(DbContextOptions<PTSProdContext> options)
: base(options)
{
}
public virtual DbSet<TblPtsVMold> TblPtsVMolds { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer
(_configuration.GetConnectionString
(_configuration["EnvionmentString:ENVIRONMENT"]));
}
}
}
public class PTSContextFactory
{
public string DbConnectionString { get; set; }
public PTSContextFactory(string dbConnectionString)
{
DbConnectionString = dbConnectionString;
}
public PTSProdContext CreateDbContext()
{
DbContextOptionsBuilder<PTSProdContext> options = new DbContextOptionsBuilder<PTSProdContext>();
options.UseSqlServer(DbConnectionString);
return new PTSProdContext(options.Options);
}
public interface IDataService<T>
{
IEnumerable<T> GetAll();
IEnumerable<T> GetAllWithID(List<int> Ids);
T Get(int id);
T Create(T entity);
}
generic dataservice class
public class GenericDataService<T> : IDataService<T> where T : class, IDomainObject
{
public readonly PTSContextFactory _contextFactory;
public GenericDataService(PTSContextFactory contextFactory)
{
_contextFactory = contextFactory;
}
public GenericDataService()
{
}
public IEnumerable<T> GetAllWithID(List<int> Ids)
{
using (PTSProdContext context = _contextFactory.CreateDbContext())
{
IEnumerable<T> entities = context.Set<T>().Where(e => Ids.Contains(e.Id)).ToList();
if (entities == null)
{
WeakReferenceMessenger.Default.Send(new LookupExceptionMessage(string.Format("Can't find record.")));
return default;
}
return entities;
}
}
public IEnumerable<T> GetAll()
{
using (PTSProdContext context = _contextFactory.CreateDbContext())
{
IEnumerable<T> results = context.Set<T>().ToList();
if (results == null)
{
return default;
}
return results;
}
}
Test:
[Fact]
public void GetAll_TestClassObjectPassed_ProperMethodCalled()
{
// Arrange
var testObject = new TblPtsVMold() { Id = 1 };
var testList = new List<TblPtsVMold>() { testObject };
var data = new List<TblPtsVMold>
{
new TblPtsVMold { MoldType = "test",Plant="testpass1" },
new TblPtsVMold { MoldType = "ZZZ",Plant="testpass2" },
new TblPtsVMold { MoldType = "AAA",Plant="testpass3" },
}.AsQueryable();
var dbSetMock = new Mock<DbSet<TblPtsVMold>>();
dbSetMock.As<IQueryable<TblPtsVMold>>().Setup(x => x.Provider).Returns(testList.AsQueryable().Provider);
dbSetMock.As<IQueryable<TblPtsVMold>>().Setup(x => x.Expression).Returns(testList.AsQueryable().Expression);
dbSetMock.As<IQueryable<TblPtsVMold>>().Setup(x => x.ElementType).Returns(testList.AsQueryable().ElementType);
dbSetMock.As<IQueryable<TblPtsVMold>>().Setup(x => x.GetEnumerator()).Returns(testList.AsQueryab`your text`le().GetEnumerator());
var context = new Mock<PTSProdContext>();
context.Setup(x => x.Set<TblPtsVMold>()).Returns(dbSetMock.Object);
// Act
var context = new Mock<PTSContextFactory>();
context.Setup(x => x.DbConnectionString).Returns(string.Empty);
var service = new GenericDataService<TblPtsVMold>(context.Object);
var result = service.GetAll();
// Assert
Assert.Equal(testList, result.ToList());
}
context.Setup(x => x.DbConnectionString).Returns(string.Empty); is not correct.
getting error:pportedException : Unsupported expression: x => x.DbConnectionString Non-overridable members (here: PTSContextFactory.get_DbConnectionString) may not be used in setup verification expressions.
How to test GetAll or create method. Is anything wrong here?

Related

EF Core : how to Insert data releated tables

I'm new to Entity Framework Core and learning.
I have a question: I have three tables with many-to-many relations (Product, ProductCategory, Categories). Everything works fine, I can update, delete and add database.
But I couldn't add Product data to related tables. It just inserts into the Product table, but I want to add Id data to ProductCategories.
Thank you for helping me here.
This is my code:
Generic repository
public class EfCoreGenericRepository<TEntity, TContext> : IRepository<TEntity>
where TEntity : class
where TContext : DbContext, new()
{
public virtual void Create(TEntity entity)
{
using (var context = new TContext())
{
context.Set<TEntity>().Add(entity);
context.SaveChanges();
}
}
public void Delete(TEntity entity)
{
using (var context = new TContext())
{
context.Set<TEntity>().Remove(entity);
context.SaveChanges();
}
}
public List<TEntity> GetAll()
{
using (var context = new TContext())
{
return context.Set<TEntity>().ToList();
}
}
public TEntity GetById(int id)
{
using (var context = new TContext())
{
return context.Set<TEntity>().Find(id);
}
}
public virtual void Update(TEntity entity)
{
using (var context = new TContext())
{
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
}
}
}
ProductRepository
public class EfCoreProductRepository : EfCoreGenericRepository<Product, FoodContext>, IProductRepository
{
public Product GetByIdWithCategories(int id)
{
using (var context = new FoodContext())
{
return context.Products
.Where(p => p.ProductId == id)
.Include(p => p.ProductCategories)
.ThenInclude(pc => pc.Category)
.FirstOrDefault();
}
}
public List<Product> GetProductsByCategory(string name)
{
using (var context = new FoodContext())
{
var products = context.Products.AsQueryable();
if (!string.IsNullOrEmpty(name))
{
products = products.Include(i => i.ProductCategories)
.ThenInclude(i => i.Category)
.Where(i => i.ProductCategories.Any(a => a.Category.Name.ToLower() == name.ToLower()));
}
return products.ToList();
}
}
public void Update(Product entity, int[] categoryIds)
{
using (var context = new FoodContext())
{
var product = context.Products
.Include(i => i.ProductCategories)
.FirstOrDefault(i => i.ProductId == entity.ProductId);
if (product != null)
{
product.Name = entity.Name;
product.Price = entity.Price;
product.ImageUrl = entity.ImageUrl;
product.ProductCategories = categoryIds.Select(catid => new ProductCategory() {
ProductId = entity.ProductId,
CategoryId = catid
}).ToList();
}
context.SaveChanges();
}
}
// I may override Create code here.
}
MVC Post method
[HttpPost]
public async Task<IActionResult> CreateProduct(ProductModel model, IFormFile file)
{
if (ModelState.IsValid)
{
if (file != null)
{
var extension = Path.GetExtension(file.FileName);
var randomName = string.Format($"{DateTime.Now.Ticks}{extension}");
model.ImageUrl = randomName;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\img\\Products", randomName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
}
var entity = new Product()
{
Name = model.Name,
Price = model.Price,
ImageUrl = model.ImageUrl,
CategoryId = model.CategoryId
};
if (_productService.Create(entity))
{
TempData.Put("message", new AlertMessage
{
Title = $"{entity.Name} named product added successfully",
Message = $"{entity.Name} named product added successfully",
AlertType = "alert-success"
});
return RedirectToAction("ProductList");
};
TempData.Put("message", new AlertMessage
{
Title = _productService.ErrorMessage,
Message = _productService.ErrorMessage,
});
}
ViewBag.Categories = _categoryService.GetAll();
return View(model);
}
You can ask for more code if you need to see it. Thanks for now!
Edit ---> Entity Classes
Category.cs
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string ImageUrl { get; set; }
public List<ProductCategory> ProductCategories{get; set;}
}
Product.cs
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public double? Price { get; set; }
public bool IsApproved { get; set; }
public int? CategoryId { get; set; }
public string ImageUrl { get; set; }
public List<ProductCategory> ProductCategories{get; set;}
}
ProductCategories.cs
public class ProductCategory
{
public int CategoryId { get; set; }
public Category Category { get; set; }
public int ProductId { get; set; }
public Product Product{get; set;}
}
Thanks to Pirate. I solved problem. I'm new in efcore but i was aware of many to many relationship unnecessary. I had done for many products with many categories but I changed my mind later. Maybe I can use it for next codes.
public override void Create(Product entity)
{
base.Create(entity);
using (var context = new FoodContext())
{
var product = context.Products
.Where(i=>i.ProductId==entity.ProductId)
.Include(i=>i.ProductCategories).FirstOrDefault();
product.ProductCategories.Add(new ProductCategory(){
ProductId=entity.ProductId,
CategoryId=(int)entity.CategoryId
});
context.SaveChanges();
}
}
Note: I don't know how true using double using-code-block

You can not define the relationship between two objects because they are associated with different ObjectContext objects

I am getting this rare exception when I am trying to update an entity. I am working with ASP.NET MVC 5, EF 6.1, Identity and the patterns repository and unit of work.
I followed this cool tutorial from Tim.
I have all the work done in 4 separate projects (UI, Entities, Domain and DataAccess), I included Tim solution to use Users and roles.
My Entities:
public class Turno : IEntity
{
public Turno()
{
//Medico = new Medico();
//Consultorio = new Consultorio();
}
public Int64 Id { get; set; }
[Required]
public DateTime Fecha { get; set; }
[Required]
[StringLength(10)]
public string Hora { get; set; }
[Required]
public Medico Medico { get; set; }
[Required]
public Consultorio Consultorio { get; set; }
[Required]
public EstadoTurno Estado { get; set; }
public virtual Paciente Paciente { get; set; }
}
namespace TurnosMedicos.Entities
{
[Table("Medicos")]
public class Medico: Persona
{
public Medico()
{
Especialidades = new List<Especialidad>();
TurnosDisponibles = new List<Turno>();
Consultorios = new List<Consultorio>();
Telefonos = new List<Telefono>();
PlanesAtendidos = new List<PrepagaPlan>();
}
[Required]
[StringLength(10)]
public string Matricula { get; set; }
[Required]
public decimal PrecioTurno { get; set; }
public virtual List<Especialidad> Especialidades { get; set; }
public virtual List<Turno> TurnosDisponibles { get; set; }
public virtual List<Consultorio> Consultorios { get; set; }
public List<PrepagaPlan> PlanesAtendidos { get; set; }
public override string ToString()
{
return Apellido + ", " + Nombres;
}
}
}
namespace TurnosMedicos.Entities
{
public class Paciente: Persona
{
public Paciente()
{
HistoriaClinica = new List<Consulta>();
Turnos = new List<Turno>();
Telefonos = new List<Telefono>();
PlanesSalud = new List<PrepagaPlan>();
TurnosAusentes = new List<TurnoInformadoAusente>();
}
public virtual List<TurnoInformadoAusente> TurnosAusentes { get; set; }
public virtual List<Consulta> HistoriaClinica { get; set; }
public virtual List<Turno> Turnos { get; set; }
public override string ToString()
{
return Apellido + ", " + Nombres;
}
public List<PrepagaPlan> PlanesSalud { get; set; }
public PrepagaPlan PlanPredeterminado()
{
if(PlanesSalud.Count()>0)
{
return PlanesSalud[0];
}
return null;
}
public string TelefonosRapido()
{
System.Text.StringBuilder tel = new System.Text.StringBuilder();
foreach(var t in this.Telefonos)
{
tel.Append(t.Numero + " (" + t.Tipo + ")");
tel.AppendLine();
}
return tel.ToString();
}
/// <summary>
/// Porcentaje de Asistencia
/// </summary>
[NotMapped]
public decimal Ranking
{
get{
if (TurnosAusentes.Count == 0)
{
return 100;
}
else{
return (100 - (Decimal.Divide(TurnosAusentes.Count, Turnos.Count) * 100));
}
}
}
}
}
My Repositories:
public class MedicosRepository: Repository<Medico> //, IMedicoRepository
{
internal MedicosRepository(ApplicationDbContext context)
: base(context)
{
}
public IQueryable<Medico> Find(System.Linq.Expressions.Expression<Func<Medico, bool>> predicate)
{
return Set.Where(predicate);
}
public override List<Medico> GetAll()
{
return Set.Include("Telefonos")
.Include("PlanesAtendidos")
.Include("Consultorios")
.Include("Consultorios.Telefonos")
.Include("TurnosDisponibles.Paciente.Telefonos")
.ToList();
}
public IQueryable<Medico> FindAll()
{
return Set.Include("Telefonos")
.Include("PlanesAtendidos")
.Include("Consultorios")
.Include("Consultorios.Telefonos")
.Include("TurnosDisponibles.Paciente.Telefonos");
}
public override Medico FindById(object id)
{
Int64 Id = Int64.Parse(id.ToString());
return Set.Include("Telefonos")
.Include("Consultorios")
.Include("Consultorios.Telefonos")
.Include("PlanesAtendidos")
.Include("TurnosDisponibles.Paciente.Telefonos")
.Single(o => o.Id == Id);
}
}
TurnosRepository:
namespace TurnosMedicos.DataAccess.Repositories
{
internal class TurnosRepository: Repository<Turno>
{
public TurnosRepository(ApplicationDbContext context): base(context)
{
}
public override List<Turno> GetAll()
{
return Set.Include("Medico")
.Include("Paciente")
.Include("Consultorio").ToList();
}
public override Turno FindById(object id)
{
Int64 Id = Int64.Parse(id.ToString());
return Set.Include("Medico")
.Include("Paciente")
.Include("Consultorio")
.Single(o => o.Id == Id);
}
}
}
User repository:
namespace TurnosMedicos.DataAccess.Repositories
{
internal class UserRepository : Repository<User>, IUserRepository
{
internal UserRepository(ApplicationDbContext context)
: base(context)
{
}
public User FindByUserName(string userName)
{
return Set.Include("Medico")
.Include("Paciente")
.FirstOrDefault(x => x.UserName == userName);
}
public Task<User> FindByUserNameAsync(string userName)
{
return Set.FirstOrDefaultAsync(x => x.UserName == userName);
}
public Task<User> FindByEmailAsync(System.Threading.CancellationToken cancellationToken, string email)
{
return Set.FirstOrDefaultAsync(x => x.Email == email, cancellationToken);
}
public User FindByEmail(string email)
{
return Set.Include("Medico")
.Include("Medico.Telefonos")
.Include("Medico.Especialidades")
.Include("Medico.TurnosDisponibles")
.Include("Medico.Consultorios")
.Include("Medico.PlanesAtendidos")
.Include("Paciente")
.Include("Paciente.Turnos")
.Include("Paciente.PlanesSalud")
.Include("Paciente.HistoriaClinica")
.Include("Paciente.TurnosAusentes")
.Include("Paciente.Telefonos")
.Include("Roles")
.FirstOrDefault(u => u.Email == email);
}
}
}
My Context:
namespace TurnosMedicos.DataAccess
{
internal class ApplicationDbContext : DbContext
{
internal ApplicationDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public ApplicationDbContext(): base("TurnosMedicosCn")
{
}
public IDbSet<User> Users { get; set; }
public IDbSet<Role> Roles { get; set; }
public IDbSet<ExternalLogin> Logins { get; set; }
public DbSet<Paciente> Pacientes { get; set; }
public DbSet<Medico> Medicos { get; set; }
public DbSet<Turno> Turnos { get; set; }
public DbSet<Consulta> Consultas { get; set; }
public DbSet<Consultorio> Consultorios { get; set; }
public DbSet<Especialidad> Especialidades { get; set; }
public DbSet<Prepaga> Prepagas { get; set; }
public DbSet<PrepagaPlan> Planes { get; set; }
public DbSet<Registro> RegistrosFacturacion { get; set; }
public DbSet<Empresa> Empresas { get; set; }
public DbSet<Recomendado> Recomendados { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new UserConfiguration());
modelBuilder.Configurations.Add(new RoleConfiguration());
modelBuilder.Configurations.Add(new ExternalLoginConfiguration());
modelBuilder.Configurations.Add(new ClaimConfiguration());
}
}
}
My Unit Of Work
namespace TurnosMedicos.DataAccess
{
public class SQLUnitOfWork: IUnitOfWork
{
private readonly ApplicationDbContext _context;
PacientesRepository _pacientes = null;
//MedicosRepository _medicos = null;
IRepository<Medico> _medicos = null;
TurnosRepository _turnos = null;
EspecialidadRepository _especialidades = null;
PrepagasRepository _prepagas = null;
PrepagaPlanesRepository _planes = null;
RegistrosFacturacionRepository _registroFacturacion = null;
RecomendadosRepository _recomendados = null;
private IExternalLoginRepository _externalLoginRepository;
private IRoleRepository _roleRepository;
private IUserRepository _userRepository;
public SQLUnitOfWork(string nameOrConnectionString)
{
_context = new ApplicationDbContext(nameOrConnectionString);
}
public IRepository<Turno> Turnos
{
get { return _turnos ?? (_turnos = new TurnosRepository(_context)); }
}
public IRepository<Paciente> Pacientes
{
get
{
if(_pacientes==null)
{
_pacientes = new PacientesRepository(_context);
}
return _pacientes;
}
}
public IRepository<Medico> Medicos
{
get { return _medicos ?? (_medicos = new MedicosRepository(_context)); }
}
public IRepository<Especialidad> Especialidades
{
get
{
if (_especialidades == null)
{
_especialidades = new EspecialidadRepository(_context);
}
return _especialidades;
}
set
{
throw new NotImplementedException();
}
}
public IRepository<Prepaga> Prepagas {
get {
if (_prepagas == null)
{
_prepagas = new PrepagasRepository(_context);
}
return _prepagas;
}
set { throw new NotImplementedException(); }
}
public IRepository<PrepagaPlan> Planes {
get
{
if (_planes == null)
{
_planes = new PrepagaPlanesRepository(_context);
}
return _planes;
}
}
public IRepository<Registro> RegistrosFacturacion
{
get
{
if(_registroFacturacion == null)
{
_registroFacturacion = new RegistrosFacturacionRepository(_context);
}
return _registroFacturacion;
}
}
public IRepository<Recomendado> Recomendados
{
get
{
if (_recomendados == null)
{
_recomendados = new RecomendadosRepository(_context);
}
return _recomendados;
}
}
public IExternalLoginRepository ExternalLoginRepository
{
get { return _externalLoginRepository ?? (_externalLoginRepository = new ExternalLoginRepository(_context)); }
}
public IRoleRepository RoleRepository
{
get { return _roleRepository ?? (_roleRepository = new RoleRepository(_context)); }
}
public IUserRepository UserRepository
{
get { return _userRepository ?? (_userRepository = new UserRepository(_context)); }
}
public int SaveChanges()
{
try
{
return _context.SaveChanges();
}
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)
{
Exception raise = dbEx;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
string message = string.Format("{0}:{1}",
validationErrors.Entry.Entity.ToString(),
validationError.ErrorMessage);
raise = new InvalidOperationException(message, raise);
}
}
throw raise;
}
}
public Task<int> SaveChangesAsync()
{
try
{
return _context.SaveChangesAsync();
}
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)
{
Exception raise = dbEx;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
string message = string.Format("{0}:{1}",
validationErrors.Entry.Entity.ToString(),
validationError.ErrorMessage);
raise = new InvalidOperationException(message, raise);
}
}
throw raise;
}
}
public Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken)
{
return _context.SaveChangesAsync(cancellationToken);
}
public void Dispose()
{
_externalLoginRepository = null;
_roleRepository = null;
_userRepository = null;
_turnos = null;
_pacientes = null;
_especialidades = null;
_planes = null;
_prepagas = null;
_recomendados = null;
_registroFacturacion = null;
}
}
}
And Finally, the Domain class where I am trying to get things done
IUnitOfWork unitofwork = null;
IRepository<Paciente> repositoryPacientes = null;
//IMedicoRepository repositoryMedicos = null;
IRepository<Medico> repositoryMedicos = null;
IRepository<Turno> repositoryTurnos = null;
IRepository<Especialidad> repositoryEspecialidad = null;
IRepository<Registro> repositoryRegistroFacturacion = null;
IRepository<Recomendado> repositoryRecomendados = null;
public TurnosManager(IUnitOfWork _unitOfWork)
{
unitofwork = _unitOfWork;
repositoryPacientes = unitofwork.Pacientes;
repositoryMedicos = unitofwork.Medicos;
repositoryTurnos = unitofwork.Turnos;
repositoryEspecialidad = unitofwork.Especialidades;
repositoryRegistroFacturacion = unitofwork.RegistrosFacturacion;
repositoryRecomendados = unitofwork.Recomendados;
}
private bool AsignarTurno(Paciente p, Turno t)
{
if(t.Fecha.Date < DateTime.Now.Date)
{
throw new TurnoInvalidoException("No se puede seleccionar un turno para una fecha en el pasado.");
}
// Ver tema de la hora para la fecha actual
t.Estado = EstadoTurno.Reservado;
t.Paciente = p;
p.Turnos.Add(t);
//repositoryTurnos.Update(t);
unitofwork.SaveChanges();
string planMedico = "Privado";
if (p.PlanesSalud.Count > 0)
{
planMedico = p.PlanesSalud[0].NombrePlan;
}
RegisstrarParaFacturacion(t, planMedico);
ReservaVM obj = new ReservaVM();
obj.Paciente = p;
obj.MedicoSeleccionado = t.Medico;
obj.TurnoSeleccionado = t;
EnviarEmail(obj);
return true;
}
The problem occurs in line unitofwork.SaveChanges();
I get the exception: "You can not define the relationship between two objects because they are associated with different ObjectContext objects" but I only have "ApplicationDbContext" in my code.
The problem is that you have one type which is ApplicationDbContext, but there are two instances of that context. One instantiated to fetch Pacient p and another one for Turno t.
Where is the AsignarTurno(Paciente p, Turno t) called from?
If this is code first you should add
public int PacienteId { get; set; }
to class Turno.
If your classes are well define you should only need to persist the relation on one side, so in the AsignarTurno function instead of:
t.Paciente = p;
p.Turnos.Add(t);
you should only have
t.PacienteId = p.Id;
This would do what you need, assuming that p and t both exist the database.

JSONIgnore data annotations on class

I have a public property which is an object that contains numerous properties itself. Using ASP.net MVC, when I serialize the JSON data I simply add the [JsonIgnore] attribute wherever I use the object so it doesn't display the contents.
Is there a way to add the [JsonIgnore] attribute to the class so it never is serialized?
//[JsonIgnore] ??
public class DataObj
{
public string ConnectionName { get; set; }
public string Query { get; set; }
...
}
public class Customer
{
public string First { get; set; }
public string Last { get; set; }
[JsonIgnore]
public DataObj Foo { get; set; }
}
public class ShipAddress
{
public string Street { get; set; }
public string City { get; set; }
public string Country { get; set; }
[JsonIgnore]
public DataObj Foo { get; set; }
}
My solution after receiving the code provided by jvanrhyn.
Also, here is a link that explains more.
public class DataObjFilterContractResolver : DefaultContractResolver
{
public static readonly DataObjFilterContractResolver Instance = new DataObjFilterContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member,MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType.Name.StartsWith("DataObj") || property.PropertyName == "DataObj")
{
property.ShouldSerialize = instance => false;
}
return property;
}
}
public class UtcJsonResult : JsonResult
{
public UtcJsonResult(object data)
{
Data = data;
JsonRequestBehavior = JsonRequestBehavior.AllowGet;
}
private const string DateFormat = #"yyyy-MM-dd HH:mm:ssZ";
public override void ExecuteResult(ControllerContext context)
{
if (context == null) throw new ArgumentNullException("context");
if (Data == null) return;
var response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
if (ContentEncoding != null) response.ContentEncoding = ContentEncoding;
var isoConvert = new IsoDateTimeConverter {DateTimeFormat = DateFormat};
JsonConvert.DefaultSettings =
() => new JsonSerializerSettings
{ ContractResolver = new DataObjFilterContractResolver()}; //<--- Used here
var json = JsonConvert.SerializeObject(Data, isoConvert);
response.Write(json);
}
}
You can add a Contract Resolver in your project.
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public new static readonly ShouldSerializeContractResolver Instance =
new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member,
MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(DataObj))
{
property.ShouldSerialize =
instance =>
{
return false;
};
}
return property;
}
}

Test Controller with unitofwork in asp.net mvc

I have a Model :
public class Post:
{
public string Description { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public Guid UniqId { get; set; }
public DateTime CreatedDate { get; set; }
public byte[] RowVersion { get; set; }
}
and i have a DataLayer that has EfContext and Iunitofwok pattern
using System.Data.Entity;
namespace Data
{
public interface IUnitOfWork
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveChanges();
}
}
using System.Data.Entity;
using DomainModel;
namespace Data
{
public class EfContext : DbContext, IUnitOfWork
{
public DbSet<Post> Post { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Category> Categories { get; set; }
public EfContext()
: base("TavanGruop")
{
}
#region IUnitOfWork Members
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
#endregion
}
}
in another project i have repository
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using Data;
namespace ServiceLayer.ServiceDbSet
{
public class ServiceContext<T> : IServiceDbSet<T> where T : class
{
private readonly IDbSet<T> _dbSet;
public ServiceContext(IUnitOfWork iUnitOfWork)
{
_dbSet = iUnitOfWork.Set<T>();
}
#region IContext<T> Members
public bool TryGet(Func<T, bool> predicate, out T entity)
{
entity = List(predicate).SingleOrDefault();
return entity != null;
}
public T Get(Func<T, bool> predicate)
{
return List(predicate).Single();
}
public virtual List<T> List(Func<T, bool> predicate = null)
{
IEnumerable<T> result = _dbSet.AsEnumerable();
if (predicate != null)
result = result.Where(predicate);
return result.ToList();
}
public T Add(T t)
{
_dbSet.Add(t);
return t;
}
public void Delete(Func<T, bool> predicate)
{
List(predicate).ToList().ForEach(p => _dbSet.Remove(p));
}
public void Delete(T t)
{
_dbSet.Remove(t);
}
#endregion
}
}
and service
using Data;
using DomainModel;
using ServiceLayer.ServiceDbSet;
namespace ServiceLayer.EfServices
{
public class EfPostService : ServiceContext<Post>
{
public EfPostService(IUnitOfWork uow)
: base(uow)
{
}
}
}
and this my services
using Data;
using ServiceLayer.EfServices;
namespace ServiceLayer
{
public class Services
{
private readonly IUnitOfWork _unitOfWork;
private EfPostService _post;
private EfCommentService _comment;
public Services(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public EfPostService Post
{
get { return _post ?? new EfPostService(_unitOfWork); }
set { _post = value; }
}
public EfCommentService Comment
{
get { return _comment ?? new EfCommentService(_unitOfWork); }
set { _comment = value; }
}
}
}
I use tructuremap for dependency injection
using System;
using System.Web.Mvc;
using System.Web.Routing;
using Data;
using StructureMap;
namespace MVCTemplateProject
{
public static class StructuremapMvc
{
public static void InitStructureMap()
{
ObjectFactory.Initialize(x => x.For<IUnitOfWork>().HttpContextScoped().Use(() => new EfContext()));
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
}
}
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return ObjectFactory.GetInstance(controllerType) as Controller;
}
}
}
in base controller i get Iunitofwork
using System.Web.Mvc;
using Data;
using ServiceLayer;
namespace MVCTemplateProject.Controllers
{
public class BaseController : Controller
{
public readonly IUnitOfWork Context;
public readonly Services DataContext;
public BaseController(Data.IUnitOfWork context)
{
Context = context;
DataContext = new Services(context);
}
}
}
and this my home controller
using System.Web.Mvc;
using Data;
using DomainModel;
namespace MVCTemplateProject.Controllers
{
public class HomeController : BaseController
{
public HomeController(IUnitOfWork context)
: base(context)
{
}
public ActionResult Index()
{
return View(DataContext.Post.List());
}
}
}
how can i test index action with moq
i write this test but i get error
public void IndxShouldReturnListOfPage()
{
var posts = new List<Post>
{
new Post {Name = "Test"},
new Post {Name = "Test"},
new Post {Name = "Test"},
new Post {Name = "Test"}
};
var efContext = new Mock<EfContext>();
var mockRepository = new Mock<ServiceLayer.Services>(efContext);
mockRepository.Setup(x => x.Post.List(It.IsAny<Func<Post, bool>>())).Returns(posts);
var controller = new HomeController(efContext.Object);
List<Post> model = controller.DataContext.Post.List();
var result = controller.Index() as ViewResult;
Assert.AreEqual(model.Count, 4);
Assert.AreEqual(string.Empty, result.ViewName);
}
i get it and i test the project
[TestFixture]
public class PostControllerTest
{
#region Setup/Teardown
[SetUp]
public void Setup()
{
UnitOfWork = TestHelper.TestHelper.GetMockUnitOfWork();
PostDbSet = TestHelper.TestHelper.GetMockPostDbSet();
Posts = TestHelper.TestHelper.GetFakePosts();
TagDbSet = TestHelper.TestHelper.GetMockTagDbSet();
CategoryDbSet = TestHelper.TestHelper.GetMockCategoryDbSet();
PostController = new PostController(UnitOfWork.Object, PostDbSet.Object, CategoryDbSet.Object, TagDbSet.Object);
}
#endregion
private Mock<IServiceDbSet<Post>> PostDbSet { get; set; }
private Mock<IServiceDbSet<Tag>> TagDbSet { get; set; }
private Mock<IServiceDbSet<Category>> CategoryDbSet { get; set; }
private PostController PostController { get; set; }
private List<Post> Posts { get; set; }
private Mock<IUnitOfWork> UnitOfWork { get; set; }
[Test]
public void DetailShouldRedirectToHomePageWhenNotFoundPostOrPostIdIs0()
{
PostDbSet.Setup(x => x.Get(It.IsAny<Func<Post, bool>>())).Returns(Posts.Find(post => post.Id == 100));
var redirectToRoutResult =
(RedirectToRouteResult) PostController.Details(100);
Assert.AreEqual("Index", redirectToRoutResult.RouteValues["action"]);
Assert.AreEqual("Home", redirectToRoutResult.RouteValues["Controller"]);
}
[Test]
public void DetailShouldShowPostById()
{
PostDbSet.Setup(x => x.Get(It.IsAny<Func<Post, bool>>())).Returns(Posts.First());
var result = PostController.Details(1) as ViewResult;
var model = result.Model as PostViewModel;
Assert.AreNotEqual(null, model);
Assert.AreEqual("I Can", model.Title);
Assert.AreEqual(string.Empty, result.ViewName);
}
[Test]
public void ListOfStringTagMustConvertToListOfTagCollection()
{
PostViewModel postViewModel = TestHelper.TestHelper.GetFakePostViewModel();
PostController.Create(postViewModel);
//Assert.AreEqual(postViewModel.RawTags[0], postViewModel.RawTags.First().Name);
}

Container Resolve based on criteria

I'm trying to implement the Strategy pattern while using Windsor container. Here is what I have:
public class OrderProcessor {
...
public OrderProcessor(ITaxStrategy strategy) {}
public void Process(Order order)
{
order.Tax = strategy.CalcTax(order);
}
}
The problem is, how do I configure my container (other container examples welcome) to have, essentially, criteria for choosing the appropriate dependency. So if I register the following
public class USTaxStrategy : ITaxStrategy { ... }
public class CanadaTaxStrateg : ITaxStrategy { ... }
how do I use the Order.Destination (address) as the criteria for the injected dependency?
Here are a few options, pick the one you like best. I usually use the first one, it's the simplest.
[TestFixture]
public class TaxStrategyTests {
[Test]
public void InjectWithFactory() {
var container = new WindsorContainer();
container.AddComponent<USTaxStrategy>();
container.AddComponent<CanadaTaxStrategy>();
container.AddComponent<OrderProcessor>();
container.AddComponent<ITaxStrategyFactory, TaxStrategyFactory>();
var order = new Order {Country = "US"};
container.Resolve<OrderProcessor>().Process(order);
Assert.AreEqual(10, order.Tax);
}
[Test]
public void InjectWithFactoryFromDictionary() {
var container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.AddComponent<USTaxStrategy>();
container.AddComponent<CanadaTaxStrategy>();
container.AddComponent<OrderProcessor>();
container.Register(Component.For<ITaxStrategyFactory>()
.UsingFactoryMethod(kernel => new TaxStrategyFactory2(new Dictionary<string, ITaxStrategy> {
{"US", kernel.Resolve<USTaxStrategy>()},
{"CA", kernel.Resolve<CanadaTaxStrategy>()},
})));
var order = new Order { Country = "US" };
container.Resolve<OrderProcessor>().Process(order);
Assert.AreEqual(10, order.Tax);
}
[Test]
public void InjectWithProxy() {
var container = new WindsorContainer();
container.AddComponent<USTaxStrategy>();
container.AddComponent<CanadaTaxStrategy>();
container.AddComponent<OrderProcessorInterceptor>();
container.AddComponent<ITaxStrategyFactory, TaxStrategyFactory>();
container.Register(Component.For<OrderProcessor2>()
.LifeStyle.Transient
.Interceptors(InterceptorReference.ForType<OrderProcessorInterceptor>()).First);
var order = new Order {Country = "CA"};
container.Resolve<OrderProcessor2>().Process(order);
Assert.AreEqual(5, order.Tax);
}
public class OrderProcessorInterceptor : IInterceptor {
private readonly ITaxStrategyFactory strategyFactory;
public OrderProcessorInterceptor(ITaxStrategyFactory strategyFactory) {
this.strategyFactory = strategyFactory;
}
public void Intercept(IInvocation invocation) {
if (invocation.MethodInvocationTarget.Name == "Process") {
var processor = (OrderProcessor2) invocation.InvocationTarget;
var order = (Order) invocation.Arguments[0];
processor.Strategy = strategyFactory.Create(order);
}
invocation.Proceed();
}
}
public interface IOrderProcessor {
void Process(Order order);
}
public class OrderProcessor2 : IOrderProcessor {
public ITaxStrategy Strategy { get; set; }
public virtual void Process(Order order) {
order.Tax = Strategy.CalcTax(order);
}
}
public class OrderProcessor : IOrderProcessor {
private readonly ITaxStrategyFactory strategyFactory;
public OrderProcessor(ITaxStrategyFactory strategyFactory) {
this.strategyFactory = strategyFactory;
}
public void Process(Order order) {
var strategy = strategyFactory.Create(order);
order.Tax = strategy.CalcTax(order);
}
}
public interface ITaxStrategyFactory {
ITaxStrategy Create(Order o);
}
public class TaxStrategyFactory : ITaxStrategyFactory {
private readonly IKernel kernel;
public TaxStrategyFactory(IKernel kernel) {
this.kernel = kernel;
}
public ITaxStrategy Create(Order o) {
if (o.Country == "US")
return kernel.Resolve<USTaxStrategy>();
return kernel.Resolve<CanadaTaxStrategy>();
}
}
public class TaxStrategyFactory2: ITaxStrategyFactory {
private readonly IDictionary<string, ITaxStrategy> strategies;
public TaxStrategyFactory2(IDictionary<string, ITaxStrategy> strategies) {
this.strategies = strategies;
}
public ITaxStrategy Create(Order o) {
return strategies[o.Country];
}
}
public interface ITaxStrategy {
decimal CalcTax(Order order);
}
public class USTaxStrategy : ITaxStrategy {
public decimal CalcTax(Order order) {
return 10;
}
}
public class CanadaTaxStrategy : ITaxStrategy {
public decimal CalcTax(Order order) {
return 5;
}
}
public class Order {
public string Country { get; set; }
public decimal Tax { get; set; }
}
}

Resources