Value cannot be null. (Parameter 'source') executing stored procedure with EF Core in ASP.NET Core web API Project - asp.net-core

I need to connect to my SAP Business One SQL Server database, specifically to OHEM table
This table has more than 100 fields but I only need seven of them
I have this dbContext
public class SAPContext:DbContext
{
public DbSet<Empleado> Empleados { get; set; }
public SAPContext(){ }
public SAPContext(DbContextOptions<SAPContext> options)
: base(options){}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseSqlServer("Data Source = SBO92PL00; Initial Catalog = FULCRUM;.... )
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.LogTo(Console.WriteLine,
new[] { DbLoggerCategory.Database.Command.Name },
LogLevel.Information)
.EnableSensitiveDataLogging();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Empleado>().ToTable("OHEM");
modelBuilder.Entity<Empleado>()
.Property(e => e.firstName)
.IsRequired(false);
modelBuilder.Entity<Empleado>()
.Property(e => e.email)
.IsRequired(false);
modelBuilder.Entity<Empleado>()
.Property(e => e.middleName)
.IsRequired(false);
modelBuilder.Entity<Empleado>()
.Property(e => e.lastName)
.IsRequired(false);
modelBuilder.Entity<Empleado>()
.Property(e => e.U_Activo)
.IsRequired(false);
modelBuilder.Entity<Empleado>()
.Property(e => e.U_Oficina)
.IsRequired(false);
I have this entity
public class Empleado
{
public int empId { get; set; }
public string firstName { get; set; } = string.Empty;
public string middleName { get; set; } = string.Empty;
public string lastName { get; set; } = string.Empty;
public string email { get; set; } = string.Empty;
public string U_Oficina { get; set; } = string.Empty;
public string U_Activo { get; set; } = string.Empty;
}
With the same field names.
I have this method in my repository:
public async Task<IEnumerable<EmpleadoDTO>> GetExtensionesEmpleados()
{
try
{
List<Empleado> listaEmpleados = new List<Empleado>();
listaEmpleados = await _context.Empleados
.FromSqlRaw("GetExtensionesIntranet")
.ToListAsync();
if (listaEmpleados.Count() == 0)
{
_logger.LogInformation($"No se han encontrado empleados");
}
return EmpleadosToDTO(listaEmpleados);
}
catch (Exception ex)
{
_logger.LogCritical($"Se ha producido una excepcion obteniendo los empleados", ex.Message);
return null;
}
}
And this is my stored procedure:
Then in my controller
[HttpGet()]
public async Task<ActionResult<IEnumerable<Empleado>>> GetEmpleados()
{
var listaEmpleadosDTO = await _repo.GetExtensionesEmpleados();
if (listaEmpleadosDTO.Count() == 0)
{
return NotFound();
}
return Ok(listaEmpleadosDTO);
}
But when I try this code:
listaEmpleados = await _context.Empleados
.FromSqlRaw("GetExtensionesIntranet")
.ToListAsync();
I keep getting this error:
System.ArgumentNullException: Value cannot be null. (Parameter 'source')
at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
at System.Linq.Enumerable.Count[TSource](IEnumerable`1 source)
at Intranet.API.Controllers.EmpleadosController.GetEmpleados() in /Users/kintela/Repos/Intranet-WebAPI/Intranet.API/Controllers/EmpleadosController.cs:line 25
Any ideas, please?
Thanks

GetExtensionesEmpleados is returning null, so see your logger output for exception in
_logger.LogCritical($"Se ha producido una excepcion obteniendo los empleados", ex.Message);

In the end I had several problems. On the one hand My entity did not have any primary key assigned and in fact I do not need it so I had to add this
modelBuilder.Entity<Empleado>().ToTable("OHEM").HasNoKey();
And in the stored procedure it was not returning. the U_Activo field that I had defined in the entity so I had to add it too and now everything works

Related

ASP.NET Core : Fluent Api relationships configuration

There are a lot of examples how to use Fluent API in the internet, but mostly shows how configure one relationship between two models. In my case I need 3 relationships between 2 models. How to configure relationships between models below with Fluent API?
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public int FinanceEstimateId { get; set; }
public Estimate FinanceEstimate { get; set; }
public int EnvironmentEstimateId { get; set; }
public Estimate EnvironmentEstimate { get; set; }
public int SelfEstimateId { get; set; }
public Estimate SelfEstimate { get; set; }
}
public class Estimate
{
public int Id { get; set; }
public string Name { get; set; } // like: bad, good, excellent
public float Value { get; set; } // like: 1,2,3
}
Maybe this points you in the right direction.
I would go for 2 configurations like:
public class CompanyConfiguration : IEntityTypeConfiguration<Company>
{
public void Configure(EntityTypeBuilder<Company> builder)
{
builder.ToTable("Companies");
builder
.HasOne(x => x.EnvironmentEstimate)
.WithMany()
.HasForeignKey(x => x.EnvironmentEstimateId)
.OnDelete(DeleteBehavior.NoAction);
builder
.HasOne(x => x.FinanceEstimate)
.WithMany()
.HasForeignKey(x => x.FinanceEstimateId)
.OnDelete(DeleteBehavior.NoAction);
builder
.HasOne(x => x.SelfEstimate)
.WithMany()
.HasForeignKey(x => x.SelfEstimateId)
.OnDelete(DeleteBehavior.NoAction);
}
}
public class EstimateConfiguration : IEntityTypeConfiguration<Estimate>
{
public void Configure(EntityTypeBuilder<Estimate> builder)
{
builder.ToTable("Estimates");
}
}
You need a DbContext:
public class MyDbContext : DbContext
{
public DbSet<Company> Companies { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"CONNECTIONSTRING");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// applies the configuration (those IEntityTypeConfiguration<T> things)
modelBuilder.ApplyConfigurationsFromAssembly(typeof(MyDbContext).Assembly);
}
}
I created a console application that demonstrates the usage
using var ctx = new MyDbContext();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();
var company1 = new Company
{
Name = "Name1",
EnvironmentEstimate = new Estimate { Name = "EnvironmentEstimate1", Value = 1 },
FinanceEstimate = new Estimate { Name = "FinanceEstimate1", Value = 2 },
SelfEstimate = new Estimate { Name = "SelfEstimate1", Value = 3 }
};
var company2 = new Company
{
Name = "Name2",
EnvironmentEstimate = new Estimate { Name = "EnvironmentEstimate2", Value = 4 },
FinanceEstimate = new Estimate { Name = "FinanceEstimate2", Value = 5 },
SelfEstimate = new Estimate { Name = "SelfEstimate2", Value = 6 }
};
await ctx.Companies.AddAsync(company1);
await ctx.Companies.AddAsync(company2);
await ctx.SaveChangesAsync();
var result = await ctx.Companies.ToListAsync();
Console.WriteLine("Done");

system.outofmemoryexception swashbuckle.aspnetcore

I am having this issue when I am dealing with Geometry datatypes when I change the property to string everything works like a charm. Below you may see that I used schema filter to remove Ignored data member , and document filter to remove anything related to nettopology.
Property Name = GeoPoly
Swagger Config Class
public static IServiceCollection AddSwaggerModule(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v2", new OpenApiInfo { Title = "Test API", Version = "0.0.1" });
c.SchemaFilter<MySwaggerSchemaFilter>();
c.DocumentFilter<RemoveBogusDefinitionsDocumentFilter>();
c.ResolveConflictingActions(x => x.First());
});
return services;
}
public static IApplicationBuilder UseApplicationSwagger(this IApplicationBuilder app)
{
app.UseSwagger(c =>
{
c.RouteTemplate = "{documentName}/api-docs";
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/v2/api-docs", "Test API");
});
return app;
}
}
public class MySwaggerSchemaFilter : Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema?.Properties == null)
{
return;
}
var ignoreDataMemberProperties = context.Type.GetProperties()
.Where(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null);
foreach (var ignoreDataMemberProperty in ignoreDataMemberProperties)
{
var propertyToHide = schema.Properties.Keys
.SingleOrDefault(x => x.ToLower() == ignoreDataMemberProperty.Name.ToLower());
if (propertyToHide != null)
{
schema.Properties.Remove(propertyToHide);
}
}
}
}
public class RemoveBogusDefinitionsDocumentFilter : Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Components.Schemas.Remove("Districts");
swaggerDoc.Components.Schemas.Remove("Geometry");
swaggerDoc.Components.Schemas.Remove("CoordinateSequenceFactory");
swaggerDoc.Components.Schemas.Remove("GeometryOverlay");
swaggerDoc.Components.Schemas.Remove("NtsGeometryServices");
swaggerDoc.Components.Schemas.Remove("CoordinateEqualityComparer");
swaggerDoc.Components.Schemas.Remove("NtsGeometryServices");
swaggerDoc.Components.Schemas.Remove("GeometryFactory");
swaggerDoc.Components.Schemas.Remove("OgcGeometryType");
swaggerDoc.Components.Schemas.Remove("Coordinate");
swaggerDoc.Components.Schemas.Remove("Point");
}
}
Entity Class
public class Districts : BaseEntity<long>
{
public string DistrictsDesc { get; set; }
public string DistrictsDescAr { get; set; }
[IgnoreDataMember]
[Column(TypeName = "geometry")]
public Geometry GeoPoly { get; set; }
public IList<Records> Records { get; set; } = new List<Records>();
public long? RegionsId { get; set; }
public Regions Regions { get; set; }
public long? CitiesId { get; set; }
public Cities Cities { get; set; }
}
Is there a way to stop swashbuckle gen from dealing with datatypes other than documents filter ?

.net core GraphQL, GraphQL.SystemTextJson: Serialization and deserialization of 'System.Type' instances are not supported

In a ASP.NET core 5 application, I use GraphQL with GraphQL.SystemTextJson.
When I attempt to return a result, I get s System.NotSupportedException saying "Serialization and deserialization of 'System.Type' instances are not supported and should be avoided since they can lead to security issues.".
I suspect something to be missing in the configuration of DocumentWriter.
It is configured like this in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
...
services.AddScoped<IDocumentWriter, DocumentWriter>();
Any suggestion?
Update:
for completeness, as asked by #AndrewSilver, I report the whole code (adapted from https://www.red-gate.com/simple-talk/dotnet/net-development/building-and-consuming-graphql-api-in-asp-net-core-3-1/ and ported to .net core 5.0).
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "GraphQlExperiments", Version = "v1" });
});
services.AddScoped<IDocumentExecuter, DocumentExecuter>();
services.AddScoped<IDocumentWriter, DocumentWriter>();
services.AddScoped<AuthorService>();
services.AddScoped<AuthorRepository>();
services.AddScoped<AuthorQuery>();
services.AddScoped<AuthorType>();
services.AddScoped<BlogPostType>();
services.AddScoped<ISchema, GraphQLDemoSchema>();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "GraphQlExperiments v1"));
}
// See: https://github.com/JosephWoodward/graphiql-dotnet
app.UseGraphiQl("/graphiql", "/api/v1/graphql");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
public class Author
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Author Author { get; set; }
}
public class AuthorType : ObjectGraphType<Author>
{
public AuthorType()
{
Name = "Author";
Field(_ => _.Id).Description("Author's Id.");
Field(_ => _.FirstName).Description("First name of the author");
Field(_ => _.LastName).Description("Last name of the author");
}
}
public class BlogPostType : ObjectGraphType<BlogPost>
{
public BlogPostType()
{
Name = "BlogPost";
Field(_ => _.Id, type:
typeof(IdGraphType)).Description("The Id of the Blog post.");
Field(_ => _.Title).Description("The title of the blog post.");
Field(_ => _.Content).Description("The content of the blog post.");
}
}
public class AuthorQuery : ObjectGraphType
{
public AuthorQuery(AuthorService authorService)
{
int id = 0;
Field<ListGraphType<AuthorType>>(
name: "authors",
resolve: context =>
{
return authorService.GetAllAuthors();
});
Field<AuthorType>(
name: "author",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context =>
{
id = context.GetArgument<int>("id");
return authorService.GetAuthorById(id);
}
);
Field<ListGraphType<BlogPostType>>(
name: "blogs",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context =>
{
return authorService.GetPostsByAuthor(id);
}
);
}
}
public class GraphQLQueryDTO
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; }
public string Variables { get; set; }
}
public class GraphQLDemoSchema : Schema, ISchema
{
public GraphQLDemoSchema(IServiceProvider resolver) : base(resolver)
{
Query = resolver.GetService<AuthorQuery>();
}
}
public class AuthorService
{
private readonly AuthorRepository _authorRepository;
public AuthorService(AuthorRepository
authorRepository)
{
_authorRepository = authorRepository;
}
public List<Author> GetAllAuthors()
{
return _authorRepository.GetAllAuthors();
}
public Author GetAuthorById(int id)
{
return _authorRepository.GetAuthorById(id);
}
public List<BlogPost> GetPostsByAuthor(int id)
{
return _authorRepository.GetPostsByAuthor(id);
}
}
public class AuthorRepository
{
private readonly List<Author> authors = new List<Author>();
private readonly List<BlogPost> posts = new List<BlogPost>();
public AuthorRepository()
{
Author author1 = new Author
{
Id = 1,
FirstName = "Joydip",
LastName = "Kanjilal"
};
Author author2 = new Author
{
Id = 2,
FirstName = "Steve",
LastName = "Smith"
};
BlogPost csharp = new BlogPost
{
Id = 1,
Title = "Mastering C#",
Content = "This is a series of articles on C#.",
Author = author1
};
BlogPost java = new BlogPost
{
Id = 2,
Title = "Mastering Java",
Content = "This is a series of articles on Java",
Author = author1
};
posts.Add(csharp);
posts.Add(java);
authors.Add(author1);
authors.Add(author2);
}
public List<Author> GetAllAuthors()
{
return this.authors;
}
public Author GetAuthorById(int id)
{
return authors.Where(author => author.Id == id).FirstOrDefault<Author>();
}
public List<BlogPost> GetPostsByAuthor(int id)
{
return posts.Where(post => post.Author.Id == id).ToList<BlogPost>();
}
}
[Route("/api/v1/graphql")]
public class GraphQLController : Controller
{
private readonly ISchema _schema;
private readonly IDocumentExecuter _executer;
public GraphQLController(
ISchema schema,
IDocumentExecuter executer
)
{
_schema = schema;
_executer = executer;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] GraphQLQueryDTO query)
{
var result = await _executer.ExecuteAsync(_ =>
{
_.Schema = _schema;
_.Query = query.Query;
_.Inputs = query.Variables?.ToInputs();
});
if (result.Errors?.Count > 0)
{
return BadRequest();
}
return Ok(result.Data);
}
}
And this is a sample request that triggers the error:
query {
author (id: 1){
id
firstName
lastName
}
blogs
{
id
title
content
}
}
I solved creating a custom JsonConverter:
public class CustomJsonConverterForType : JsonConverter<Type>
{
public override Type Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options
)
{
// Caution: Deserialization of type instances like this
// is not recommended and should be avoided
// since it can lead to potential security issues.
// If you really want this supported (for instance if the JSON input is trusted):
// string assemblyQualifiedName = reader.GetString();
// return Type.GetType(assemblyQualifiedName);
throw new NotSupportedException();
}
public override void Write(
Utf8JsonWriter writer,
Type value,
JsonSerializerOptions options
)
{
string assemblyQualifiedName = value.AssemblyQualifiedName;
// Use this with caution, since you are disclosing type information.
writer.WriteStringValue(assemblyQualifiedName);
}
}
Then, in configureServices:
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.Converters.Add(new CustomJsonConverterForType());
});
Instead of using System.Text.Json.JsonSearializer Use NewtonSoft.JsonConvert.SearializeObject
I fixed that problem by using the snippet shown in the docs: https://graphql-dotnet.github.io/docs/migrations/migration3
[HttpPost]
public async Task<IActionResult> Post([FromBody] GraphQLQueryDTO query)
{
var result = await _executer.ExecuteAsync(_ =>
{
_.Schema = _schema;
_.Query = query.Query;
_.Inputs = query.Variables?.ToInputs();
});
/* ----------- Added this ---------------------------------*/
HttpContext.Response.ContentType = "application/json";
HttpContext.Response.StatusCode = 200; // OK
var writer = new GraphQL.SystemTextJson.DocumentWriter();
await writer.WriteAsync(HttpContext.Response.Body, result);*
/* ------------------------------------------------------*/
if (result.Errors?.Count > 0)
{
return BadRequest();
}
return Ok(result.Data);
}
}
In your startup.cs, in ConfigureServices
Add AddNewtonsoftJson() after AddControllers()
services.AddControllers().AddNewtonsoftJson();

EF Core2.0 dbvalidation errors not displaying all errors

I've Created an EF core 2.0 application and trying to validate the model on Savechanges but its only returning the first validation error.
Here are my Dbcontext and controller
public partial class ProductWarehouseContext : DbContext
{ public List<string> ErrorList=new List<string>();
public ProductWarehouseContext(DbContextOptions<ProductWarehouseContext> options)
: base(options)
{
}
public virtual DbSet<Customer> Customer { get; set; }
public virtual DbSet<Order> Order { get; set; }
public virtual DbSet<OrderItem> OrderItem { get; set; }
public virtual DbSet<Product> Product { get; set; }
public virtual DbSet<Supplier> Supplier { get; set; }
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,
validateAllProperties: true);
}
return base.SaveChanges();}
}
Controller
[HttpPost]
public IActionResult Save([FromBody]CustomerViewModel customer)
{
using (var cont = _context.Database.BeginTransaction())
{
try
{
var cust = new Customer()
{
FirstName = customer.FirstName,
LastName = customer.LastName,
City = customer.City,
Country = customer.Country,
Phone = customer.Phone,
IsSubscribedforAlerts = customer.IsSubscribedforAlerts
};
_context.Customer.Add(cust);
_context.SaveChanges();
cont.Commit();
}
catch (Exception e)
{
Errors.Add(e.Message);
cont.Rollback();
foreach (var err in Errors)
{
ModelState.AddModelError("errors", err);
}
return Ok(ModelState);
}
}
return Ok();
}
Class
public partial class Customer
{
public Customer()
{
Order = new HashSet<Order>();
}
public int Id { get; set; }
[Required(ErrorMessage = "FirstName is required to save a new customer")]
public string FirstName { get; set; }
[Required(ErrorMessage = "LastName is required to save a new customer")]
public string LastName { get; set; }
public string City { get; set; }
public string Country { get; set; }
[Required(ErrorMessage = "PhoneNumber is required to save a new customer")]
public string Phone { get; set; }
public bool? IsSubscribedforAlerts { get; set; }
public ICollection<Order> Order { get; set; }
}
and error is only returnig ""firstname" is required and if I pass the firstname in request object then its returning "lastname" is required.
What should I do to return all the errors how we do it in EF6 using DbEntityValidationException ?
That's because ValidateObject() throws upon first encountering an error. Try using TryValidateObject() instead, and pass it a List<ValidationResult> that accumulate all errors.
Something like:
public class EntityValidationException : Exception
{
public EntityValidationException(IEnumerable<ValidationException> exceptions)
{
this.ValidationErrors = exceptions;
}
public IEnumerable<ValidationException> ValidationErrors { get; }
}
Then in your SaveChanges():
foreach (var entity in entities)
{
var validationContext = new ValidationContext(entity);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(entity, validationContext, validationResults);
if (validationResults.Any())
throw new EntityValidationException(validationResults.Select(x => new ValidationException(x, null, null)));
}
Then in your controller/action, you can explicitly handle EntityValidationException:
catch (EntityValidationException validationException)
{
foreach (var err in validationException.ValidationErrors)
{
var validationResult = err.ValidationResult;
ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
}
}

Insert into SQL Server database through NHibernate 4 with Session.Save using mapping-by-code

Currently experiencing issues with Session.Save() not inserting records and producing the following exception:
null id in NHModels.Domain.Activity entry (don't flush the Session after an exception occurs)
at NHibernate.Event.Default.DefaultFlushEntityEventListener.CheckId(Object obj, IEntityPersister persister, Object id, EntityMode entityMode)
at NHibernate.Event.Default.DefaultFlushEntityEventListener.GetValues(Object entity, EntityEntry entry, EntityMode entityMode, Boolean mightBeDirty, ISessionImplementor session)
at NHibernate.Event.Default.DefaultFlushEntityEventListener.OnFlushEntity(FlushEntityEvent event)
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEntities(FlushEvent event)
at NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event)
at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()
at NHUnitOfWork.Dispose() in NHUnitOfWork.cs:line
at DatabaseActivityOperations.<WriteActivity>d__6.MoveNext() in DatabaseActivityOperations.cs:line 240
My class and mapping look like this.
Activity (For simplicity sake, I've removed several ILists related to this class)
public class Activity {
public Activity() {
Activityschema = new List<ActivitySchema>();
}
public virtual int ActivityKey { get; set; }
public virtual string Activityname { get; set; }
public virtual string Activitydescription { get; set; }
public virtual DateTime Averageactivitytime { get; set; }
public virtual int Averagenumberpeople { get; set; }
public virtual string Worktype { get; set; }
public virtual bool? Canautocomplete { get; set; }
public virtual IList<ActivitySchema> Activityschema { get; set; }
}
ActivityMap
public class ActivityMap : ClassMapping<Activity> {
public ActivityMap() {
Schema("dbo");
Lazy(true);
Id(x => x.ActivityKey, map => { map.Generator(Generators.Identity); });
Property(x => x.Activityname, map => { map.NotNullable(true); map.Length(50); });
Property(x => x.Activitydescription, map => { map.NotNullable(true); map.Length(100); });
Property(x => x.Averageactivitytime, map =>
{
map.NotNullable(true);
map.Type(NHibernateUtil.Time);
});
Property(x => x.Averagenumberpeople, map => { map.NotNullable(true); map.Precision(10); });
Property(x => x.Worktype, map => { map.NotNullable(true); map.Length(50); });
Property(x => x.Canautocomplete);
Bag(x => x.Activityschema, colmap => { colmap.Key(x => x.Column("ActivityKey")); colmap.Inverse(true); }, map => { map.OneToMany(); });
}
}
Finally, here's the Unit of Work class I have:
public class NHUnitOfWork : IDisposable
{
public static string ConnectingString { get; private set; } = #"data source=nh;initial catalog=db;MultipleActiveResultSets=True;";
protected static Configuration _config;
protected static NHibernate.ISessionFactory _sessionFactory;
public NHibernate.ISession Session { get; private set; }
protected NHibernate.ITransaction Transaction { get; set; }
private const System.Data.IsolationLevel ISOLATION_LEVEL = System.Data.IsolationLevel.ReadUncommitted;
private bool RollBack { get; set; } = false;
public NHUnitOfWork(string databaseConnectionString)
{
if (_config == null)
{
var cfg = new Configuration();
cfg.DataBaseIntegration(db =>
{
db.Driver<NHibernate.Driver.SqlClientDriver>();
db.ConnectionString = #"data source=nh;initial catalog=db;MultipleActiveResultSets=True;";
//db.ConnectionString = databaseConnectionString;
db.Dialect<MsSql2012Dialect>();
db.BatchSize = 500;
})
.AddAssembly(typeof(Activity).Assembly)
.SessionFactory()
.GenerateStatistics();
var mapper = new ModelMapper();
mapper.AddMappings(typeof(ActivityMap).Assembly.GetTypes());
cfg.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
_config = cfg;
_sessionFactory = _config.BuildSessionFactory();
}
Session = _sessionFactory.OpenSession();
Transaction = Session.BeginTransaction(ISOLATION_LEVEL);
RollBack = false;
}
public void Commit()
{
Transaction.Commit();
}
public void Rollback()
{
if (Transaction.IsActive) Transaction.Rollback();
}
public void Dispose()
{
if (RollBack)
{
Transaction.Rollback();
}
else
{
Transaction.Commit();
}
Session.Close();
}
}
By this point, I believe my configuration for this is correct. I'm successfully used Session.Query to read data and that doesn't present issues. The problem comes when I write something like this to add a new record:
var activity = new Activity
{
Activityname = "TestActivity",
Activitydescription = "This is a test",
Averagenumberpeople = 1,
Worktype = "Test",
Canautocomplete = false,
Averageactivitytime = new DateTime(1, 1, 1, 0, 55, 55)
};
using (var uow = new NHUnitOfWork(NHUnitOfWork.ConnectingString))
{
uow.Session.Save(activity); // Produces exception here
//This also produces an exception
//uow.Session.Save(activity, Generators.Identity);
}
I think this has something to do with how I'm mapping the ID in ActivityMap and the generator isn't working as expected. I've tried to change it to several other types and get the same exception, or one stating that it's not able to convert to SystemInt32. I've also tried changing the ID to long and specifiying a data type, but no luck. What do I seem to be doing wrong here?
Literally minutes after I posted this, I figured out that the issue was actually with how I was setting the date time.
new DateTime(1, 1, 1, 0, 55, 55)
It didn't like the "1/1/0001" part, so that seemed to be causing the issue, I'm only concerned with the time piece. Changing the year to something like 2001 fixed the insert and it's working fine, the message was just not very descriptive.