Sharp Architecture issue with EntityWithTypedId - fluent-nhibernate

I wish to use Guid comb for my identity. I've added the EntityWithTypedId interface to my class which cause my table not to persist. ie using nh prof i can see the SaveOrUpdate method is not called. If i just use the Entity interface i can see it in the profiler.
public class Application : EntityWithTypedId<Guid>
{
public virtual string Name { get; set; }
public virtual string CreatedByUserName { get; set; }
public virtual string ModifiedByUserName { get; set; }
public virtual DateTime Created { get; set; }
public virtual DateTime Modified { get; set; }
}
public class ApplicationQuery : NHibernateQuery, IApplicationQuery
{
public void Update(Application application)
{
Session.SaveOrUpdate(application);
}
}
public class ApplicationMap : IAutoMappingOverride<Application>
{
public void Override(AutoMapping<Application> mapping)
{
mapping.Table("Application");
mapping.Id(x => x.Id, "Id").GeneratedBy.GuidComb();
}
}
Controller
[HttpPost]
[ActionName("Edit")]
public ActionResult EditPost(Application application)
{
var updateApplication = new Application();
updateApplication.Created = DateTime.Now;
updateApplication.Modified = DateTime.Now;
_applicationQuery.Update(updateApplication);
return RedirectToAction("Index");
}

I fixed this by updating my mapping to the following.
public void Override(AutoMapping<Application> mapping)
{
mapping.Table("Application");
mapping.Id(x => x.Id, "Id").GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
}
See here for more info.
http://s274.codeinspot.com/q/1486941

Related

How to update an existing entity that has a nested list of entities?

I'm trying to update an entity using entity framework but, everytime I try to do it, it raises an error saying that a nested entity the main class contains cannot be tracked.
These are my classes:
public abstract class BaseEntity
{
public int Id { get; set; }
}
public class Dashboard : BaseEntity
{
public int Order { get; set; }
public string Title { get; set; }
public bool Enabled { get; set; }
public virtual ICollection<Submenu> Submenu { get; set; }
}
public class Submenu : BaseEntity
{
public int Order { get; set; }
public bool Enabled { get; set; }
public string Title { get; set; }
public string Image { get; set; }
public string Descriptions { get; set; }
public virtual ICollection<Action> Actions { get; set; }
public int DashboardId { get; set; }
public virtual Dashboard Dashboard { get; set; }
}
public class Action : BaseEntity
{
public string Type { get; set; }
public string Label { get; set; }
public string Url { get; set; }
public string Extension { get; set; }
public virtual Submenu Submenu { get; set; }
public int SubmenuId { get; set; }
}
The one I am using to update is Dashboard, which contains the rest of the classes.
I'm trying to do it using a generic service layer and a generic repository that are defined this way:
public class GenericService<T> : IGenericService<T> where T : BaseEntity
{
private readonly IBaseRepository<T> baseRepository;
public GenericService(IBaseRepository<T> baseRepository)
{
this.baseRepository = baseRepository;
}
public async Task Update(T entity, T attachedEntity)
{
await baseRepository.Update(entity, attachedEntity);
}
}
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly PortalContext dataContext;
private DbSet<T> DbSet { get; set; }
public BaseRepository(PortalContext context)
{
dataContext = context;
DbSet = dataContext.Set<T>();
}
public async Task Update(T entity, T attachedEntity)
{
dataContext.Entry(attachedEntity).State = EntityState.Detached;
DbSet.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
await dataContext.SaveChangesAsync();
}
}
And, at last but no least, this is the way I am configuring everything at Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PortalContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("PortalContext"))
);
services.AddTransient(typeof(IGenericService<>), typeof(GenericService<>));
services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddTransient<Func<string, ClaimsPrincipal, IRoleCheck>>((serviceProvider) =>
{
return (controllerName, claimsPrincipal) =>
new RoleCheck(serviceProvider.GetRequiredService<IGenericService<Dossier>>(),
serviceProvider.GetRequiredService<IGenericService<DossierTemplate>>(),
serviceProvider.GetRequiredService<IGenericService<Dashboard>>(),
controllerName, claimsPrincipal);
});
}
What the application first does is calling the RoleCheck class to retrieve and filter the required entities and, after that, the user can update them.
When I call the update function at the controller
public async Task<ActionResult<Dashboard>> Put(int id, [FromBody] Dashboard dashboard)
{
var currentDashboard = await service.Get(id);
if (currentDashboard == null)
{
return NotFound();
}
await service.Update(dashboard, currentDashboard);
return Ok();
}
I always receive the next error at the repository:
error
Is there something I am doing wrong? I have been stuck with this for a week now...
Thanks in advance and sorry for the long text, but I wanted it to be clear.
I could finally solve it by adding .AsNoTracking() at the Get() method of my repository:
public async Task<T> Get(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> includes)
{
IQueryable <T> query = DbSet.AsNoTracking();
if (includes != null)
{
query = includes(query);
}
return await query.FirstOrDefaultAsync(m => m.Id == id);
}

select - keyword not working with odata, automapper and efcore

I am trying to apply the odata query to my automapper - mappings at my efcore context. Everything works as expected until I use the $select query option.
When I try to use the select keyword in the request to my odata - controller, I get the exception:
SerializationException: 'SourceSourceInjectedQuery`2' cannot be serialized using the ODataMediaTypeFormatter.
I am using the UseAsDataSource - Extension method because it was recommended here on github.
This is my oDataController:
public class StudentsController : ODataController {
private readonly SchoolContext schoolContext;
public StudentsController(SchoolContext schoolContext) {
this.schoolContext = schoolContext;
}
[EnableQuery]
public IActionResult Get() {
return Ok(
schoolContext
.Students
.UseAsDataSource()
.For<StudentVM>()
);
}
}
This is my Entity for EFCore:
public class Student {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
And this is my mappingprofile for automapper:
public class StudentVM {
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
}
public class StudentProfile : Profile {
public StudentProfile() {
CreateMap<Student, StudentVM>();
}
}
Do I need some specific mapping to do this?
I figured out I had a mistake in my configuration of the odataservice inside my startup.cs
private static IEdmModel GetEdmModel() {
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Student>("Students");
builder.EntitySet<Course>("Courses");
return builder.GetEdmModel();
}
I put my Entities instead of my ViewModels there. This is the fixed code:
private static IEdmModel GetEdmModel() {
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<StudentVM>("Students");
builder.EntitySet<CourseVM>("Courses");
return builder.GetEdmModel();
}
Now it's working as expected

fluent NHibernate many to many creates additional table

I have two tables "recall" and "service" and I need many to many between them.
I use fluent NHibernate mapping but it creates additional table with name "servicetorecall"
public class Recall : BaseDomain
{
public virtual string Name { get; set; }
public virtual string PersonPosition { get; set; }
public virtual string RecallText { get; set; }
private ICollection<Service> _services = new List<Service>();
public virtual ICollection<Service> Services
{
get { return _services; }
set { _services = value; }
}
}
public class Service : BaseDomain
{
public virtual string Name { get; set; }
public virtual string Url { get; set; }
public virtual string ImgPath { get; set; }
public virtual string ShortContent { get; set; }
public virtual string Content { get; set; }
public virtual bool ServiceIsVisible { get; set; }
ICollection<Recall> _recalls = new List<Recall>();
public virtual ICollection<Recall> Recalls
{
get { return _recalls; }
set { _recalls = value; }
}
}
Mappings :
class RecallMappingOverride : IAutoMappingOverride<Recall>
{
public void Override(AutoMapping<Recall> mapping)
{
mapping.Cache.ReadWrite();
mapping.HasManyToMany(q => q.Services).Table(MappingNames.RECALLS_RELATIONS)
.ParentKeyColumn(MappingNames.RECALL_ID)
.ChildKeyColumn(MappingNames.SERVICE_ID).Inverse().Cascade.All();
}
}
public class ServiceMappingOverride : IAutoMappingOverride<Service>
{
public void Override(AutoMapping<Service> mapping)
{
mapping.Cache.ReadWrite();
mapping.HasManyToMany(q => q.Recalls).Table(MappingNames.RECALLS_RELATIONS) .ParentKeyColumn(MappingNames.SERVICE_ID).ChildKeyColumn(MappingNames.RECALL_ID)
.Inverse().Cascade.All();
}
}
I tried to change cascades but this didn't help. Also I did the same with other entities and it works correctly what type of magic is it?
How do you define "correct", what do you want to achieve?
I never heard of any clean solution for many to many relations which doesn't use pivot table.
[quick glimpse at your mappings]: only one of the "ManyToMany" should be Inverse

Fluent NHibernate mapping using component

I have just started working on a project using Fluent NHibernate.
What is the correct way to map the following classes using Fluent NHibernate?
public class DurationUnit
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Duration
{
public virtual int Value { get; set; }
public virtual DurationUnit Unit { get; set; }
public virtual int DurationInMinutes { get{ throw new NotImplementedException(); } }
}
public class Event
{
public virtual int Id { get; set; }
public virtual String Name { get; set; }
public virtual Duration MaxDuration { get; set; }
public virtual Duration MinDuration { get; set; }
}
My inital approach was to declare a ClassMap for DurationUnit and Event, with Duration as a component of Event. When trying this I received an exception:
NHibernate.MappingException: Could not determine type for:
Entities.DurationUnit
if your mapping looks like this
public EventMap()
{
Component(x => x.MaxDuration, c =>
{
c.Map(x => x.Value, "MaxDurationValue");
c.Reference(x => x.Unit, "MaxDurationUnitId");
});
}
then make sure class DurationUnitMap is public and is added to the configuration

How to model this classes withN Hibernate and Fluent.NHibernate Maps?

I'm using ASP.NET MVC with NHibernate and Fluent.NHibernate Maps.
I would like to know how to map the classes on Fluent and to create the database tables on my MySQL:
public class AgenteDeViagem {
public virtual int Id { get; set; }
public virtual string Email { get; set; }
public virtual AgentePessoa AgentePessoa { get; set; }
}
public interface AgentePessoa {
}
public class AgenteDeViagemPJ:AgentePessoa {
public virtual int Id { get; set; }
public virtual AgenteDeViagem AgenteDeViagem { get; set; }
public virtual string Razao { get; set; }
}
public class AgenteDeViagemPF:AgentePessoa {
public virtual int Id { get; set; }
public virtual AgenteDeViagem AgenteDeViagem { get; set; }
public virtual string Nome { get; set; }
}
Thank you very much!
Looks to me like you're halfway there. You're already using virtual and relations are set, so using the Automapping strategy, you only need to build the session factory:
private static ISessionFactory InitializeNHibernate()
{
var cfg = Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(c =>
c.Database("agente").Server("localhost")
.Username("user").Password("password")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<AgenteDeViagem>())
.ExposeConfiguration(configuration =>
{
// Comment to disable schema generation
BuildDatabaseSchema(configuration);
});
return cfg.BuildSessionFactory;
}
private static void BuildDatabaseSchema(Configuration configuration)
{
var schemaExport = new SchemaExport(configuration);
schemaExport.SetOutputFile("mysql_script.sql");
schemaExport.Create(false, true);
}