How do I access a view using Fluent NHibernate? - c#

My web app uses half a dozen tables, each of which get populated when a user passes through the system. In order to do stats analysis I've written a database view to flatten these tables into a single view.
The view is working, however, I want to automate some tests around the view creation.
My idea to do this was to create a model/map and repository for the view - with list action only. My current implementation doesn't work.
This is my Repository:
namespace FunctionalTests.SpssView
{
public class SpssRepository
{
private readonly ISessionManager _sessionManager;
public SpssRepository(ISessionManager sessionManager)
{
_sessionManager = sessionManager;
}
public IList<Spss> ListFromSpssView()
{
ICriteria criteria = _sessionManager.GetSession().CreateCriteria(typeof(Spss));
return criteria.List<Spss>();
}
}
}
This is the model class:
namespace FunctionalTests.SpssView
{
public class Spss
{
public virtual String StudentId { get; set; }
public virtual String UPNSCN { get; set; }
...
}
}
And the mapping:
namespace FunctionalTests.SpssView
{
public sealed class SpssMap : ClassMap<Spss>
{
public SpssMap()
{
Id(x => x.StudentId).GeneratedBy.Assigned();
Map(x => x.UPNSCN);
...
}
}
}
I'm not entirely confident in the ID mapping - as it is just read from the view?
This is my test:
[Test]
public void ShouldPopulateAndRetrieveFromSpssView()
{
var mockSessionManager = new Mock<ISessionManager>();
mockSessionManager.Setup(x => x.GetSession()).Returns(_session);
var caseRepository = new CaseRepository(mockSessionManager.Object);
var caseList = caseRepository.ListCases();
Assert.That(caseList.Count, Is.EqualTo(2));
var repository = new SpssRepository(mockSessionManager.Object);
var spssList = repository.ListFromSpssView();
Assert.That(spssList.Count, Is.EqualTo(2));
}
Note the case list code - I put that in there to make sure the db connection was being made. This part of the test passes.
Running select * from spss; returns two results. (I'm using sql server 2005 fwiw)
And because this isn't production code, I created a new folder in my FunctionalTests visual studio project (I mention this, because it seems to me to be one of the main differences between this and my working repositories.) Should this make a difference??
Is it possible to test views like this?
Is there anyway I can see the sql that is being generated?
What am I doing wrong??!?
Thanks :)

Try adding:
public SpssMap()
{
Table("myViewBame"); // ADD THIS
Id(x => x.StudentId).GeneratedBy.Assigned();
Map(x => x.UPNSCN);
...
}
In order to see the generated SQL add this:
.ShowSql()
For example:
Fluently.Configure().Database(
MsSqlConfiguration.MsSql2005
.ConnectionString(
ConfigurationManager.ConnectionStrings["my"].ConnectionString).ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MyClass>())
.BuildSessionFactory();

Related

How to specify a Schema while mapping with DapperExtensions?

I'm trying to get all records from SQL database using DapperExtensions.
But I have a Schema set to other than dbo for some tables. Hence, the table is not recognized from sql query.
For example, a table is in the form [Schema][TableName]. But when I start query, error is thrown like:
Invalid object name 'TableName'.
This is the Model class:
using System;
using Dapper.Contrib.Extensions;
using ImOnTech.Teftis.Core.Models;
using ImOnTech.Teftis.Core.Models.DT_Inspection;
namespace ImOnTech.Teftis.Core.Models.DT_Inspection
{
[Table("DT_Inspection.City")]
public class City
{
This is the function to GetAll records from database:
public async Task<IReadOnlyList<City>> GetAllAsync()
{
var CityList = await Context.Connection.GetListAsync<City>();
Context.Connection.Close();
return CityList.ToList();
}
While mapping your models, be bit more explicit. Mention the Schema explicitly.
Following is an example how to provide various mapping properties.
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
}
public sealed class CustomerMapper : ClassMapper<Customer>
{
public CustomerMapper()
{
Schema("dbo");
Table("Customer");
Map(x => x.CustomerID).Key(KeyType.Identity);
AutoMap();
}
}
Please note that, if your column names and property name in model is same, you do not need to call Map for each property (the way I did above Map(x => x.CustomerID).Key(KeyType.Identity);). Instead, only call AutoMap(); and properties will be automatically mapped.
To make these mappings known to Dapper Extensions, call the following code only once at application startup:
DapperExtensions.DapperExtensions.SetMappingAssemblies(new[] { Assembly.GetExecutingAssembly() });

C# Passing Dapper POCO and Dapper.Fluent EntityMap as a arguments

I am currently trying to setup work with MySQL db via dapper.
I have declared POCO for dapper:
public class DevicesOnTesterDbTable
{
public string UUID { get; set; }
public string DeviceType { get; set; }
public string DeviceAddedAt { get; set; }
public uint Address { get; set; }
}
I also have Dapper.Fluent EntityMap for that table:
public class DevicesOnTesterDbTableMap : EntityMap<DevicesOnTesterDbTable>
{
public DevicesOnTesterDbTableMap()
{
Map(p => p.UUID).ToColumn("devices_on_tester_uuid");
Map(p => p.DeviceType).ToColumn("devices_on_tester_type");
Map(p => p.DeviceAddedAt).ToColumn("devices_on_tester_add_at");
Map(p => p.Address).ToColumn("devices_on_tester_address");
}
}
My database have about ten tables so i have ten pairs of POCO and EntityMap classes for them. So in case of reading i have to perform something like this for each table:
public static List<DevicesOnTesterDbTable> ReadDevices(string server)
{
FluentMapper.Initialize(config =>
{
config.AddMap(new DevicesOnTesterDbTableMap());
});
try
{
using (var mySqlConnection = OpenConnection(server))
{
mySqlConnection.Open();
return mySqlConnection.Query<DevicesOnTesterDbTable>("Select * from power_source_calibration").ToList();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
Now is there a way to pass this pairs into some other method that will perform common operations such as read\write\update etc? Or maybe there is some better way around?
There is a better way around; actually you are doing it wrong.
You do not need to call FluentMapper.Initialize in each of your method like ReadDevices. Ideally, all your mappings (for all entities) should happen only once at the startup of your application.
Following is from here:
Initialize your mapping at the start of your application.
FluentMapper.Initialize(config =>
{
config.AddMap(new InvoiceMap());
});
Also, refer this question which shows how to do it at application startup. Refer Register() method which is static and caller call it once at application startup somewhere.

Duplicating a unity class that is connected to Entity Framework

TIA
Full Disclosure - I am new to ASP.NET web development and I am still learning some of the framework and it's interactions. I will try to guard my statements here and identify ones that I am not sure I am using correctly.
I am working with an open source package that I downloaded called BeYourMarket where I have a payment controller that has a service. I am not sure exactly what this means, but I believe it has something to do with Unity? This service seems to control order related items on the site. It is connected to a table in the database that is being used in the entire website. I believe that Entity Framework is providing the conduit for this.
Here is code snippets of what I am trying to duplicate instead of it managing Orders this duplication will manage Token Orders. The ellipse's are my way of showing that there is code in between.
I suspect there is something I fundamentally do not understand here.
Below is the controller which you can see I duplicated the OrderService
PaymentController.cs
{
[Authorize]
public class PaymentController : Controller
{
...
private readonly IOrderService _orderService;
private readonly IOrderServiceToken _orderServiceToken;
...
}
public PaymentController(
...
IOrderService orderService,
IOrderServiceToken orderServiceToken,)
{
...
_orderService = orderService;
_orderServiceToken = orderServiceToken;
...
}
}
Below are the files/classes I created a duplicate from
BeYourMarket.Service\OrderService.cs
public interface IOrderService : IService<Order>
{
}
public class OrderService : Service<Order>, IOrderService
{
public OrderService(IRepositoryAsync<Order> repository)
: base(repository)
{
}
}
BeYourMarket.Models\Models\Order.cs
{
public partial class Order : Repository.Pattern.Ef6.Entity
{
public Order()
{
this.ListingReviews = new List<ListingReview>();
}
...
public int DataBaseVariable { get; set; }
...
}
}
BeYourMarket.Model\Models\Mapping\OrderMap.cs
public class OrderMap : EntityTypeConfiguration<Order>
{
public OrderMap()
{
// Primary Key
this.HasKey(t => t.ID);
// Properties
...
this.Property(t => t.PaymentPlugin)
.HasMaxLength(250);
// Table & Column Mappings
this.ToTable("Orders");
...
this.Property(t => t.ID).HasColumnName("ID");
...
}
}
}
It might be worth noting that below my DbSet never gets ran. Perhaps I need to do some sort of another Initial setup that was ran when I first launched this package. I have no understanding how to run this 'setup' again.
BeYourMarket.Model\Models\BeYourMarketContext.cs
public BeYourMarket()
: base("Name=DefaultConnection")
{
}
...
public DbSet<Order> Orders { get; set; }
public DbSet<OrderToken> OrderToken { get; set; }
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new OrderMap());
modelBuilder.Configurations.Add(new OrderTokenMap());
}
BeYourMarket.Service\DataCacheService.cs
public class DataCacheService
{
...
private IOrderService OrderService
{
get { return _container.Resolve<IOrderService>(); }
}
private IOrderServiceToken OrderServiceToken
{
get { return _container.Resolve<IOrderServiceToken>(); }
}
...
}
Attached here is an image of the error I get. If I replace the <OrderToken> class in my OrderSErviceToken.cs with any other already created database for instance the <Order> everything works fine. It has something to do with me manually creating all this and missing some link or registration.
enter image description here
Thanks for reading if you were able to stick through it this long.
Chris
The problem was that I overlooked a line I needed to duplicate inside the following file:
-Note the line that I forgot is the line with asterisks.
UnityConfig.cs
.RegisterType<IRepositoryAsync<Order>, Repository<Order>>()
**.RegisterType<IRepositoryAsync<OrderToken>, Repository<OrderToken>>()**
...
.RegisterType<IOrderService, OrderService>()
.RegisterType<IOrderServiceToken, OrderServiceToken>()

AutoMapper TwoWay Mapping with same Property Name

Given these two objects
public class UserModel
{
public string Name {get;set;}
public IList<RoleModel> Roles {get;set;}
}
public class UserViewModel
{
public string Name {get;set;}
public IList<RoleViewModel> Roles {get;set;} // notice the ViewModel
}
Is this the most optimal way to do the mapping, or is AutoMapper capable of mapping Roles to Roles on its own?
App Config
Mapper.CreateMap<UserModel, UserViewModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Mapper.CreateMap<UserViewModel, UserModel>()
.ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.Roles));
Implementation
_userRepository.Create(Mapper.Map<UserModel>(someUserViewModelWithRolesAttached);
Is this the most optimal way to do the mapping, or is AutoMapper capable of mapping Roles to Roles on its own?
If the property names are identical, you should not have to manually provide a mapping:
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Just make sure the inner types are mapped as well (RoleViewModel ↔ RoleModel)
What this means, however, is that if you change a source or destination property name, AutoMapper mappings can fail silently and cause hard to track down problems (e.g., if you changed UserModel.Roles to UserModel.RolesCollection without changing UserViewModels.Roles).
AutoMapper provides a Mapper.AssertConfigurationIsValid() method that will check all of your mappings for errors and catch misconfigured mappings. It's useful to have a unit test that runs with the build that validates your mappings for this kind of problem.
You don't need to map the properties. Just make sure that the property names match and there is a mapping defined between them.
Mapper.CreateMap<UserModel, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserModel>();
Mapper.CreateMap<RoleModel, RoleViewModel>();
Mapper.CreateMap<RoleViewModel, RoleModel>();
Or with the cooler way I just found out:
Mapper.CreateMap<UserModel, UserViewModel>().ReverseMap();
Mapper.CreateMap<RoleModel, RoleViewModel>().ReverseMap();
All the other answers, are much better (which I gave an upvote to each).
But what I wanted to post here is a quick playground that you could copy and past right into LinqPad in C# program mode and play your idea's without messing with your actual code.
Another awesome thing about moving all your conversions into a TyperConverter class is that your conversions are now Unit Testable. :)
Here you will notice that the model and viewmodel are almost identical except for one property. But through this process the right property is converted to the correct property in the destination object.
Copy this code into LinqPad and you can run it with the play button after switching to C# Program mode.
void Main()
{
AutoMapper.Mapper.CreateMap<UserModel, UserViewModel>().ConvertUsing(new UserModelToUserViewModelConverter());
AutoMapper.Mapper.AssertConfigurationIsValid();
var userModel = new UserModel
{
DifferentPropertyName = "Batman",
Name = "RockStar",
Roles = new[] {new RoleModel(), new RoleModel() }
};
var userViewModel = AutoMapper.Mapper.Map<UserViewModel>(userModel);
Console.WriteLine(userViewModel.ToString());
}
// Define other methods and classes here
public class UserModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; }
public string DifferentPropertyName { get; set; }
}
public class UserViewModel
{
public string Name {get;set;}
public IEnumerable<RoleModel> Roles { get; set; } // notice the ViewModel
public string Thingy { get; set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine(string.Format("Name: {0}", Name));
sb.AppendLine(string.Format("Thingy: {0}", Thingy));
sb.AppendLine(string.Format("Contains #{0} of roles", Roles.Count()));
return sb.ToString();
}
}
public class UserModelToUserViewModelConverter : TypeConverter<UserModel, UserViewModel>
{
protected override UserViewModel ConvertCore(UserModel source)
{
if(source == null)
{
return null;
}
//You can add logic here to deal with nulls, empty strings, empty objects etc
var userViewModel = new UserViewModel
{
Name = source.Name,
Roles = source.Roles,
Thingy = source.DifferentPropertyName
};
return userViewModel;
}
}
public class RoleModel
{
//no content for ease, plus this has it's own mapper in real life
}
Result from the Console.WriteLine(userViewModel.ToString());:
Name: RockStar
Thingy: Batman
Contains #2 of roles
Inside the Startup.cs in the Configure() method:
Mapper.Initialize(config => {
config.CreateMap<UserModel, UserViewModel>().ReverseMap();
// other maps you want to do.
});

No persister (NHibernate.MappingException) for ASubClass - Fluently

Before asking here I read all post relative to the subject but I can't find any solution.
I removed all my domain complexity and I'm down to the following simple issue:
I have 2 class (in a domain file Domain.cs) :
public abstract class BaseClass
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class ASubClass : BaseClass
{
public ASubClass() {}
public virtual string prop { get; set; }
}
My Mapping is (In another file Mapping.cs):
public class BaseClassMap : ClassMap<BaseClass>
{
public BaseClassMap ()
{
Id(x => x.ID).GeneratedBy.HiLo("1000");
Map(x => x.Name);
DiscriminateSubClassesOnColumn("Type");
}
}
public class ASubClassMap : SubclassMap<ASubClass>
{
public ASubClassMap ()
{
Map(x => x.prop);
DiscriminatorValue(1);
}
}
First I let NHibernate create the DB for me :
var cfg = new NHibernate.Cfg.Configuration();
Fluently.Configure(cfg)
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(connectionstring)
.ShowSql())
.Mappings(m => m
.FluentMappings.AddFromAssemblyOf<BaseClass>()).BuildConfiguration();
new SchemaExport(cfg).Execute(false,true,false);
This works great, then my unit test is the following :
I build a session Factory within a Repository and use the Session/Transaction to try to save an empty SubClass:
var contract = new ASubClass();
IRepository<ASubClass> repository = new Repository<ASubClass>();
repository.Add(contract); >>> BUG HERE
This in fact bug on the line(inside the repo) : session.Save(contract);
I tried to copy/paste my Mapping.cs into Domain.cs or change the attribute of
.FluentMappings.AddFromAssemblyOf<BaseClass> to .FluentMappings.AddFromAssemblyOf<BaseClassMap>
I also tried to change the hierarchy of subclass by removing the Discriminator and the
DiscriminateSubClassesOnColumn keyword.
Didnt work so far.
Thanks.
You code actually works fine for me.
There must be something wrong with your configuration or your repository or how you use the configuration+repository within your unit tests. I mean, you create a new Repository, does this use the configuration you posted? 100% sure?
Are you using your cfg to build the ISessionFactory to be used by your repository? e.g.
var factory = cfg.BuildSessionFactory();
Maybe check that all mappings are in the same assembly, otherwise add both assemblies.

Categories

Resources