After, many hours of reading outdated documentation on the Internet for managing session and configuration of NHibernate / Fluent Nhibernate I actually got a configuration that works without using XML, my poco's and map files work in the WebProject and I almost got excited.
However; when I move helper class that returns ISessionFactory to the actual layer it should be in, nothing works, no errors, I get an ISession from the Session Factory just no Entites. Please note I am using the property in this class to get my Factory back, self explanatory I know.
Some sort of error would be great to go on here.
CODE:
public class NhibernateSessionFactoryHelper
{
private static ISessionFactory _sessionFactory;
private static string _connectionString =
ConfigurationManager.ConnectionStrings["SqlConnectionString"].ToString();
public static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
_sessionFactory = BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISessionFactory BuildSessionFactory()
{
var cfg =
Fluently.Configure().ProxyFactoryFactory(typeof(ProxyFactoryFactory).AssemblyQualifiedName).Mappings(
m => m.FluentMappings.AddFromAssemblyOf<Category>()).Database(MsSqlConfiguration.MsSql2008.ConnectionString(_connectionString)).Cache(c => c.UseQueryCache());
return cfg.BuildSessionFactory();
}
}
I have ripped out all my Windsor container config to make troubleshooting easier so I have the basic setup as follows.
Web.UI
---> entities (category.cs)
---> mappings (categoryMap.cs)
---> FactoryGoo (NHibernateSessionFactory.cs
CODE For POCO /Entity
public class Category {
public Category() { }
public virtual int CategoryID { get; set; }
public virtual string CategoryName { get; set; }
public virtual string CategoryDescription { get; set; }
public virtual System.Nullable<int> ParentCategoryID { get; set; }
public virtual System.Nullable<int> DisplayOrder { get; set; }
public virtual int Active { get; set; }
public virtual string DateCreated { get; set; }
public virtual string LastUpdated { get; set; }
}
Code for Mapping
public class CategoryMap : ClassMap<Category> {
public CategoryMap() {
Table("Categories");
LazyLoad();
Id(x => x.CategoryID).GeneratedBy.Identity().Column("CategoryID");
Map(x => x.CategoryName).Column("CategoryName").Not.Nullable().Length(50);
Map(x => x.CategoryDescription).Column("CategoryDescription").Not.Nullable();
Map(x => x.ParentCategoryID).Column("ParentCategoryID");
Map(x => x.DisplayOrder).Column("DisplayOrder");
Map(x => x.Active).Column("Active").Not.Nullable();
Map(x => x.DateCreated).Column("DateCreated").Not.Nullable();
Map(x => x.LastUpdated).Column("LastUpdated").Not.Nullable();
}
}
So as I stated earlier, I get my Entity and my data when all of classes live in the same assembly. When I move my SessionFactory to my SessionManagement project or the mapping to the Infrastructure.Data.Mappings project and the entities to the Domain.Entities project nothing works and I gt no error as to why.
Thanks for reading this, I hope I have posted enough for you to get an idea of the setup.
Make sure the category class referenced in the BuildSessionFactory method is referencing the category class in the expected namespace/project. In other words, there may be multiple category classes in different projects.
i'm not sure of course, but I think you meant to link the parent category with your category and that is of the same type? If yes, than use the full blown category object as a property instead of the categoryId, and not use Map() but use fluent References().
Related
I'm using an external library to return data to me, the library has a lot of fields in it and goes quite deep with nested objects.
My class looks something like this;
public class Dto
{
public Dto(Val val)
{
Val = val;
}
[Key]
public int Id { get; set; }
public Val Value{ get; set; }
}
And when trying to save the changes to EF I get the error;
Cannot insert explicit value for identity column in table 'Value' when IDENTITY_INSERT is set to OFF.
When looking through the migrations it appears as if the Primary Key has been set on a few properties within the Value object. I've looked and looked but can't find a way to stop the migration automatically assigning primary keys. I've found stuff such as in the example above using the;
[Key]
attribute and adding bits and pieces into the OnModelCreating override but nothing has came up with a successful result.
Edit to give some more clarity instead of an example.
[DbContext(typeof(SummonerDtoContext))]
[Migration("20180314210242_Migration8")]
partial class Migration8
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.2-rtm-10011")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("LccWebAPI.Models.SummonerDto", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long?>("SummonerId");
b.HasKey("Id");
b.HasIndex("SummonerId");
b.ToTable("Summoners");
});
modelBuilder.Entity("RiotSharp.SummonerEndpoint.Summoner", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("AccountId");
b.Property<long>("Level");
b.Property<string>("Name");
b.Property<int>("ProfileIconId");
b.Property<int>("Region");
b.Property<DateTime>("RevisionDate");
b.HasKey("Id");
b.ToTable("Summoner");
});
modelBuilder.Entity("LccWebAPI.Models.SummonerDto", b =>
{
b.HasOne("RiotSharp.SummonerEndpoint.Summoner", "Summoner")
.WithMany()
.HasForeignKey("SummonerId");
});
#pragma warning restore 612, 618
}
}
}
and my actual data object I'm trying to store;
public class SummonerDto
{
public SummonerDto(Summoner summoner)
{
Summoner = summoner;
}
public int Id { get; set; }
public Summoner Summoner { get; set; }
}
and my context;
public class SummonerDtoContext : DbContext
{
public SummonerDtoContext(DbContextOptions<SummonerDtoContext> options)
: base(options)
{ }
public DbSet<SummonerDto> Summoners { get; set; }
}
I don't have access to modify the inside of the Summoner object itself to add annotations to ignore them as keys.
And the model structure of the Summoner object which I have no access to amend;
public class Summoner : SummonerBase
{
[JsonProperty("profileIconId")]
public int ProfileIconId { get; set; }
[JsonConverter(typeof(DateTimeConverterFromLong))]
[JsonProperty("revisionDate")]
public DateTime RevisionDate { get; set; }
[JsonProperty("summonerLevel")]
public long Level { get; set; }
}
And the base;
public class SummonerBase
{
public Region Region { get; set; }
[JsonProperty("id")]
public long Id { get; set; }
public long AccountId { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
Edit : RESOLVED
After 3 hours of trying to figure this out I've finally managed to solve it, since I don't have access to annotate the models directly, I'd previously tried accessing the Ids through my SummonerDto model. Instead I accecssed them directly and it's worked.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SummonerDto>()
.HasKey(c => c.Id);
modelBuilder.Entity<Summoner>().Property(x => x.Id).ValueGeneratedNever();
modelBuilder.Entity<Summoner>().Property(x => x.ProfileIconId).ValueGeneratedNever();
modelBuilder.Entity<SummonerBase>().Property(x => x.Id).ValueGeneratedNever();
base.OnModelCreating(modelBuilder);
}
If I have the following objects:
public class Application
{
public int ApplicationId { get; set; }
public string Name { get; set; }
public virtual ICollection<TestAccount> TestAccounts { get; set; }
}
public class TestAccount
{
public int TestAccountId { get; set; }
public int ApplicationId { get; set; }
public string Name { get; set; }
public virtual Application Application { get; set; }
}
EF Mapping looks like this:
modelBuilder.Entity<Application>()
.HasMany(a => a.TestAccounts)
.WithRequired(t => t.Application)
.WillCascadeOnDelete(false);
In one part of my code I want to retrieve data for Application and have
it return TestAccount data.
In another part of my code I want to retrieve data for Application and
have it NOT return TestAccount data.
Is there a way I can make this happen with LINQ or some other way?
This question has already been answered here: Disable lazy loading by default in Entity Framework 4.
Basically, in the constructor of your DbContext, just add this:
this.Configuration.LazyLoadingEnabled = false;
I hope this helps.
EDIT
Also, if you want to know how to load it manually later, it should be a simple matter of using Include() like this:
var query = context.Application.Include(x => x.TestAccounts).ToList()
I'm having a trouble do the following mapping:
Domain (simplified version):
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual ModelDto Model { get; set; }
}
ViewModel:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
In the Controller, when I do the Mapping I'm loosing the Make_ID from the Dropdownlist of the View:
public virtual ActionResult Create(CreateAdViewModel adViewModel)
{
if (ModelState.IsValid)
{
var adDto = Mapper.Map<CreateAdViewModel, CreateAdDto>(adViewModel);
_adService.CreateAd(adDto);
}
return RedirectToAction(MVC.Home.Index());
}
The mapping is:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>()
Thanks.
As you have mentionned, the Ad need to know the Model_Id and to set it into the Model
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
You also need from the other mapping side to let the Dto know where to get the Model id.
Mapper.CreateMap<Ad, CreateAdDto>()
.ForMember(dest => dest.Model_Id, opt => opt.MapFrom(src => src.Model.Id}));
The code above is not secure because a validation to see if Model is null should be added.
For the rest of your code, you seem to do it right. The section with Entity Framework requires you to attach because the entity Model already exist, otherwise, EF would insert this entity to the database.
CreateAdDto doesn't have a Make navigation property or a Make_Id property.
Solution found after some research:
ViewDomain:
public class CreateAdViewModel
{
// Primary properties
public int Kms { get; set; }
public int Make_Id { get; set; }
public int Model_Id { get; set; }
// Navigation properties
public IEnumerable<SelectListItem> MakeList { get; set; }
public IEnumerable<SelectListItem> ModelList { get; set; }
}
DTO:
public class CreateAdDto
{
// Primary properties
public int Kms { get; set; }
public int Model_Id { get; set; }
// Navigation properties
//public virtual ModelDto Model { get; set; }
}
Domain:
public class Ad
{
// Primary properties
public int Kms { get; set; }
// Navigation properties
public virtual Model Model { get; set; }
}
Viewmodel -> Dto Mapping:
Mapper.CreateMap<CreateAdViewModel, CreateAdDto>();
Dto -> Domain Mapping:
Mapper.CreateMap<CreateAdDto, Ad>()
.ForMember(dest => dest.Model, opt => opt.MapFrom(src => new Model { Id = src.Model_Id }));
Atention:
To achieve this with Entity Framework, I had to attach first the Model Entity to the Context and then Ad the new Ad:
public void CreateAd(CreateAdDto adDto)
{
var adDomain = Mapper.Map<CreateAdDto, Ad>(adDto);
_modelRepository.Attach(adDomain.Model);
_adRepository.Add(adDomain);
_adRepository.Save();
}
Hope this is the best practice.
BTW I would like to have some opinions regarding this aproach.
Thanks.
Based on what is I see in your question, I suggest a simple approach. Your application is medium scale. You should very carefully about maintainability,my experience say.So try to create a simple an strain forward approach for yourself like below approach:
I can describe all layer in detail but with notice to title of your question I prefer describe only Model(bussiness Ojbect) layer:
Good! As you can see PM.Model include:
Tow sub folders contain our ViewModels and in root of Library we have a .tt file contain Entity framework Objects (POCO classes) and we have a Mapper folder(Since that i dont like use autoMapper or third party like this :) ).
You can see IListBox interface in Domain layer. I put all ListBox container to this interface.
I hope current approach useful for you but finally I suggest remove one of this layers DTO or ViewModel, because in the future will be very complex.
Good luck
Do you aware about cost of these mapping?! You have 2 layers mapping (before arrived to Entity framework) for an simple insert.We can do more complex CRUD(s) in less than 2 layers mapping.
How to think about maintainability of this code?
Please keep DRY,KISS,SOLID conventions in your mind and top of your everyday work.
Good luck
I'm building an ASP.NET MVC3 website with an code first database and have the following question:
Is it possible to make an instance of MyDbContext class with an additional argument set which will be used for filtering the results of calls to mydbcontext.
I want to use this for restricting the resultset to the current user that is logged in on my asp.net mvc website.
Any directions would be great!
I don't see why that should be a problem. Something like this should work:
public class Northwind : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind : Northwind
{
public IQueryable<Products> GetFilteredProducts(string userRole)
{
return Products.Where(product => product.UserRole == userRole);
}
}
Update
To make it impossible for your MyDbContext to be abused, you could put all your database code and models into a separate project/assembly. Then make your DbContext an internal class (instead of public), then create a public class (FilteredDbContext) that wraps your MyDbContext and exposes methods that allow you to only grab the data your allowed to see. Then in your main assembly (your web project), you will only be able to use FilteredDbContext.
So, for example:
internal class Northwind : DbContext // note: internal class
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind // note: does not inherit from `Northwind`
{
private readonly _dbContext = new Northwind();
public IQueryable<Products> GetProducts(string userRole)
{
return _dbContext.Products.Where(product => product.UserRole == userRole);
}
}
If Northwind and FilteredNorthwind are in a separate assembly from your web app, you can instantiate only FilteredNorthwind from your web app.
Update 2
If you use a ViewModel, then your web app can't get back to the list of all products for a category because you extract out only the properties you need (and only the properties the user is allowed to see).
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public IEnumerable<Products> GetProducts(string userRole)
{
return _dbContext.Products
.Where(product => product.UserRole == userRole)
.Select(product => new ProductViewModel
{
Id = product.Id,
Name = product.Name,
Price = product.Price
};
}
You could make a layer above and hide the generated one and create a your own DbContext which derives from the generated MyDbContext. Just a wild guess but it seems logical to me and so you can implement your own argument set and still use the generated one.
I would do this:
public interface IUserContext {
string User { get; set; }
}
public class Database : DbContext {
public IDbSet<Product> Products { get; set; }
}
public class AuthorizedDatabase {
private readonly Database _database;
private readonly IUserContext _userContext;
public AuthorizedDatabase(Database database, IUserContext userContext) {
_database = database;
_userContext = userContext;
}
private bool Authorize<TEntity>(TEntity entity) {
// Some code here to look at the entity and the _userContext and decide if it should be accessible.
}
public IQueryable<Product> Products {
get {
return _database.Products.Where(Authorize);
}
}
}
This would allow me to cleanly abstract the actual logic around the authorization (and your IUserContext interface can be as simple or complex as required to suite your exact needs.)
To ensure that the user is unable is circumvert this protection using a navigation property (Product.Category.Products, for example.) you might need to turn off lazy loading and explicitly load the required related entities.
Have a look at this post from ADO.NET Team Blog for ideas: loading related entities
I'm trying to get a web app working based on the S#arp Architecture. At the moment I have a the below code for my entity.
[Serializable]
public abstract class EventBase : Entity
{
#region Properties
[DomainSignature]
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual AgeRange Ages { get; set; }
public virtual int Rating { get; set; }
public virtual decimal Price { get; set; }
public virtual string PhoneNumber { get; set; }
public virtual string EmailAddress { get; set; }
public virtual string Website { get; set; }
public virtual EventState State { get; set; }
#endregion
protected EventBase() {}
protected EventBase(string name, string description)
{
// ReSharper disable DoNotCallOverridableMethodsInConstructor
Name = name;
Description = description;
Price = 0;
State = EventState.New;
// ReSharper restore DoNotCallOverridableMethodsInConstructor
}
}
This is mapped using Fluent NHibernate as follows
public class EventBaseMap : AutoMap<EventBase>
{
public EventBaseMap()
{
Id(x => x.ID).WithUnsavedValue(-1).GeneratedBy.Identity();
Component<AgeRange>(x => x.Ages, m =>
{
m.Map(x => x.From).TheColumnNameIs("AgeFrom");
m.Map(x => x.To).TheColumnNameIs("AgeTo");
});
JoinedSubClass<Music>("EventId", sub =>
{
sub.Map(x => x.Headliner);
});
}
}
I created a very simple repository using the very useful S#arp base repository classes.
public interface IEventRepository : INHibernateRepositoryWithTypedId<EventBase, int>
{
List<EventBase> FindByName(string searchPhase);
}
public class EventRepository : NHibernateRepository<EventBase>, IEventRepository
{
public List<EventBase> FindByName(string searchPhase)
{
return Session.Linq<EventBase>().Where(x => x.Name == searchPhase).ToList();
}
}
I can create entities in the db and return all records. When I try to test the FindByName method I get the below error.
NHibernate.QueryException: could not
resolve property: Name of:
Model.Events.EventBase
Does anyone have any ideas? Is it a problem with my mapping?
Thanks,
This is using the Auto-mapping feature. I thought you only explicitly map properties that you want to override or that don't meet the conventions?
If I add an explicit mapping this solves the issue but I am still not sure why.