I have a LinkTable named: BlogsBlogPosts that relates two other tables named: Blogs and BlogPosts together, I want the link table to appear in the list of entities and There will be two one to many relationships defined on the linktable am I correct to define it like this?:
public class BlogsBlogPostConfiguration : EntityTypeConfiguration<BlogsBlogPost>
{
public BlogsBlogPostConfiguration()
{
this.HasRequired(bb => bb.Blog)
.WithMany(b => b.BlogsBlogPosts)
.HasForeignKey(bb =>bb.BlogID);
this.HasRequired(bb => bb.BlogPost)
.WithMany(bp => bp.BlogsBlogPosts)
.HasForeignKey(bb => bb.BlogPostID);
}
}
Yes. The only question which remains for me is: What about the primary key property of BlogsBlogPost? Two options:
You either have a distinct key property like public int BlogsBlogPostID { get; set; } or public int ID { get; set; } in this entity. In this case combinations of (BlogID, BlogPostID) don't have to be unique.
Or you have a composite key consisting of (BlogID, BlogPostID). You would have to mark this with the [Key] annotation or define it in Fluent API:
this.HasKey(bbp => new { bbp.BlogID, bbp.BlogPostID });
Now combinations of (BlogID, BlogPostID) must be unique. This is more of a "many-to-many relationship with additional data in the link table" type of relationship.
Both models are possible. Which to use depends on your business needs.
Related
I have problem with updating entites that have many-to many relationship. Below my User and category class:
public class User : IEntity
{
[Key]
public virtual long Id { get; set; }
private ICollection<Category> _availableCategories;
public virtual ICollection<Category> AvailableCategories
{
get { return _availableCategories ?? (_availableCategories = new List<Category>()); }
set { _availableCategories = value; }
}
}
public class Category : IEntity
{
[Key]
public long Id { get; set; }
/// <summary>
/// Full name or description of a category
/// </summary>
[StringLength(255)]
public string FullName { get; set; }
}
This is code snippet from my repository
public override void Edit(User user)
{
var dbUser = _context.Users.Include(x => x.AvailableCategories)
.Single(x => x.Id == user.Id);
var categories = _context.Categories;
dbUser.AvailableCategories.Clear();
foreach (var cat in user.AvailableCategories)
{
dbUser.AvailableCategories.Add(cat);
}
_context.Entry(dbUser).State = EntityState.Modified;
}
However the categories don't get updated. What EF does is insert empty rows into category table and sets relations to this new rows with user.
How can I update User so that I change only categories that already exist in the database?
User that I pass to Edit method has AvailableCategories with only Ids set (rest of properties are empty).
When you're doing something like posting back M2M relationships, you either must post the full object, as in every single property on those objects, or simply post a list of ids and then use those to query the associated objects back from the database. Otherwise, Entity Framework understands your purpose to be to update the properties on the objects as well, in this case with empty values.
Obviously the first option is quite unwieldy, so the second way is the preferred and standard way. Generally, for this, you'd want to use a view model so you could have a property like the following, that you would post into:
public List<long> SelectedCategories { get; set; }
But, if you insist on using the entity directly, you can get much the same result by simply doing:
var selectedCategories = user.AvailableCategories.Select(m => m.Id)
Once you have the ids:
var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));
And then finally set that on your user:
dbUser.AvailableCategories = newAvailableCategories;
I notice you are also adding the user.AvailableCategories directly into dbUser.AvailableCategories. I've noticed when binding back complex objects from an MVC view that DB Entities are no longer attached to the DbContext. If you look at the entity, you can verify by checking dbContext.Entry(cat).State is "detached" (or something unexpected) I believe.
You must query those entities back out of the dbContext (possibly by using the returned cat.Id's). Or otherwise manually set the entities as "unchanged". And then add those "non-detached" items into dbUser.AvailableCategories. Please see Chris's answer as it shows with specific code how to get this done.
Also, I might use a linking entity. Possibly something like this:
public class UserCategory
{
public User User {get;set;}
public Category Category {get;set;}
}
And add it to DB context. Also, drop the linking lists in your current User and Category class. This way you can manipulate the UserCategory class (and DbSet) to manage your many-to-many relationship.
Take a look the classes below:
public class Produt
{
public virtual int id { get; set; }
public virtual string name { get; set; }
[ScriptIgnore]
public virtual Unit unit { get; set; }
}
public class Unit
{
public virtual int id { get; set; }
public virtual string name { get; set; }
public virtual IList<Produt> produts { get; set; }
}
After this, the mappings:
public partial class ProdutMap : ClassMap<Produt>
{
public ProdutMap()
{
Id(x => x.id).GeneratedBy.Identity();
Map(x => x.name).Length(100).Not.Nullable();
References(x => x.unit, "idUnit").Cascade.All().LazyLoad();
}
}
public partial class UnitMap : ClassMap<Unit>
{
public UnitMap()
{
Id(x => x.id).GeneratedBy.Identity();
Map(x => x.name).Length(100).Not.Nullable();
HasMany(x => x.produts).Cascade.All().KeyColumns.Add("idUnit").LazyLoad();
}
}
Now, imagine that I want to execute this query:
SELECT produt.id, produt.name, unit.name FROM Produt, Unit WHERE produt.idUnit = unit.id
with nhibernate? How to do? Something help?
P.S. The [ScriptIgnore] is because I had problems with circular references. My classes are not only these. This is just an example.
Just fetch Products
The simplest way to do this is to just fetch a list of Products. Product already contains all of the information you need because it has a reference to Unit.
// NOTE: This fetches ALL products. You really should limit this.
var products = session.Query<Product>();
foreach (var product in products)
Console.WriteLine("Product: {0}, Unit: {1}", product.Name, product.Unit.Name);
On each iteration of this loop, if product.Unit points to a unit that NHibernate has not fetched yet, NHibernate will lazily execute another query to fetch that unit.
Sometimes you are not able to use lazy loading - perhaps you need to close the NHibernate session before iterating over the results. Also, for performance it would be better to reduce the number of round-trips to the database. We can fix these problems by changing our query like so:
var products = session.Query<Product>().Fetch(x => x.Unit);
Flatten your results
If for some reason you need flattened result objects where you don't have to dig through nested objects to get the data you need, you can use "projections" to do this. With LINQ, this looks like:
var productInfos = session.Query<Product>().Select(x => new
{
ProductId = x.Id,
ProductName = x.Name,
UnitName = x.Unit.Name
});
This is also useful if you need to limit the columns returned by NHibernate - for example, if one of the column contains huge BLOBs that you want to avoid fetching.
Besides LINQ, NHibernate has several different ways to execute queries: native SQL, HQL, Criteria, and QueryOver. For Criteria or QueryOver, the AliasToBean result transformer will help you when executing these types of queries. See this related question for an example using AliasToBean in a Criteria query: NHibernate - Only retrieve specific columns when using Critera queries?
I have an Entity Framework 5 Code First model with a many to many relationship
i.e.
Public class Product
{
public int ProductId {get;set;}
public ICollection<Category> Categories {get;set;}
}
Public class Category
{
public int CategoryId {get;set;}
public ICollection<Product> Products {get;set;}
}
I'm creating the actual relation in fluent, thus;
modelBuilder.Entity<Product>()
.HasMany(p => p.Categories)
.WithMany(c => c.Products)
.Map(m =>
{
m.ToTable("ProductsToCategories");
m.MapLeftKey("Products_ProductId");
m.MapRightKey("ProductCategories_ProductCategoryId");
});
Now when I retrieve my data is is retrieving Product, and Product as a bunch of Categories, BUT each Category also has a bunch of products in it as well, and so it recurses around.
The problem is that this is causing havoc when I then serialise it to JSON for use by the front end (I'm using KnockOut, but that is kind of irrelevant).
I've tried turning Lazy loading off, and when I get my products I use an include;
db.Products.Include("Categories").ToList()
but this is still then performing the recursive gathering of products within each Category.
Any ideas?
Regards
You can also use business objects instead of using database objects directly. In this manner you only reference from on side, say :
Public class Product
{
public int ProductId {get;set;}
public IList<Category> Categories {get;set;}
}
Public class Category
{
public int CategoryId {get;set;}
}
You have reference cyclic between Product and Category.
In other words Product have a relation to Category and Category have a relation to Product.
So, what you need to do is to delete one of these relations.
I would do someting like that :
var categoriesGraph = db.Categories.Include("Products").ToList();
var data = categoriesGraph.Select(c => new
{
c.CategoryId,
Products = c.Products.Select(p = > new {
p.ProductId,
CategoriesID = p.Categories.Select(c => c.CategoryId).ToArray(),
// don't add the categories.
}).ToArray()
}).ToArray();
I hope it helps.
You can tell Json.Net to just ignore circular references:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
If you want references to existing data, then you look at fix 2 in this article
I'm trying to map the following:
A user can be connected to another user. The user class has a 'Connections' property which a collection of UserConnection. A UserConnection has two references to User, one for the requester (he who initiated the connection) and one for the requestee (he who is the target of the connection).
Using FluentNHibernate I want to load all UserConnections for when loading the User. A User's connections are those where the user's id matches either the requesterId or the requesteeId.
public class User
{
//....
public virtual IList<UserConnection> Connections { get; set; }
}
public class UserConnection
{
//....
public virtual User Requester { get; set; }
public virtual User Requestee { get; set; }
}
This feels like a many-to-many relationship, but I can't quite wrap my head around it. Can anyone help?
EDIT:
My current User mapping (below) obviously only matches on one column...
HasMany(x => x.Connections).Inverse().Key(k => k.Columns.Add("Requestee_id")).Fetch.Select().Not.LazyLoad();
EDIT 2:
I have considered this approach but I would prefer to avoid that if possible...
Isn't it 1-many? user has a collection of Connections? But each connection has 1 RequesterUser or 1 RequesteeUser?
This would be a self referencing relation, mapping something like below.
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("Users");
DynamicUpdate();
References<User>(x => x.Requester)
.Cascade.None()
.NotFound.Ignore();
References<User>(x => x.Requestee)
.Cascade.None()
.NotFound.Ignore();
HasMany<User>(x => x.RequesterConnections)
.Cascade.AllDeleteOrphan()
.Inverse()
.KeyColumn("RequesterId");
HasMany<User>(x => x.RequesteeConnections)
.Cascade.AllDeleteOrphan()
.Inverse()
.KeyColumn("RequesteeId");
}
}
I could be totally wrong, not been using NH long but maybe it will spark some ideas.
I think this is how i would try and do it.
Obviously you don't have a single collection of Connections but you could get them both pretty easily.
Hope this helps.
Kohan
I am trying to figure out something with EF4 Code Only. If i use TPH and i wanted to change a saved Person to Instructor or vice versa, how would i accomplish this. My POCO classes:
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Instructor : Person
{
public DateTime? HireDate { get; set; }
}
public class Student : Person
{
public DateTime? EnrollmentDate { get; set; }
}
public class Admin : Person
{
public DateTime? AdminDate { get; set; }
}
public class PersonConfiguration : EntityConfiguration<Person>
{
public PersonConfiguration()
{
this.HasKey(u => u.PersonId).Property(u => u.PersonId).IsIdentity();
MapHierarchy()
.Case<Person>(p => new
{
p.PersonId,
p.FirstName,
p.LastName,
PersonCategory = 0
})
.Case<Instructor>(i => new
{
i.HireDate,
PersonCategory = 1
})
.Case<Student>(s => new
{
s.EnrollmentDate,
PersonCategory = 2
})
.Case<Admin>(a => new
{
a.AdminDate,
PersonCategory = 3
}).ToTable("Person");
}
}
Lets say i have a person:
var person1 = new Person { FirstName = "Bayram", LastName = "Celik" };
context.People.Add(person1);
context.SaveChanges();
Later on i want to make this person an admin. How would i accomplish this.
var person = context.People.FirstOrDefault();
Admin test = person as Admin; // wont work
following will change the HireDate column but my discriminator field PersonCategory is still 0. So it is still not an Admin type as far as EF concerns
Admin admin = new Admin();
admin.PersonId = person.PersonId;
admin.AdminDate = DateTime.Now;
context.ObjectContext.Detach(person);
context.People.Attach(admin);
var customerEntry = context.ObjectContext.ObjectStateManager.GetObjectStateEntry(admin);
customerEntry.SetModified();
customerEntry.SetModifiedProperty("AdminDate");
You can't ever change the type of an object. You can't do it in C#, and the EF doesn't work around this. This is a fundamental principle of OOP.
Instead, you need to fix your model design. As I wrote a while back:
One of the mental barriers that you have to get over when designing a good object relational mapping is the tendency to think primarily in object oriented terms, or relational terms, whichever suits your personality. A good object relational mapping, though, incorporates both a good object model and a good relational model. For example, let’s say you have a database with a table for People, and related tables for Employees and Customers. A single person might have a record in all three tables. Now, from a strictly relational point of view, you could construct a database VIEW for employees and another one for customers, both of which incorporate information from the People table. When using a one VIEW or the other, you can temporarily think of an individual person as "just" an Employee or "just" a Customer, even though you know that they are both. So someone coming from this worldview might be tempted to do an OO mapping where Employee and Customer are both (direct) subclasses of Person. But this doesn’t work with the data we have; since a single person has both employee and customer records (and since no Person instance can be of the concrete subtype Employee and Customer simultaneously), the OO relationship between Person and Employee needs to be composition rather than inheritance, and similarly for Person and Customer.