Error using Autofac.Extras.NHibernate - nhibernate

I'm trying to inject dependencies im my models that NHibernate creates.
What I'm trying to do is the same here: http://fabiomaulo.blogspot.com.br/2008/11/entities-behavior-injection.html
But my container is Autofac.
So, I've found https://www.nuget.org/packages/Autofac.Extras.NHibernate/
I saw the post http://chadly.net/2009/05/dependency-injection-with-nhibernate-and-autofac/ that I've think is the origin of the Autofac.Extras.NHibernate.
My problem is that the code in Autofac.Extras.NHibernate and described in Chad post are different.
Looking at the source code I (think) figured out how to set the BytecodeProvider using:
Cfg.Environment.BytecodeProvider = new AutofacBytecodeProvider(Container, new DefaultProxyFactoryFactory(), new DefaultCollectionTypeFactory());
But now, I'm getting an exception when I tried to retrieve data from database:
[PropertyAccessException: could not set a property value by reflection setter of NHibernate.Autofac2.App_Start.Model.User.Id]
If I comment the line where I set BytecodeProvider the code works.
I created a POC to simulate:
My model:
public class User
{
private readonly ISomeService _someService;
public User(ISomeService someService)
{
this._someService = someService;
}
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual string GetTranslate
{
get { return this._someService != null ? this._someService.T(this.Name) : " No Translate" + this.Name; }
}
}
My mapping:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Length(16)
.Not.Nullable();
}
}
Creation of the Autofac container and SessionFactory using Fluent Nhibernate:
// Create your builder.
var builder = new ContainerBuilder();
builder.RegisterType<SomeService>().As<ISomeService>();
builder.RegisterType<User>().As<IUser>();
Container = builder.Build();
SessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString("Data Source=(local);Initial Catalog=NHibernate.Autofac;User ID=test;Password=102030;Pooling=True"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MvcApplication>())
.ExposeConfiguration(config => config.Properties.Add("use_proxy_validator", "false"))
.ExposeConfiguration(config =>
{
//config.Properties.Add("proxyfactory.factory_class", "");
Cfg.Environment.BytecodeProvider = new AutofacBytecodeProvider(Container, new DefaultProxyFactoryFactory(), new DefaultCollectionTypeFactory());
new SchemaExport(config).Drop(false, false);
new SchemaExport(config).Create(false, true);
})
.BuildSessionFactory();

Well, I've found a solution that works for me.
Now, I'm using NHibernate.DependencyInjection.
The IEntityInjector implemenation:
public class EntityInjector : IEntityInjector
{
private readonly IContainer _container;
public EntityInjector(IContainer container)
{
_container = container;
}
public object[] GetConstructorParameters(System.Type type)
{
var constructor = type.GetConstructors().FirstOrDefault();
if (constructor != null)
return constructor.GetParameters().Select(a => a.ParameterType).Select(b => this._container.Resolve(b)).ToArray();
return null;
}
}
And in Global.asax:
Initializer.RegisterBytecodeProvider(new EntityInjector(Container));
SessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString("Data Source=(local);Initial Catalog=NHibernate.Autofac;User ID=XXX;Password=XXXX;Pooling=True"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MvcApplication>())
.ExposeConfiguration(config => config.Properties.Add("use_proxy_validator", "false"))
.ExposeConfiguration(config =>
{
new SchemaExport(config).Drop(false, false);
new SchemaExport(config).Create(false, true);
})
.BuildSessionFactory();

Related

How to set up a migration using keyless repositories with HasNoKey() Method?

So i am working on an API that connects with a two Databases with different Db Contexts, one(IdentityContexto) is already working, the other one(EtraducaoContexto) needs repositories that have the context of the DB. So far so good, but when i try to add a migration for EtraducaoContexto, there's an error saying
The entity type 'ControleDeValoresRepositorio' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.
ControleDeValoresRepositorio is one of the Repositories, and here is its code:
using System.Threading.Tasks;
using YellowLingAPI.Models.Entidades;
using YellowLingAPI.Models.Interfaces;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace YellowLingAPI.Data.Repositorio
{
public class ControleDeValoresRepositorio : IControleDeValorRepositorio //controla o repostiro do banco de dados dos valores
{
private readonly EtraducaoContexto contexto;
public ControleDeValoresRepositorio(EtraducaoContexto contexto)
{
this.contexto = contexto;
}
public async Task<decimal> BuscarControleDeValor(int id) //busca o valor dependendo do controle de valores
{
ControleDeValores controle = await contexto.ControleDeValores.FirstOrDefaultAsync(x =>(x.Id.Equals(id)));
var valor =controle.ValorFixo;
return valor;
}
public async Task<decimal> BuscarControleDeValorTipo(string tipo) //busca o valor dependendo do controle de valores
{
ControleDeValores controle = await contexto.ControleDeValores.FirstOrDefaultAsync(x =>(x.TipoValor.Equals(tipo)));
var valor = controle.ValorFixo;
return valor;
}
public async Task Atualizar(ControleDeValores controle)
{
contexto.Update(controle);
await contexto.SaveChangesAsync();
}
public async Task Adicionar(ControleDeValores controle)
{
await contexto.AddAsync(controle);
await contexto.SaveChangesAsync();
}
}
}
i tried updating to 5.0.0 -preview of EntityFrameWorkCore.Tools to use the data annotation [Keyless] nut it causes compatibility error with others packages that a have installed so i have to use the HasNoKey() Method, but i cant figure it out where do i need to use this method, the forums showed being used in the Db Context on ModelCreating, but since it is a Repository it isn't declared there instead the context is given to the Repository. so i am at a lost on what to do.
Here is the code of EtraducaoContexto:
using System;
using YellowLingAPI.Models.Entidades;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace YellowLingAPI.Data
{
public class EtraducaoContexto : DbContext //modelo da database
{
private readonly IConfiguration configuration;
public EtraducaoContexto(DbContextOptions<EtraducaoContexto> options)
: base(options)//IConfiguration configuration
{
//this.configuration = configuration;
// this.Database.SetCommandTimeout(100);
}
public DbSet<Cliente> Cliente { get; set; }
public DbSet<ControleDeValores> ControleDeValores { get; set; }
public DbSet<Documento> Documento { get; set; }
public DbSet<Pagamento> Pagamento { get; set; }
public DbSet<Solicitacao> Solicitacao { get; set; }
public DbSet<Tradutor> Tradutor { get; set; }
public DbSet <Config> Config { get; set; } //
public DbSet <Idiomas> Idiomas { get; set; }//
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.CoefDeadline);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.CoefDatasDisponiveis);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.DeadLineMinima);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.DeadLineMaxima);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.QuantidadeDeLaudas);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.idiomas);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.config);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.controlerepositorio);
modelBuilder.Entity<ControleDeValores>().Ignore(x => x.idiomas);
modelBuilder.Entity<Cliente>().OwnsOne(x => x.Cnpj)
.Property(x => x.Codigo)
.HasColumnName("Cnpj");
modelBuilder.Entity<Cliente>()
.HasIndex(x => x.Email)
.HasName("Unique Email")
.IsUnique();
modelBuilder.Entity<Cliente>().OwnsOne(x => x.Cpf)
.Property(x => x.Codigo)
.HasColumnName("Cpf");
modelBuilder.Entity<Pagamento>().HasOne(x => x.Solicitacao)
.WithOne(x => x.Pagamento)
.HasForeignKey<Pagamento>(x => x.SolicitacaoId);
modelBuilder.Entity <Solicitacao>().HasOne(x => x.Tradutor)
.WithMany(x => x.Solicitacoes)
.HasForeignKey(x => x.TradutorId);
modelBuilder.Entity<Solicitacao>()
.Property(e => e.TipoDeSolicitacao)
.HasConversion(v => v.ToString(), v => (TiposDeSolicitacao)Enum.Parse(typeof(TiposDeSolicitacao), v));
modelBuilder.Entity<Pagamento>()
.Property(e => e.FormaDePagamento)
.HasConversion(v => v.ToString(), v => (FormaDePagamento)Enum.Parse(typeof(FormaDePagamento), v));
modelBuilder.Entity<Pagamento>()
.Property(e => e.StatusDePagamento)
.HasConversion(v => v.ToString(), v => (StatusDePagamento)Enum.Parse(typeof(StatusDePagamento), v));
}
/* protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
Database.SetCommandTimeout(100);
optionsBuilder.UseSqlServer(configuration.GetConnectionString("EtraducaoContexto"));
base.OnConfiguring(optionsBuilder);
}*/
}
}
i was using configuration as a Constructor but it was having an error, so a used an empty one rending configuration useless.
Note that the hole project is pretty big, and i didn't created it from the start i got it already done mid way.

How to create DbContext

below default code in controller working fine
public ProductController(appDbContext parmContext)
{
_context = parmContext;
}
now I want to add DAL and in that, getting error creating object of type appDbContext, what to pass/set for parmContext?
below is the connection in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<appDbContext>(config =>
{
config.UseMySql(Configuration.GetConnectionString("PreSales"));
});
}
Below is the code I want to use
public IEnumerable<ProductStatusMaster> GetProductStatusFRdal()
// here I ant to create object of DBcontext (i.e. _context)
{
try
{
var msm = _context.ProductStatusMaster
.Where(s => s.ActiveYn == 1 )
.OrderBy(s => s.Status)
.ToList();
return msm;
}
catch
{
throw;
}
}
Let me get an answer.
There are 2 ways to realize code you want.
1) Through Controller DI.
2) Through Service Locator pattern (antipattern).
By the code:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<appDbContext>(config =>
{
config.UseMySql(Configuration.GetConnectionString("PreSales"));
});
}
you set up dependency for DbContext and configure connection parameters. So, for the first method you should pass the dependency through controller. Possible code will be like this:
public class MyTestAppController
{
private appDbContext _context;
public MyTestApController(appDbContext externalAppDbContext)
{
_context = externalAppDbContext;
}
public IEnumerable<ProductStatusMaster> GetProductStatusFRdal()
{
try
{
var msm = _context.ProductStatusMaster
.Where(s => s.ActiveYn == 1 )
.OrderBy(s => s.Status)
.ToList();
return msm;
}
catch
{
throw;
}
}
}
2) Using Service Locator pattern
In this case you should use IServiceProvider dependency. Code example like this:
public class MyTestAppController
{
private IServiceProvider _provider;
public MyTestAppController(IServiceProvider provider) => _provider = provider;
public IEnumerable<ProductStatusMaster> GetProductStatusFRdal()
{
var _context = _provider.GetService<appDbContext>();
try
{
var msm = _context.ProductStatusMaster .Where(s => s.ActiveYn == 1 )
.OrderBy(s => s.Status) .ToList(); return msm;
}
catch { throw; }
}
}

Migrating to Automapper 4.2.1 from Automapper 2.2.1

I have a project where I have been using automapper 2.2.1. I upgraded to 4.2.1 and am completely broken. I tried to follow this
by changing all references to Mapper.CreateMap<Source, Target>() to Mapper.Initialize(cfg => { cfg.CreateMap<Source, Target>()}). I'm now broken bad and I cant seem to get it done.I'm getting "System.Reflection.ReflectionTypeLoadException was unhandled by user code exception with Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information", message. What am I missing. Code looks like this:
AutomapperTypeAdapterFactory
public class AutomapperTypeAdapterFactory : ITypeAdapterFactory
{
public AutomapperTypeAdapterFactory()
{
//scan all assemblies finding Automapper Profile
var profiles = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.BaseType == typeof(Profile));
Mapper.Initialize(cfg =>
{
foreach (var item in profiles)
{
if (item.FullName != "AutoMapper.SelfProfiler`2")
cfg.AddProfile(Activator.CreateInstance(item) as Profile);
}
});
}
public ITypeAdapter Create() => new AutomapperTypeAdapter();
}
Department Profile
public class DepartmentProfile : Profile
{
protected override void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Department, DepartmentDto>();
});
}
}
UnityContainer
public static class Container
{
static IUnityContainer _container = new UnityContainer();
static Container()
{
var typeAdapterFactory = _container.Resolve<ITypeAdapterFactory>();
TypeAdapterFactory.SetCurrent(typeAdapterFactory);
}
//other container resolutions....
}
In projects where I've moved away from the static configuration I've defined a few mapping profiles (generally a profile per application layer):
public class MappingProfile : Profile
{
protected override void Configure()
{
CreateMap<Source, Destination>();
}
}
I then configure my IoC (SimpleInjector, in my case) to find all profiles and add them to a single, unified configuration. I then register the MapperConfiguration in my container, as well as the IMapper object created from the same:
public void ConfigureSimpleInjector(IAppBuilder app)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.RegisterAutoMapper();
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.RegisterMvcIntegratedFilterProvider();
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
public static Container RegisterAutoMapper(this Container container)
{
var profiles = typeof(AutoMapperRegistry).Assembly.GetTypes().Where(t => typeof(Profile).IsAssignableFrom(t)).Select(t => (Profile)Activator.CreateInstance(t));
var config = new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
});
container.Register<MapperConfiguration>(() => config);
container.Register<IMapper>(() => container.GetInstance<MapperConfiguration>().CreateMapper());
return container;
}
}
I can then use DI for IMapper (or you could store it statically), as well as the MapperConfiguration for use in LINQ projections.

FluentNHibernate and custom TableNameConvention

FluentNHibernate version 1.3.0.727
I have the following custom TableNameConvention:
public class TableNameConvention : IClassConvention, IClassConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.TableName, Is.Not.Set);
}
public void Apply(IClassInstance instance)
{
instance.Table(instance.EntityType.Name + "s");
}
}
I have the following entity mapping:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Email).Not.Nullable().Length(200);
Map(x => x.FirstName).Length(100);
Map(x => x.LastName).Length(100);
Map(x => x.Password).Not.Nullable().Length(30);
}
}
I'm generating database like this:
var configuration = Fluently.Configure()
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<IEntity>()
.Conventions.Add<TableNameConvention>())
.BuildConfiguration();
var schema = new SchemaExport(configuration);
schema.Drop(false, true);
schema.Create(false, true);
Then generating database User entity table is still generated as User but not Users as I want it to be. It seams that Accept method fails. Is this FluentNHibernate bug?
Remove this code
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.TableName, Is.Not.Set);
}
because it is not needed and maybe even not what you want. FNH has three values internally for each property (e.g. tablename)
value set explicitly in classmap
value set by convention
defaultvalue
and it uses what it finds in this order valueInEffect = explicitValue ?? conventionValue ?? defaultValue

QueryOver<A>().Where(a => a.B.Count() > 0) does not work

I get an exception: Unrecognised method call in epression a.B.Count() when I run:
var query = session.QueryOver<A>()
.Where(a => a.B.Count() > 0)
.List();
The following code works:
var query1 = session.QueryOver<A>().List();
var query2 = query1.Where(a => a.B.Count() > 0);
Any ideas? Thanks.
Edit:
Here is my mappings. I'm using NHibernate 3.1.0.4000:
Models:
public class A
{
public virtual int Id { get; private set; }
public virtual ICollection<B> Bs { get; set; }
}
public class B
{
public virtual int Id { get; private set; }
}
Mappings:
public class AMappings : ClassMap<A>
{
public AMappings()
{
Id(x => x.Id);
HasMany(x => x.Bs).LazyLoad();
}
}
public class BMappings : ClassMap<B>
{
public BMappings()
{
Id(x => x.Id);
}
}
Rest of my code:
class Program
{
static void Main(string[] args)
{
// Create connection string
string connectionString = new System.Data.SqlClient.SqlConnectionStringBuilder()
{
DataSource = #".\r2",
InitialCatalog = "TestNHibernateMappings",
IntegratedSecurity = true
}.ConnectionString;
// Create SessionFactory
ISessionFactory sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration
.MsSql2008.ConnectionString(connectionString)
.ShowSql())
.Mappings(m => m.FluentMappings
.Add(typeof(AMappings))
.Add(typeof(BMappings)))
.ExposeConfiguration(BuildSchema)
.BuildConfiguration()
.BuildSessionFactory();
// Test
var session = sessionFactory.OpenSession();
// This line works OK
var query1 = session.Query<A>()
.Where(a => a.Bs.Count() > 0);
// This line throws exception: Unrecognised method call in epression a.Bs.Count()
var query2 = session.QueryOver<A>()
.Where(a => a.Bs.Count() > 0);
}
static void BuildSchema(Configuration cfg)
{
new SchemaExport(cfg).Create(false, true);
}
}
QueryOver is not LINQ.
Your second code snippet works because it's retrieving ALL THE RECORDS and using LINQ-to-objects in memory.
What you should do is:
session.Query<A>()
.Where(a => a.B.Count() > 0)
.ToList();
or better yet:
session.Query<A>()
.Where(a => a.B.Any())
.ToList();
Query is an extension method, you need to add using NHibernate.Linq;