Linq to Entities - Get list of all unique related entities - asp.net-mvc

asp.net mvc 4, Entity Framework 5, SQL Server 2012 Express, Code First
I have a Place model:
public virtual int PlaceID { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual string Name { get; set; }
and a related Tag model:
public virtual int TagID { get; set; }
public virtual string Name { get; set; }
public virtual string NamePlural { get; set; }
public virtual ICollection<Place> Places { get; set; }
they have a many to many relationship.
I have a List places - and I would like to create a List tags - populated with every (unique) tag associated with every place in 'places'.
For example, one place might have 'restaurant' and 'pub' tag, another 'pub' and 'bar', and another 'shop' and 'cafe'.
I would like the List to contain one of each of the tags with these names:
Bar, Cafe, Restaurant, Pub, Shop
How can I do this in Linq?
Thanks.

If you want all the unique tag names you can do:
places.SelectMany(x => x.Tags).Select(x => x.Name).Distinct()
if the two instances of the same tag are the same object then you can just do
places.SelectMany(x => x.Tags).Distinct();
if they are different objects then you can do
places.SelectMany(x => x.Tags).GroupBy(x => x.TagId).Select(g => g.First());
UPDATE after new comment.
Add ToList() to then end to convert the result into a list.
places.SelectMany(x => x.Tags).Select(x => x.Name).Distinct().ToList();
places.SelectMany(x => x.Tags).Distinct().ToList();
places.SelectMany(x => x.Tags).GroupBy(x => x.TagId).Select(g => g.First()).ToList();

Related

Entity framework one to many - empty virtual collection

I have this code first database
public partial class SystemWarning
{
[Key]
public long Id { get; set; }
/// <summary>
/// id of the admin that created the entry
/// </summary>
public string CreatedById { get; set; }
public virtual AspNetUser CreatedBy { get; set; }
public string AcknowledgedById { get; set; }
public virtual AspNetUser AcknowledgedBy { get; set; }
}
public partial class AspNetUser
{
public AspNetUser()
{
SystemWarnings = new HashSet<SystemWarning>();
}
[Key]
public string Id { get; set; }
public virtual ICollection<SystemWarning> SystemWarnings { get; set; }
}
And linked together as follows
modelBuilder.Entity<AspNetUser>()
.HasMany(e => e.SystemWarnings)
.WithOptional(e => e.CreatedBy)
.HasForeignKey(e => e.CreatedById).WillCascadeOnDelete(false);
For reasons that escape me at the moment, when I extract my AspNetUser, the SystemWarnings collection is always empty, even if there are systemwarnings that are linked to the AspNetUser in the database.
I have a bunch of these 1-n links, even on the same object, and the other links remain non empty, and for now I'm not seeing the difference.
#edit: here's that other object for comparison:
public partial class UserProfile: BaseObject
{
public Guid Id { get; set; }
public string OwnerId { get; set; }
public virtual AspNetUser Owner { get; set; }
}
and the mapping
modelBuilder.Entity<AspNetUser>()
.HasMany(e => e.OwnedUserProfiles)
.WithOptional(x => x.Owner)
.HasForeignKey(x => x.OwnerId);
Seems the same to me, except that the SystemWarning has a long as Id with identity specs (forgot to post that before - it's defined as follows)
modelBuilder.Entity<SystemWarning>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
And that SystemWarning actually has another reference to the AspNetUser that I now added (AcknowledgedBy(Id)) which is linked as follows
modelBuilder.Entity<AspNetUser>()
.HasMany(e => e.SystemWarnings)
.WithOptional(e => e.AcknowledgedBy)
.HasForeignKey(e => e.AcknowledgedById).WillCascadeOnDelete(false);
I'll post the SQL trace soon...
Well, that edit did the trick.. both links from SystemWarning to AspNetUser are mapped on the SystemWarnings collection.. no wonder that's not working the way it's supposed to.
Note to myself.. map every 1:n to a different collection or there'll be trouble.

How to rename db columns of linking table (many to many realiton) in ASP .NET MVC EF (code first)

I need to create this db context:
public class Contact
{
public int ID { get; set; }
public virtual List<Contact> Employers { get; set; }
public virtual List<Contact> Staff { get; set; }
}
EF creates the table ContactContacs with columns Contact_ID and Contact_ID1. How (where) can I rename this context columns?
In your DbContext's OnModelCreating() method:
modelBuilder.Entity<Contact>().HasMany(x => x.Employers).WithMany().Map(x =>
{
x.ToTable("ContactEmployers");
x.MapLeftKey("ContactId");
x.MapRightKey("EmployerId");
});
modelBuilder.Entity<Contact>() => The model you're configuring
HasMany(x => x.Employers) => Indicates it's a many-to-X relationship with a navigation property
WithMany() => Configures it as a many-to-many relationship without a navigation property on the other side
Map() => Configuring the table and columns for the IDs

Using HTML.Grid to display child object

I have an ASP.NET MVC application that is using a single view to display the properties and children (with their properties) of a model entity.
My model looks something like this:
public class Market
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IList<EmailAddress> EmailAddresses { get; set; }
}
public class EmailAddress
{
public virtual int ID { get; set; }
public virtual int MarketID { get; set; }
public virtual string Email { get; set; }
}
On the view, I want to use a table to display the list of related email addresses. To do this, I am using Html.Grid.
<%= Html.Grid(Model.EmailAddresses).Columns( column =>
{
column.For(x => x.Email + Html.Hidden("ID", x.ID)).Encode(false).Sortable(false);
})
.Attributes(style => "width:100%")
.Attributes(id => "emailGrid")
.Empty("There are no Email Addresses set up") %>
However, when I do this, the hidden ID is that of the parent entity Market, not that of the EmailAddress.
How do I remedy this?
It seems it could be a bug in the WebGrid. Have you tried renaming your ID field in the EmailAddress class, e.g. EmailID and pass that to the WebGrid and see if it displays correctly?
This works for me, could it be that you have something wrong in the filling of your model?
Since you're using the lambda expression for the Column.For() method, the x parameter is re referring to a single email. I think you mean to refer to the Model, not a single email ID
Instead of doing x.ID, I think you just want Model.ID

Code First Mapping for Entity Framework Hierarchy

I have a model that looks like this:
public class Category
{
public string Id { get; set; }
public string Description { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
public ICollection<Product> Products { get; set; }
}
With a database table that looks like
Categories
Id (PK varchar(5))
Description (nvarchar(50))
ParentId (FK varchar(5))
But Im stumped when it comes to setting up the mapping
modelBuilder.Entity<Category>()
.HasMany(x => x.Children)
.WithMany(x => x.Children)
.Map(m =>
{
m.ToTable("Categories");
m.MapLeftKey(x => x.Id, "Id");
m.MapRightKey(x => x.Id, "ParentId");
});
I can see why the mapping fails (StackOverflowException), but am unsure as to how to fix it. Any help would be greately appreciated.
This is using the latest release of EF (4.1?).
Thanks!
Why do you map many-to-many relation on the same navigation property? That is completely wrong. First your table obviously expect one-to-many relation. Even if you need many-to-many relation you cannot use the same navigation property for that.
Just try:
modelBuilder.Entity<Category>()
.HasMany(x => x.Children)
.WithOptional(y => y.Parent)
.Map(m => m.MapKey("ParentId"));

EF Code First composite key mapping

I'm using the CTP 5 of EF 4 and Code first.
I don't get a many-many relation working with a composite key on one side.
modelBuilder.Entity<Item>()
.HasMany(i => i.Categories)
.WithMany(o => o.Items)
.Map(
mc =>
{
mc.ToTable("ItemCategories");
mc.MapLeftKey(i => i.Id, "ItemId");
mc.MapRightKey(o => o.TemplateID, "TemplateId");
mc.MapRightKey(o => o.ItemId, "ItemId");
}
);
So instead of having a simple key for Categories in my matching table, I've got a composite one. And one part of the composite key is also the key for the Item type,
which seems to be the problem here.
I get the error: "Each property name in a type must be unique. Property name 'ItemId' was already defined."
How can I configure EF to use a composite key in this case?
Of course you cannot have 2 columns with the same name within one table. This will work:
modelBuilder.Entity<Item>()
.HasMany(i => i.Categories)
.WithMany(c => c.Items)
.Map(m =>
{
m.MapRightKey(i => i.Id, "ItemId");
m.MapLeftKey(c => c.ItemId, "ItemId2");
m.MapLeftKey(c => c.TemplateId, "TemplateId");
});
public class Category
{
[Key]
public string ItemId { get; set; }
[Key]
public string TemplateId { get; set; }
public string Value { get; set; }
public ICollection<Item> Items { get; set; }
}
public class Item
{
public string Id { get; set; }
public string Name { get; set; }
public ICollection<Category> Categories { get; set; }
}
The mapping table ItemCategories is not a POCO, but used for mapping those 2 as
shown.
It has SQL columns
Id (own primary key)
ItemId (FK to Item table and Category table)
TemplateId (FK to Category table)
and another ID column which maps to a different table.
In my opinion the only difference here to "normal" many-many scenario is the composite key
in the ItemCategories table, which builds the relation to the Category table.

Resources