I have a problem with the Ninject.
My binding rules:
this.Bind<ISphinxQLServer>().To<SQLServer>();
this.Bind<IMySQLServer>().To<SQLServer>();
this.Bind<ISQLLogger>().To<StandardSQLLogger>()
.InRequestScope();
this.Bind<DatabaseConnections>()
.ToMethod(x => ConnectionFactory.GetConnections())
.InRequestScope();
this.Bind<SQLServer>().ToSelf()
.InRequestScope()
.WithConstructorArgument("connections", Kernel.Get<DatabaseConnections>())
.WithConstructorArgument("logger", Kernel.Get<ISQLLogger>());
Where
SQLServer, ISphinxQLServer and IMySQLServer are:
public class SQLServer: ISphinxQLServer, IMySQLServer
{
public DatabaseConnections Connections { get; internal set; }
public ISQLLogger Logger { get; internal set; }
public SQLServer(DatabaseConnections connections)
{
this.Connections = connections;
}
public SQLServer(DatabaseConnections connections, ISQLLogger logger)
{
this.Connections = connections;
this.Logger = logger;
}
}
I want that each user request to my asp.net mvc site creates a single SQLServer, a single ISQLLogger and a single DatabaseConnections. But my solution dont work. What am I doing wrong? =(
You don't need to specify the WithConstructorArgument. Resolving the parameters to the constructors of your injected objects is part of what Ninject does for you. So the definitions should look more like this:
this.Bind<SQLServer>()
.ToSelf()
.InRequestScope();
this.Bind<ISphinxQLServer>()
.ToMethod( x => x.Kernel.Get<SQLServer>() );
this.Bind<IMySQLServer>()
.ToMethod( x => x.Kernel.Get<SQLServer>() );
this.Bind<ISQLLogger>()
.To<StandardSQLLogger>()
.InRequestScope();
this.Bind<DatabaseConnections>()
.ToMethod(x => ConnectionFactory.GetConnections())
.InRequestScope();
Related
I am building a multitenant application using ASP.Net Core and AutoFac. Each tenant will be assigned it's own database (e.g. TenantA will use DatabaseA, TenantB will use DatabaseB).
I want to use AutoFac to resolve which database to connect to at runtime.
I set things up in Startup.cs:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<TenantConnectionStringProvider>()
.As<IConnectionStringProvider>()
.WithParameter((paramInfo, componentContext) => paramInfo.Name == "tenantKey",
(paramInfo, componentContext) => userSession2.TenantKey)
.InstancePerLifetimeScope();
services.AddDbContext<TenantDataContext>(options => {
var connectionStringProvider = new TenantConnectionStringProvider(userSession2.TenantKey);
options.UseSqlServer(connectionStringProvider.ConnectionString); });
containerBuilder.Populate(services);
}
Also:
public interface IConnectionStringProvider
{
string ConnectionString { get; set; }
}
public class TenantConnectionStringProvider : IConnectionStringProvider
{
private string _tenantKey;
public TenantConnectionStringProvider(string tenantKey)
{
_tenantKey = tenantKey;
ConnectionString = GetConnectionString(_tenantKey);
}
public string ConnectionString { get; set; }
public string GetConnectionString(string tenantKey)
{
if (tenantKey == "64C15227-8F9E-4AE4-8620-1986F051FB8F")
{
return "TenantA Connection String";
}
return "TenantB Connection String";
}
}
Unfortunately the TenantDbContext is always set with the TenantA connection string. This happens even if I step through the code and manually set the return value of the tenantKey parameter so that it returns the TenantB connection string.
How can I get around this? How can I have Autofac resolve and create instances of DBContext based on the appropriate connection string for the current tenant?
We have ASP MVC web project. After reading a lot of articles and discussions here in stackoverflow about the correct architechture we have decided to go with the following one, although there is not only one correct way of doing things this is the way we have decided, but we still have some doubts.
We are publishing this here not only to be helped but also to show what we have done in case it is helpful to somebody.
We are working in ASP .NET MVC project, EF6 Code first with MS SQL Server.
We have divided the project into 3 main layers that we have separate into 3 projects: model, service and web.
The model creates the entities and setup the DataContext for the database.
The service make the queries to the data base and transform those entities into DTOs to pass them to the web layer, so the web layer doesn't know anything about the database.
The web uses AutoFac for the DI (dependency Injection) to call the services we have in the service layer and obtain the DTOs to transform those DTOs into Model Views to use them in the Views.
After reading a lot of articles we decided not to implement a repository pattern and unit of work because, in summary, we have read the EF acts as a unit of work itself. So we are simplifying things a little here.
https://cockneycoder.wordpress.com/2013/04/07/why-entity-framework-renders-the-repository-pattern-obsolete/
This is the summary of our project. Now I'm going to go through every project to show the code. We are going to show only a couple of entities, but our project has more than 100 different entities.
MODEL
Data Context
public interface IMyContext
{
IDbSet<Language> Links { get; set; }
IDbSet<Resources> News { get; set; }
...
DbSet<TEntity> Set<TEntity>() where TEntity : class;
DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
}
public class MyDataContext : DbContext, IMyContext
{
public MyDataContext() : base("connectionStringName")
{
}
public IDbSet<Language> Links { get; set; }
public IDbSet<Resources> News { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
}
}
Here is how we declare the entities
public class Link
{
public int Id{ get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
SERVICES
These are the generic classes we use for all the services.
As you see we use the DTOs to get data from the web layer. Also we connect to the database using Dbset = Context.Set()
public interface IService
{
}
public interface IEntityService<TDto> : IService where TDto : class
{
IEnumerable<TDto> GetAll();
void Create(TDto entity);
void Update(TDto entity);
void Delete(TDto entity);
void Add(TDto entity);
void Entry(TDto existingEntity, object updatedEntity);
void Save();
}
public abstract class EntityService<T, TDto> : IEntityService<TDto> where T : class where TDto : class
{
protected IClientContext Context;
protected IDbSet<T> Dbset;
protected EntityService(IClientContext context) { Context = context; Dbset = Context.Set<T>(); }
public virtual IEnumerable<TDto> GetAll()
{
return Mapper.Map<IEnumerable<TDto>>(Dbset.AsEnumerable());
}
public virtual void Create(TDto entity)
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
Dbset.Add(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Update(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
public virtual void Delete(TDto entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
Dbset.Remove(Mapper.Map<T>(entity));
Context.SaveChanges();
}
public virtual void Add(TDto entity)
{
Dbset.Add(Mapper.Map<T>(entity));
}
public virtual void Entry(TDto existingEntity, object updatedEntity)
{
Context.Entry(existingEntity).CurrentValues.SetValues(updatedEntity);
}
public virtual void Save()
{
Context.SaveChanges();
}
}
We declare the DTOs in this project (this is a very simple example so we don't have to put all the code here):
public class LinkDto
{
public int Id { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public bool Active { get; set; }
}
Then one of our services:
public interface ILinkService : IEntityService<LinkDto>
{
IPagedList<LinkDto> GetAllLinks(string searchTitle = "", bool searchActive = false, int pageNumber = 1, int pageSize = 10);
LinkDto FindById(int id);
LinkDto Test();
}
public class LinkService : EntityService<Link, LinkDto>, ILinkService
{
public LinkService(IClientContext context) : base(context) { Dbset = context.Set<Link>(); }
public virtual IPagedList<LinkDto> GetAllLinks(bool searchActive = false, int pageNumber = 1, int pageSize = 10)
{
var links = Dbset.Where(p => p.Active).ToPagedList(pageNumber, pageSize);
return links.ToMappedPagedList<Link, LinkDto>();
}
public virtual LinkDto FindById(int id)
{
var link = Dbset.FirstOrDefault(p => p.Id == id);
return Mapper.Map<LinkDto>(link);
}
public LinkDto Test()
{
var list = (from l in Context.Links
from o in Context.Other.Where(p => p.LinkId == l.Id)
select new OtherDto
{ l.Id, l.Title, l.Url, o.Other1... }).ToList();
return list;
}
}
As you see we use AutoMapper (version 5 which has changed a little) to transform from Entities to DTOs the data.
One of the doubts we have is if the use of "Dbset.Find" or "Dbset.FirstOrDefault" is correct and also if the use of "Context.Links" (for any entity).
WEB
FInally the web project where we receive the DTOs and transform those DTOs into ModelViews to show in our views.
We need to call, in the Global.asax Application_Start, AutoFac to do the DI so we can use our services.
protected void Application_Start()
{
...
Dependencies.RegisterDependencies();
AutoMapperBootstrapper.Configuration();
...
}
public class Dependencies
{
public static void RegisterDependencies()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EfModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class ServiceModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(Assembly.Load("MyProject.Service")).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces().InstancePerLifetimeScope();
}
}
public class EfModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType(typeof(MyDataContext)).As(typeof(IMyContext)).InstancePerLifetimeScope();
}
}
As you see we also call AutoMapper to configure the different maps.
Then in our controllers we have this.
public class LinksController : Controller
{
private readonly ILinkService _linkService;
public LinksController(ILinkService linkService)
{
_linkService = linkService;
}
public ActionResult Index()
{
var links = _linkService.GetAllLinks();
return View(links.ToMappedPagedList<LinkDto, LinksListModelAdmin>());
}
...
public ActionResult Create(LinksEditModelAdmin insertedModel)
{
try
{
if (!ModelState.IsValid) return View("Create", insertedModel);
var insertedEntity = Mapper.Map<LinkDto>(insertedModel);
_linkService.Create(insertedEntity);
return RedirectToAction("Index");
}
catch (Exception ex)
{
throw ex;
}
}
}
Well, this is it...I hope this can be useful for somebody...and also I hope we can have a little help with the questions we have.
1) Although we are separating database from the web project we do need a reference in the web project to initialize the database and also to inject dependencies, is this correct?
2) Is it correct the approach we have done having our Entities->DTOs->ViewModels? It's a little more work but we have everything separated.
3) In the Service project, when we need to reference a different entity than the main one we are using in the service, is it correct to call Context.Entity?
For example, if we need to retrieve also data from the News entity in the links service, is it correct to call "Context.News.Where..."?
4) We do have a little problem with Automapper and EF proxy, because when we call "Dbset" to retrieve data, it gets a "Dynamic proxies" object so Automapper can't find the proper map so, in order to work, we have to set ProxyCreationEnabled = false in the DataContext definition. This way we can get an Entity in order to map it to the DTO. This disables LazyLoading, which we don't mind, but is this a correct approach or there is a better way to solve this?
Thanks in advance for your comments.
For Question no. 2
Entities->DTOs->ViewModels? is good approach
because you are doing the clean separation, the programmer can work together with ease.
The person who design ViewModels, Views and Controllers don't have to worry about the service layer or the DTO implementation because he will make the mapping when the others developpers finish their implementation.
For Question no. 4
When the flag ProxyCreationEnabled is set to false, the proxy instance will not be created with creating a new instance of an entity. This might not be a problem but we can create a proxy instance using the Create method of DbSet.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create();
}
The Create method has an overloaded version that accepts a generic type. This can be used to create an instance of a derived type.
using (var Context = new MydbEntities())
{
var student = Context.StudentMasters.Create<Student>();
}
The Create method just creates the instance of the entity type if the proxy type for the entity would have no value (it is nothing to do with a proxy). The Create method does not add or attach the entity with the context object.
Also i read some where if you set ProxyCreationEnabled = false the child element will not loaded for some parent object unless Include method is called on parent object.
We are using AutoMapper extensively in our ASP.NET MVC web applications with the AutoMapViewResult approach set out in this question. So we have actions that look like this:
public ActionResult Edit(User item)
{
return AutoMapView<UserEditModel>(View(item));
}
This creates hidden failure points in the application if the requested mapping has not been configured - in that this is not a compile time fail.
I'm looking at putting something in place to test these mappings. As this needs to test the actual AutoMapper configuration I presume this should be done as part of integration testing? Should these tests be structured per controller or per entity? What about the possibility of automatically parsing all calls to AutoMapView?
Note that we are already testing that the AutoMapper configuration is valid using AssertConfigurationIsValid, it is missing mappings that I want to deal with.
If your controller action looked like this:
public AutoMapView<UserEditModel> Edit(User item)
{
return AutoMapView<UserEditModel>(View(item));
}
Then you can pretty easily, using reflection, look for all controller actions in your project. You then examine the action parameter types and the generic type parameter of your AutoMapView action result. Finally, you ask AutoMapper if it has a type map for those input/output models. AutoMapper doesn't have a "CanMap" method, but you can use the IConfigurationProvider methods of FindTypeMapFor:
((IConfigurationProvider) Mapper.Configuration).FindTypeMapFor(null, typeof(User), typeof(UserEditModel);
Just make sure that's not null.
[Test]
public void MapperConfiguration()
{
var mapper = Web.Dto.Mapper.Instance;
AutoMapper.Mapper.AssertConfigurationIsValid();
}
You can use the AssertConfigurationIsValid method. Details are on the automapper codeplex site (http://automapper.codeplex.com/wikipage?title=Configuration%20Validation)
Strictly speaking you should be writing a test to validate the mapping before you write a controller action that depends on the mapping configuration being present.
Either way, you can use the Test Helpers in the MvcContrib project to check the action method returns the expected ViewResult and Model.
Here's an example:
pageController.Page("test-page")
.AssertViewRendered()
.WithViewData<PortfolioViewData>()
.Page
.ShouldNotBeNull()
.Title.ShouldEqual("Test Page");
I do something like this.
using System.Linq;
using System.Reflection;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
public class SampleDto
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Sample
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string LoginId { get; set; }
}
public class AutomapperConfig
{
public static void Configure()
{
Mapper.Initialize(cfg => cfg.AddProfile<ViewModelProfile>());
}
}
public class ViewModelProfile : Profile
{
protected override void Configure()
{
CreateMap<SampleDto, Sample>();
}
}
[TestClass]
public class AutoMapperTestsSample
{
public AutoMapperTestsSample()
{
AutomapperConfig.Configure();
}
[TestMethod]
public void TestSampleDtoFirstName()
{
#region Arrange
var source = new SampleDto();
source.FirstName = "Jim";
//source.LastName = "Bob";
var dest = new Sample();
dest.FirstName = "FirstName";
dest.LastName = "LastName";
dest.LoginId = "LoginId";
#endregion Arrange
#region Act
AutoMapper.Mapper.Map(source, dest);
#endregion Act
#region Assert
Assert.AreEqual("Jim", dest.FirstName);
Assert.AreEqual(null, dest.LastName);
Assert.AreEqual("LoginId", dest.LoginId);
#endregion Assert
}
[TestMethod]
public void TestSampleDtoLastName()
{
#region Arrange
var source = new SampleDto();
//source.FirstName = "Jim";
source.LastName = "Bob";
var dest = new Sample();
dest.FirstName = "FirstName";
dest.LastName = "LastName";
dest.LoginId = "LoginId";
#endregion Arrange
#region Act
AutoMapper.Mapper.Map(source, dest);
#endregion Act
#region Assert
Assert.AreEqual(null, dest.FirstName);
Assert.AreEqual("Bob", dest.LastName);
Assert.AreEqual("LoginId", dest.LoginId);
#endregion Assert
}
/// <summary>
/// This lets me know if something changed in the Dto object so I know to adjust my tests
/// </summary>
[TestMethod]
public void TestSampleDtoReflection()
{
#region Arrange
var xxx = typeof(SampleDto);
#endregion Arrange
#region Act
#endregion Act
#region Assert
Assert.AreEqual(2, xxx.GetRuntimeFields().Count());
Assert.AreEqual("System.String", xxx.GetRuntimeFields().Single(a => a.Name.Contains("FirstName")).FieldType.ToString());
Assert.AreEqual("System.String", xxx.GetRuntimeFields().Single(a => a.Name.Contains("LastName")).FieldType.ToString());
#endregion Assert
}
}
I'm struggling to get Fluent NHibernate to play nice with SQL Server's Geospatial types. I want to store a geographic point in my Place class, but I keep getting an NHibernate configuration error when I run my ASP.NET MVC app:
Method 'SetParameterValues' in type 'NHibernate.Spatial.Type.GeometryType' from
assembly 'NHibernate.Spatial, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' does not have an implementation.
Update: This is caused by an out-dated NHibernate.Spatial DLL. Referencing the latest version (2.2+) solves the problem. Kudos to psousa for leading me to a solution.
Place class:
using System;
using GisSharpBlog.NetTopologySuite.Geometries;
using NHibernate.Validator.Constraints;
namespace MyApp.Data.Entities
{
public class Place
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual Point Location { get; set; }
}
}
Fluent Place Mapping:
using MyApp.Data.Entities;
using FluentNHibernate.Mapping;
using NHibernate.Spatial.Type;
namespace MyApp.Data.Mappings
{
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
ImportType<GisSharpBlog.NetTopologySuite.Geometries.Point>();
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location)
.CustomType(typeof(GeometryType));
}
}
}
Fluent NHibernate Configuration:
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringKey))
.ShowSql()
.Dialect("NHibernate.Spatial.Dialect.MsSql2008GeographyDialect,NHibernate.Spatial.MsSql2008"))
.ExposeConfiguration(BuildSchema)
.Mappings(x => x.FluentMappings.AddFromAssembly(typeof(UserMap).Assembly)
.Conventions.AddFromAssemblyOf<ColumnNullabilityConvention>());
You're using a Geography dialect but using a CustomType of Geometry on your mapping. You should use a custom type of Geography. Something like:
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location).CustomType(typeof(MsSql2008GeographyType)); //for SQL2008
}
}
Also, there's something else that you may need to do. If your spatial column has an SRID different from 0 (zero), and if you want to skip NH xml mappings, you'll need to declare a custom type like this:
public class Wgs84GeographyType : MsSql2008GeographyType
{
protected override void SetDefaultSRID(GeoAPI.Geometries.IGeometry geometry)
{
geometry.SRID = 4326;
}
}
And then use it on your mapping:
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location).CustomType(typeof(Wgs84GeographyType));
}
}
UPDATE:
You should be referencing NHibernate.Spatial.MsSql2008.dll, and I would advise you to use the strongly-typed Dialect method in your database configuration.
.Dialect<MsSql2008GeographyDialect>()
I'm developing a website with ASP.NET MVC, NHibernate and Fluent Hibernate and getting the error "no session or session was closed" when I try to access a child object.
These are my domain classes:
public class ImageGallery {
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual IList<Image> Images { get; set; }
}
public class Image {
public virtual int Id { get; set; }
public virtual ImageGallery ImageGallery { get; set; }
public virtual string File { get; set; }
}
These are my maps:
public class ImageGalleryMap:ClassMap<ImageGallery> {
public ImageGalleryMap() {
Id(x => x.Id);
Map(x => x.Title);
HasMany(x => x.Images);
}
}
public class ImageMap:ClassMap<Image> {
public ImageMap() {
Id(x => x.Id);
References(x => x.ImageGallery);
Map(x => x.File);
}
}
And this is my Session Factory helper class:
public class NHibernateSessionFactory {
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory {
get {
if(_sessionFactory == null) {
_sessionFactory = Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(MyConnString))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ImageGalleryMap>())
.ExposeConfiguration(c => c.Properties.Add("hbm2ddl.keywords", "none"))
.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession() {
return SessionFactory.OpenSession();
}
}
Everything works fine, when I get ImageGallery from database using this code:
IImageGalleryRepository igr = new ImageGalleryRepository();
ImageGallery ig = igr.GetById(1);
But, when I try to access the Image child object with this code
string imageFile = ig.Images[1].File;
I get this error:
Initializing[Entities.ImageGallery#1]-failed to lazily initialize a collection of role: Entities.ImageGallery.Images, no session or session was closed
Someone know how can I fix this?
Thank you very much!
Edit
My GetById method is:
public ImageGallery GetById(int id) {
using(ISession session = NHibernateSessionFactory.OpenSession()) {
return session.Get<ImageGallery>(id);
}
}
Presumably, your GetById is closing the session. This could be explicit, or via a using statement.
A better approach to session management is the Open Session in View pattern. NHibernate sessions are cheap to create, so create one at the start of each request, and close it at the end.
// in global.asax.cs
public void Application_Start()
{
BeginRequest += delegate {
CurrentSessionContext.Bind( sessionFactory.OpenSession());
};
EndRequest += delegate {
var session = sessionFactory.GetCurrentSession();
if (null != session) {
session.Dispose();
}
CurrentSessionContext.Unbind(sessionFactory);
};
}
// in your NHibernateSessionFactory class
public static ISession OpenSession() {
return SessionFactory.GetCurrentSession();
}
Using a DI container, we can have the session injected with instances scoped per-request.
// ninject example
Bind<ISession>()
.ToMethod( ctx => sessionFactory.GetCurrentSession() )
.InRequestScope();
The session used to get the ig must be alive when accessing ig.Images[1]
I usually do this by instantiating the session before all repository calls, passing the session reference to the the repository constructor and using that reference inside the repository class
I'm not sure if this applies here, but this is (or at least used to be) a common problem in Java MVC frameworks. It usually happens when create the session inside your action. When your action finishes executing, the session object goes out of scope and the session is closed/flushed. Then when the view tries to render the collection of objects you retrieved, it tries to load the lazy-loaded collection using a session that's been closed.
If you're using dependency injection in your project you could have your DI framework instantiate the session for you and pass it as constructor argument into your controller. You could also specify that the DI container should scope the NHibernate session to be the same as your HTTP Request. This will keep the session alive until the request ends (view has finished rendering).
I've used the Autofac as our DI container on a project and work and have been very happy with it.