How to map and test a "Many to Many Relationship" in NHibernate using Fluent NHibernate - unit-testing

When I test my many to many classes an error occurs:
System.ApplicationException: Actual
count does not equal expected count.
Entities:
public interface IEntity
{
int Id { get; set; }
}
public abstract class Entity : IEntity
{
public virtual int Id { get; set; }
public virtual bool IsPersistent
{
get { return isPersistentObject(); }
}
public override bool Equals(object obj)
{
if (isPersistentObject())
{
var persistentObject = obj as Entity;
return (persistentObject != null) && (Id == persistentObject.Id);
}
return base.Equals(obj);
}
public override int GetHashCode()
{
return isPersistentObject() ? Id.GetHashCode() : base.GetHashCode();
}
private bool isPersistentObject()
{
return (Id != 0);
}
}
public class Team : Entity
{
public virtual string Name { get; set; }
public virtual ISet<Employee> Employees { get; set; }
public Team()
{
Employees = new HashedSet<Employee>();
}
}
public class Employee : Entity
{
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual ISet<Team> Teams { get; set; }
public virtual string EMail { get; set; }
public Employee()
{
Teams = new HashedSet<Team>();
}
}
Mappings:
public class TeamMap : ClassMap<Team>
{
public TeamMap()
{
// identity mapping
Id(p => p.Id).Column("TeamID");
// column mapping
Map(p => p.Name);
// relationship mapping
HasManyToMany(m => m.Employees)
.Table("EmployeeTeam")
.LazyLoad()
.Cascade.SaveUpdate()
.AsSet()
.ParentKeyColumn("TeamID")
.ChildKeyColumn("EmployeeID");
}
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
// identifier mapping
Id(p => p.Id).Column("EmployeeID");
// column mapping
Map(p => p.EMail);
Map(p => p.LastName);
Map(p => p.FirstName);
// relationship mapping
HasManyToMany(m => m.Teams).Table("EmployeeTeam")
.Inverse()
.Cascade.SaveUpdate()
.AsSet()
.LazyLoad()
.ParentKeyColumn("EmployeeID")
.ChildKeyColumn("TeamID");
}
}
Test:
[TestMethod]
public void CanCorrectlyMapEmployee()
{
var team = new List<Team> {new Team() {Name = "Team1"}};
new PersistenceSpecification<Employee>(_session)
.CheckProperty(p => p.EMail, "Mail")
.CheckProperty(p => p.FirstName, "Firstname")
.CheckProperty(p => p.Id, 1)
.CheckProperty(p => p.LastName, "Lastname")
.CheckList(p => p.Teams,team )
.VerifyTheMappings();
}
Whether I add an Employee or a Team my EmployeeTeam table is always empty.
I have tested it against SQLLite with FNH and manually against SQL Server 2008.
Does anybody of you have an idea to fix this?
edit:
I was amazed to find out that when I create an Employee and add 2 Teams to the Employee and load the created Employee he has 2 Teams. So it works fine. But when I look in my relationsip table EmployeeTeam then everything is empty. Can someone explain me why?
And does anybody know how I can use Fluent NHibernate to test my many to many relationship?
Thanks in advance!

Employee map has inverse attribute. As you probably know, this means that when you save employee, relationship table (EmployeeTeam) will not be updated. To add/remove new relationship information you have to add employee to the Team and save Team.
So, in your case - don't test many to many on the side of employee, test it on the side of team. (If you'd like NHibernate to add records when you add team to employee, you'll have to invert "Inverse" attributes - give it to team, not employee, but then - same story with Team entity).
Why were you able to load Employee with teams? Because of session-level cache. You've probably saved and loaded Employee in the same ISession - this means that NHibernate returned you exactly reference to the same object, without loading it from the db. Try saving & loading in two different sessions and you'll see no team in Employee.Teams set.
Side note: It is considered a good practice to create methods that will enforce consistency between many-to-many relationships, that is - when you add Team to Employee, Employee is added to the team, sth. like this:
class Employee
{
// ...
public void AddTeam(Team team)
{
// check for null, etc.
Teams.Add(team);
team.Employees.Add(this);
}
}
and very simillar method in the Team class.

Related

How to retrieve list from DB to front-end with AutoMapper

After changing the mapping to Automapper, only an empty list is sent through the endpoint.
Initially I had an endpoint that retrieved all employees with info including a list with every course each employee had taken. This was with manual mapping between entities & Dto.
//From startup.cs in Configure
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Employee, Models.EmployeeCoursesDto>();
cfg.CreateMap<Employee, Models.EmployeeDto>();
cfg.CreateMap<EmployeeCourses, Models.EmployeeCoursesDto>();
});
//From Employee entity
public class Employee
{
[Key]
//Gen new Id key in DB when object created
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(50)]
public string Title { get; set; }
public ICollection<EmployeeCourses> EmployeeCourses { get; set; }
= new List<EmployeeCourses>();
}
}
//From employee Dto
public class EmployeeDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public ICollection<EmployeeCoursesDto> EmployeeCourses { get; set; }
= new List<EmployeeCoursesDto>();
}
}
//Endpoint in controller
[HttpGet()]
public IActionResult GetAllEmployees()
{
var employeeEntities = _employeeInfoRepository.GetEmployees();
var results = Mapper.Map<IEnumerable<EmployeeDto>>(employeeEntities);
return Ok(results);
}
//From Irepository
IEnumerable<Employee> GetEmployees();
//From repository
public IEnumerable<Employee> GetEmployees()
{
return _context.Employees.OrderBy(c => c.Name).ToList();
}
I expected output all employees with all datafileds, including their list of courses.
The output is all fields with data, except the list of courses which is "0" when running with a breakpoint, and in Postman it shows as only:
"id": 2,
"name": "Test Person",
"title": "Bus Driver",
"numberOfCourses": 0,
"employeeCourses": [],
"totalAchievedHoursAuditor": 0,
"totalAchievedHoursAccountant": 51,
"courseBalanceAccountant": null,
"courseBalanceAuditor": null
However, if I try another endpoint only for retrieving a specific course, or a list of courses, the data show correctly. Seems there are an issue with mapping the employees & courses at the same time?
I found the error, not Automapper, but my Linq statement:
return _context.Employees.Include(c => c.EmployeeCourses).ToList();
Please close this thread. Thanks for the reply Lucian Bargaoanu & have a great weekend.

I want no Parent entity or foreign key on the Grandchild entity

I have three entities, BaseEntity, Child and GrandChild, that are predictably linked together.
It goes like this:
public class BaseEntity
{
public string CompanyId { get; set; }
public string BaseEntityId { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
public string CompanyId { get; set; }
public string BaseEntityId { get; set; }
public string ChildId { get; set; }
public virtual BaseEntity Parent { get; set; }
public virtual List<GrandChild> GrandChildren { get; set; }
}
public class GrandChild
{
public string CompanyId { get; set; }
public string BaseEntityId { get; set; }
public string ChildId { get; set; }
public string GrandChildId { get; set; }
public virtual Child ParentChild { get; set; }
}
public class BaseContext : DbContext
{
protected override void OnModelCreating(ModelBuilder p_mbuModel)
{
p_mbuModel.Entity<BaseEntity>().ToTable("T_BaseEntity");
p_mbuModel.Entity<BaseEntity>().HasKey(t => new { t.CompanyId, t.BaseEntityId });
p_mbuModel.Entity<Child>().ToTable("T_Child");
p_mbuModel.Entity<Child>().HasKey(t => new { t.CompanyId, t.BaseEntityId, t.ChildId });
p_mbuModel.Entity<Child>().HasOne(c => c.Parent).
WithMany(p => p.Children).
HasForeignKey(c => new { c.CompanyId, c.BaseEntityId }).
HasConstraintName("FK_Child_BaseEntity");
p_mbuModel.Entity<GrandChild>().ToTable("T_GrandChild");
p_mbuModel.Entity<GrandChild>().HasKey(t => new { t.CompanyId, t.BaseEntityId, t.ChildId, t.GrandChildId });
p_mbuModel.Entity<GrandChild>().HasOne(gc => gc.ParentChild).
WithMany(c => c.GrandChildren).
HasForeignKey(gc => new { gc.CompanyId, gc.BaseEntityId, gc.ChildId }).
HasConstraintName("FK_GrandChild_Child");
}
}
You'll note that it features no direct link from GrandChild to BaseEntity and no foreign key between GrandChild and BaseEntity. Which is consistent. I don't want that direct link. (If nothing else, it could lead to unpleasantness with cascading deletions.)
Still, when I launch Add-Migration, I get the following:
table.ForeignKey(
name: "FK_T_GrandChild_T_BaseEntity_BaseEntityId",
columns: x => { x.CompanyId, x.BaseEntityId },
principalTable: "T_BaseEntity",
principalColumns: new { "CompanyId", "BaseEntityId" },
onDelete: ReferentialAction.Cascade);
The very thing that I have tried to keep out. (On migrating, it creates the foreign key constraint that I don't want, and with that unwieldy name to boot.)
I have tried adding
    p_mbuModel.Entity<GrandChild>().Ignore(t => new { t.CompanyId, t.BaseEntityId });
which raises an exception to the tune of
The expression 't => new <>f__AnonymousType10`2(CompanyId = t.CompanyId, BaseEntityId = t.BaseEntityId)' is not a valid property expression. The expression should represent a simple property access: 't => t.MyProperty'.
(I guess that's to be expected; that was a long shot on my behalf.)
I could add a GrandParent property to GrandChild and use Ignore() on it, but that would require me to create the very link I want to hide.
I want no link between BaseEntity and GrandChild, neither as entities nor as database tables.
How can I achieve this?
... I'm embarrassed to admit this. When I posted the question, I trimmed the irrelevant parts of my classes. And I didn't notice that BaseEntity included this line:
public virtual List<GrandChild> GrandChildren { get; set; }
I removed it and launched Add-Migration again. No link appeared between BaseEntity and GrandChild.
Apologies to any and all who tried to make sense of this incomplete riddle.

Validating entities using data annotations or fluent api in EF 7.0 (In Memory)

I can't verify and test my database by in memory providers.
for example I set these properties to required :
public abstract class Log
{
#region Properties
public Guid Id { get; set; }
[Required]
public string ClientIp { get; set; }
[Required]
public string Application { get; set; }
[Required]
public string Host { get; set; }
[Required]
public string Path { get; set; }
[Required]
public string Method { get; set; }
[Required]
public string User { get; set; }
[Required]
public string Date { get; set; }
#endregion
}
and this is my DBContext :
public class ApplicationDbContext : IdentityDbContext<ApplicationUsers, Role, Guid>, IUnitOfWork
{
private readonly IConfigurationRoot _configuration;
public ApplicationDbContext(IConfigurationRoot configuration)
{
_configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var useInMemoryDatabase = _configuration[key: "UseInMemoryDatabase"].Equals(value: "true",
comparisonType: StringComparison.OrdinalIgnoreCase);
if (useInMemoryDatabase)
optionsBuilder.UseInMemoryDatabase();
else
optionsBuilder.UseSqlServer(
connectionString: _configuration[key: "ConnectionStrings:ApplicationDbContextConnection"]
, sqlServerOptionsAction: serverDbContextOptionsBuilder =>
{
var minutes = (int) TimeSpan.FromMinutes(3).TotalSeconds;
serverDbContextOptionsBuilder.CommandTimeout(commandTimeout: minutes);
});
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Log>()
.HasKey(c => c.Id);
modelBuilder.Entity<Log>()
.HasDiscriminator<int>(name: "Type")
.HasValue<LogRequest>(value: Convert.ToInt32(value: LogLevel.Information))
.HasValue<LogError>(value: Convert.ToInt32(value: LogLevel.Error));
}
And this is my unit test :
[TestClass]
public class LogRepositoryTest
{
private readonly IServiceProvider _serviceProvider;
public LogRepositoryTest()
{
var services = new ServiceCollection();
services.AddScoped<IUnitOfWork, ApplicationDbContext>();
services.AddScoped<ILogRepository, LogRepository>();
services.AddSingleton(provider => new ConfigurationBuilder()
.AddInMemoryCollection(initialData: new[]
{
new KeyValuePair<string, string>(key: "UseInMemoryDatabase", value: "true"),
})
.Build());
services.AddEntityFrameworkInMemoryDatabase().AddDbContext<ApplicationDbContext>(ServiceLifetime.Scoped);
_serviceProvider = services.BuildServiceProvider();
}
[TestMethod]
public async Task Verify_SaveRequestLog()
{
using (var serviceScope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
using (var context = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>())
{
context.Set<Log>().Add(new LogRequest());
var result =await context.SaveAllChangesAsync();
Assert.AreEqual(1, result);
}
}
}
But the unit test method always return 1 and passes, meanwhile the empty object of LogRequest must not save anything to database!
How can I determine not null properties for unit test ? In fact how can I enforce unit test to reflect to validation policies ?
Update:
Based on this linke :
Entity Framework Core Issues
that I asked, I got this respond:
EF Core doesn't do any validation of entities beyond what is needed
for internal consistency. Validation is something that could be done
in EF, but experience shows that it is not something that is useful to
many developers because it usually cannot replace either client-side
validation or database validation and there are also other places
where validation can be done more effectively.
Going beyond EF to the database, the in-memory database does not
currently validate nullability (i.e. requiredness) when saving
property values. I will leave this issue open so that we can discuss
as a team whether this is something we should add.
Also, if the intent is test with an in-memory database as an
approximation for a relational database, then you might want to
consider using SQLite in in-memory mode. See
https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/index
for more information.
Based on this linke :
Entity Framework Core Issues
that I asked, I got my answer :
class MyContext : DbContext
{
public override int SaveChanges()
{
var entities = from e in ChangeTracker.Entries()
where e.State == EntityState.Added
|| e.State == EntityState.Modified
select e.Entity;
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
Validator.ValidateObject(entity, validationContext);
}
return base.SaveChanges();
}
}

Sitecore Load all items into an MVC model?

I have created a bunch of custom templates to store items (such as Industries, Subindustries, etc.) in Sitecore. I now want to go about loading these into my Sitecore MVC model.
The lists are located in sitecore > Content > Lists. For example inside the Lists folder there is a folder called Country. I want to get back all the items within the Country folder and populate them as unordered list in my view.
UPDATE: I implemented the Glass.Mapper.Sc method suggested below. It is fully operational now.
This is what my working model looks like now:
using Glass.Mapper.Sc.Configuration;
using Glass.Mapper.Sc.Configuration.Attributes;
using Sitecore.Data.Items;
using Sitecore.Mvc.Presentation;
using System;
using System.Collections.Generic;
namespace Sitecore.Web.Models
{
public class Registration: IRenderingModel
{
public Rendering Rendering { get; set; }
public Item Item { get; set; }
public Item PageItem { get; set; }
public IEnumerable<CountryChildItem> CountryList { get; set; }
[SitecoreType(AutoMap = true)]
public class CountryItem
{
public virtual IEnumerable<CountryChildItem> Children { get; set; }
}
[SitecoreType(AutoMap = true)]
public class CountryChildItem
{
[SitecoreId]
public virtual Guid Id { get; set; }
[SitecoreInfo(SitecoreInfoType.Path)]
public virtual string Path { get; set; }
[SitecoreField]
public virtual string DisplayName { get; set; }
[SitecoreField]
public virtual string Abbreviation { get; set; }
}
public void Initialize(Rendering rendering)
{
Rendering = rendering;
Item = rendering.Item;
PageItem = PageContext.Current.Item;
}
}
}
and this is what my working contoller looks like:
using Glass.Mapper.Sc;
using Sitecore.Web.Models;
using System.Web.Mvc;
namespace Sitecore.Web.Controllers
{
public class RegistrationController : Controller
{
Registration registrationModel = new Registration();
public ActionResult Index()
{
ISitecoreContext sitecoreContext = new SitecoreContext();
ISitecoreService service = new SitecoreService(sitecoreContext.Database);
Registration.CountryItem countryItem = service.GetItem<Registration.CountryItem>("/sitecore/content/Lists/Country");
registrationModel.CountryList = countryItem.Children;
return View(registrationModel);
}
}
}
and a snippet of my working view:
<ul class="select-menu-options dropdown-menu">
#foreach (var country in Model.CountryList)
{
<li>#country.DisplayName</li>
}
</ul>
If I were in your position I'd look into Glassmapper for Sitecore.
It's a fairly lightweight ORM for Sitecore.
http://www.glass.lu/Mapper/Sc
I'd also suggest moving the lists located in
sitecore > Templates > User Defined > Lists > Content
to some where under either
sitecore > Content
or
sitecore > System
(whichever makes more sence)
UPDATE:
Try adding this above your class:
[SitecoreType(AutoMap = true)]
public class CountryItem
{
//...
}
If you change your CountryItem and other model classes to inherit from SearchResultItem like that:
[PredefinedQuery("TemplateID", ComparisonType.Equal, "{ID-OF-CountryItem-TEMPLATE}", typeof(ID))]
public class CountryItem : Sitecore.ContentSearch.SearchTypes.SearchResultItem
{
[IndexField("_displayname")]
public virtual string DisplayName { get; set; }
[IndexField("abbreviation")]
public string Abbreviation { get; set; }
}
You should be able to use Sitecore indexes to retrieve all the countries and other lists like that:
private static string IndexName
{
get
{
return string.Format("sitecore_{0}_index", (Context.ContentDatabase ?? Context.Database).Name);
}
}
private static string Language { get { return Context.Language.Name; } }
public IEnumerable<CountryItem> GetCountries()
{
using (var context = ContentSearchManager.GetIndex(IndexName).CreateSearchContext())
{
IQueryable<CountryItem> queryable = context.GetQueryable<CountryItem>();
queryable = queryable.Where(i => i.Language == Language);
queryable = queryable.Where(i => i.LatestVersion);
// ... maybe excluding standard values or some other filters
var searchResults = queryable.GetResults();
return queryable.ToList();
}
}
Please be aware that this is just an example. You need to test it and most probably adapt to your solution.
And as Dar Brett mentioned, you should not keep any data items under the Templates node.

How to fake a validation error in a MonoRail controller unit-test?

I am running on Castle's trunk, and trying to unit-test a controller-action where validation of my DTO is set up. The controller inherits from SmartDispatcherController. The action and DTO look like:
[AccessibleThrough(Verb.Post)]
public void Register([DataBind(KeyReg, Validate = true)] UserRegisterDto dto)
{
CancelView();
if (HasValidationError(dto))
{
Flash[KeyReg] = dto;
Errors = GetErrorSummary(dto);
RedirectToAction(KeyIndex);
}
else
{
var user = new User { Email = dto.Email };
// TODO: Need to associate User with an Owning Account
membership.AddUser(user, dto.Password);
RedirectToAction(KeyIndex);
}
}
public class UserRegisterDto
{
[ValidateNonEmpty]
[ValidateLength(1, 100)]
[ValidateEmail]
public string Email { get; set; }
[ValidateSameAs("Email")]
public string EmailConfirm { get; set; }
[ValidateNonEmpty]
public string Password { get; set; }
[ValidateSameAs("Password")]
public string PasswordConfirm { get; set; }
// TODO: validate is not empty Guid
[ValidateNonEmpty]
public string OwningAccountIdString { get; set; }
public Guid OwningAccountId
{
get { return new Guid(OwningAccountIdString); }
}
[ValidateLength(0, 40)]
public string FirstName { get; set; }
[ValidateLength(0, 60)]
public string LastName { get; set; }
}
The unit test looks like:
[Fact]
public void Register_ShouldPreventInValidRequest()
{
PrepareController(home, ThorController.KeyPublic, ThorController.KeyHome, HomeController.KeyRegister);
var dto = new UserRegisterDto { Email = "ff" };
home.Register(dto);
Assert.True(Response.WasRedirected);
Assert.Contains("/public/home/index", Response.RedirectedTo);
Assert.NotNull(home.Errors);
}
("home" is my HomeController instance in the test; home.Errors holds a reference to an ErrorSummary which should be put into the Flash when there's a validation error).
I am seeing the debugger think that dto has no validation error; it clearly should have several failures, the way the test runs.
I have read Joey's blog post on this, but it looks like the Castle trunk has moved on since this was written. Can someone shed some light, please?
http://www.candland.net/blog/2008/07/09/WhatsNeededForCastleValidationToWork.aspx would appear to contain an answer.