I have a simple class that looks like this...
public class Item {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int ParentId { get; set; }
public virtual IList<Item> Children { get; private set; }
public Item() {
Children = new List<Item>();
}
}
... where the Id is the primary key and ParentId is the foreign key. When I run this code I get Invalid object name 'ItemToItem'. exception and I can't figure out what's wrong? I seems like NHibernate tries to select from a table called ItemToItem or something like that?
Correct way to self reference
// Class
public class Item
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Item Parent { get; private set; }
public virtual IList<Item> Children { get; set; }
public Item() {
Children = new List<Item>();
}
}
// Map
References(x => x.Parent).Column("ParentId");
HasMany(x => x.Children).Cascade.All().KeyColumn("ParentId");
// Add Item
session.Save(new Item { Description = "Electronics",
Children = {
new Item { Description = "PS2" },
new Item { Description = "XBox" }
}});
// Get Item
var items =
(from c in session.Linq<Item>()
where c.Parent == null
select c).ToList();
Yes. fluent nhibernate is seeing this as a many-many relationship. I dont know off the top of my head how to create the type of relationship you want. you'll probably at least want to set up a member:
public virtual Item Parent{ get; set; }
Related
I have this entity model for a recursively structured category tree:
public class ProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public ICollection<ProductInCategory> ProductInCategory { get; set; }
public ICollection<FrontPageProduct> FrontPageProduct { get; set; } // Nav.prop. to front page product
// Recursive sorting:
public void RecursiveOrder()
{
Children = Children.OrderBy(o => o.SortOrder).ToList();
Children.ToList().ForEach(r => r.RecursiveOrder());
}
}
... and this, supposedly, matching ViewModel:
public class ViewModelProductCategory
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
public bool Checked { get; set; } // Used for assigning a product to multiple categories in Product/Edit
// Nav.props:
public ViewModelProductCategory ParentCategory { get; set; } // Nav.prop. to parent
public ICollection<ViewModelProductCategory> Children { get; set; } // Nav.prop. to children
public IEnumerable<ViewModelProduct> Products { get; set; } // Products in this category
public IEnumerable<ViewModelFrontPageProduct> FrontPageProducts { get; set; }
public string ProductCountInfo { get { return Products?.Count().ToString() ?? "0"; } }
}
When I try to populate the viewmodel, like this:
List<ProductCategory> DbM =
await _context.ProductCategories
.Include(c => c.Children)
.Where(x => x.ParentId == null)
.OrderBy(o => o.SortOrder)
.ToListAsync();
foreach (var item in DbM)
{
VMSelectCategories.Add(
new ViewModelProductCategory{
Id = item.Id,
Children = item.Children,
Title = item.Title
});
}
VisualStudio screams at me that it can't implicitly convert ProductCategory to ViewModelCategory. This happens at Children = item.Children.
Why isn't it working? Can't I have additional properties in the viewmodel that I use unrelated to the original entity model? Like Checked and ProductCountInfo?
In this line:
Children = item.Children,
Children is the ViewModelProductCategory.Children property, which is type ICollection<ViewModelProductCategory>, while item.Children is the ProductCategory.Children property, which is type ICollection<ProductCategory>. They are different types and neither inherits or implements the other so why would you expect to be able to assign an object of one type to a property of the other type? Would you expect this to work:
var list1 = new List<int> {1, 2, 3};
List<string> list2 = list1;
Of course not (I hope) because assigning a List<int> object to a List<string> variable would be silly. What you're trying to do is exactly the same. You need to provide some way to convert from one type to the other and then implement that in your code. An option for that might be like this:
Children = item.Children.Select(pc => MapToViewModel(pc)).ToList(),
where MapToViewModel is a method that you write to create a ViewModelProductCategory and populate its properties from a ProductCategory parameter.
You might also look at using something like AutoMapper.
I Have a problem with the EF.
The realated entity is always null. I didn't get any solutions so far.
Here are the models:
public class Categories
{
public int ID { get; set; }
public string Name { get; set; }
public int AtpID { get; set; }
public virtual ICollection<SubCategories> SubCategories { get; set; }
}
public class SubCategories
{
public int ID { get; set; }
public string Name { get; set; }
public int CategoryID { get; set; }
public string LinkToProducts { get; set; }
}
So, every Categorie has more subcategories.
In the Seed method I populated the database, so I have data:
var categories = new List<Categories>
{
new Categories{Name="Abgasanlage", ID=1},
new Categories{Name="Elektrik",ID=2},
new Categories{Name="Filter", ID=3},
new Categories{Name="Karosserie", ID=4},
new Categories{Name="Kuhlunkg",ID=5}
};
categories.ForEach(s => context.Categories.Add(s));
context.SaveChanges();
var subCategories = new List<SubCategories>
{
new SubCategories{Name="Montageteile", ID=1, CategoryID=1},
new SubCategories{Name="Lamdasonde",ID=2, CategoryID=1},
new SubCategories{Name="Anlasser", ID=3, CategoryID=2},
new SubCategories{Name="Luftfilter", ID=4, CategoryID = 3},
new SubCategories{Name="Ohlfilter", ID=5, CategoryID = 3},
new SubCategories{Name="Sonstige", ID=6, CategoryID = 4},
new SubCategories{Name="Wasserpumpe", ID=7, CategoryID = 5}
};
subCategories.ForEach(s => context.SubCategories.Add(s));
context.SaveChanges();
Altought it seems that evrything is ok, the related entity is always null, even with the Include(), is null.
I tried this way:
Models.Categories entity = db.Categories.Where(m => m.ID == 3)
.Include(m => m.SubCategories)
.FirstOrDefault();
but entity.SubCategories is always null.
with Include also the related entity is null
var setting = (from s in db.Categories.Include("SubCategories")
where s.ID == 3
select s).FirstOrDefault();
In my project I have more related entities, where the lazy loading is working.
Only with these models (Categories and SubCategories) I have the problem.
What I'm doing wrong?
Your problem is on your SubCategories model.You have to fix that as shown below.
Note : use public virtual Categories Categories { get; set; } on it.Hence you didn't do that,EF doesn't know how to fetch the related entities (or navigational property) from the db when you use Include or Lazy loading.And also you need to change CategoryID as CategoriesID.B'cos your model's name is Categories.
public class SubCategories
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("CategoriesID")]
public virtual Categories Categories{ get; set; }//you have to do this
public int CategoriesID { get; set; }
public string LinkToProducts { get; set; }
}
Try this:
public class SubCategories
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("CategoryID")]
public Categories Category { get; set; }
public int CategoryID { get; set; }
public string LinkToProducts { get; set; }
}
You are missing the navigation property in SubCategories class:
public virtual Categories Categories { get; set; }
How do you map to a collection of property but only interested with the last record of the collection.
Say, something like.
public class ItemDTO <-- destination class
{
public string Name { get; set; }
public decimal PricesPrice { get; set; }
}
public class Item <-- Source class
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Price> Prices { get; set; }
}
public class Price <-- Source class
{
public int Id { get; set; }
public int ItemId { get; set; }
public decimal Price { get; set; }
public virtual Item Item { get; set; }
}
Then I have tried something like this, but doesn't seems to be right.
Mapper.CreateMap<Item, ItemDTO>()
.ForMember(dto => dto.PricesPrice, opt => opt.MapFrom(s => s.Prices.LastOrDefault().Price));
EDIT: And then after that I did a Projection because If I use Mapper.Map() It will return the entire result set which is not what I want, I only want the values I needed. So I did something like this:
Project().To<ItemDTO>()
Well, basically, I want something like this:
from item in SomeDbContext.Items
where item.ItemId == 1
select new ItemDTO
{
Name = item.Name,
PricesPrice = item.Prices.LastOrDefault()
}
Can above code be done using AutoMapper?
I think I understand what you are trying to do now.
Try this
from item in SomeDbContext.Items
where item.ItemId == 1
select Mapper.Map<ItemDTO>(item)
or you could use LINQ
SomeDbContext.Items.Where(i=> i.ItemId == 1).Select(Mapper.Map<ItemDTO>)
Both of these will return you a list of ItemDTO object where the ItemId is 1
I'm attempting to bring back a list of objects. This object has an IEnumerable property of a second class. I'm attempting to filter this child list based on a condition.
There are the classes:
public class Parent
{
public int Id { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual IEnumerable<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public int OtherId { get; set; }
public bool Active { get; set; }
public virtual Parent Parent { get; set; }
}
Here is the EF code where I am attempting to get the parents and filter the children:
public IEnumerable<ParentViewModel> GetParents(int otherId)
{
var parents = _databaseContext.Parents
.Include(i => i.Children.Where(child => child.OtherId == otherId));
return parents;
}
When I call this method, I get an ArgumentException, with the message:
The Include path expression must refer to a navigation property defined on the type.
Use dotted paths for reference navigation properties and the Select operator for
collection navigation properties.
Given that the exception mentions using Select, I've tried doing that too:
public IEnumerable<ParentViewModel> GetParents(int otherId)
{
var parents = _databaseContext.Parents
.Where(parent => parent.Active == true)
.Include(parent => parent.Children);
.Select(parent => new
{
Active = parent.Active,
Id = parent.Id,
Children = parent.Children
.Where(child => child.OtherId == propertyId)
.Select(child => new
{
Active = child.Active,
Id = child.Id,
ParentId = child.ParentId,
OtherId = child.OtherId,
Title = child.Title
},
Title = parent.Title
});
return parents;
}
This also blows up, giving me the exception:
The specified type member 'Children' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported.
And that's where I'm all out of ideas! I don't know what I'm doing wrong, but this doesn't feel like it should be as hard as it has been, so I'm guessing that I'm missing something pretty fundamental to Entity Framework.
Ah, ok! I am not familiar with Code First (I am using a generated model in my Project) but I am pretty sure that EF doesn't recognize that these Entities are related. The error Message: " ... and entity navigation properties are supported" tells me that you should define a navigation property (a Relation between these Entities).
public class Parent
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public bool Active { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public int ParentId { get; set; }
public int OtherId { get; set; }
public bool Active { get; set; }
public int ParentId [get;set;}
[ForeignKey("ParentId")]
public virtual Parent Parent { get; set; }
}
I have a mapped model with Nhibernate like this:
public class A
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
/* and other 20 properties... */
}
public class B
{
public virtual long Id { get; set; }
public virtual A ItemA { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Date { get; set; }
}
I would like to create a query (queryover, linq, hql etc... anyway) to get a List<B> and fill ItemA property with just Id and Number properties because I need only this properties (I have lots of properties in A class and I will not use it on my results). Is there any way to do this or should I create a ViewModel? If I need to create a ViewModel, how can I do this with QueryOver?
Thank you!
Yes, you have to create a view model, like following:
public class BViewModel
{
public virtual long Id { get; set; }
public virtual AViewModel ItemA { get; set; }
public virtual string Name { get; set; }
public virtual DateTime Date { get; set; }
}
public class AViewModel
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
}
Now you oculd query it with linq like this:
var listOfB = session.Query<B>()
.Select(b => new BViewModel
{
Id = b.Id,
Name = b.Name,
Date = b.Date,
ItemA = new AViewModel
{
Id = b.ItemA.Id,
Number = b.ItemA.Number,
},
}).ToList();
using QueryOver, you can use JoinAlias to join A with B, and SelectList to retrieve only the properties that you want.
Then use TransformUsing to transform the result into whatever format you want.
Something along the lines of:
A aTemplate = null;
s.QueryOver<B>().
.JoinAlias(() => aTemplate)
.Where (/*whatever conditions*/)
.SelectList(list => list
.Select(() => aTemplate.Id)
.Select(() => aTemplate.Number)
.TransformUsing(Transformers.AliasToBeanTransformer)
.List<A/*or some DTO*/>();