Linq to Entities - Delete / Add related data (many to many relationship) - asp.net

I have places - with related tags:
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; }
many to many relationship.
So for example - a place could have a 'bar' and a 'cafe' tag. (and many places could have those tags also).
I can add places with related tags fine. However, how can I manipulate the tags that are related to places with Linq? (add / delete / edit).
Also - if I try and save a place model, with related tags - the place is updated but not the tags - how could I do this?
place.Tags = SelectedTags
db.Entry(ptvm.place).State = EntityState.Modified;
db.SaveChanges();
Thanks.

You should use micromanagement with entity state of entities only if you know what and why are you doing.
In this case, you should give control of it to entity framework.
db.Places.Attach(place);
context.Entry(place).Collection(p => p.Tags).Load();
foreach(var tag in SelectedTags)
{
place.Tags.Add(tag);
}
db.SaveChanges();
Additional note: You cannot change navigation property ICollection to new collection. You can only add, remove and clear. If you want to remove or clear this collection, you should load it from database before.

Related

How to Handle caching with 3-tier design pattern in a website

Im developing a asp.net small website and thought about using 3-tier design pattern (Gui, BLL, DAL). My main problem is that i feel bit lost with how should i handle the caching right.
1.First, where should the caching be done? Is it in the GUI website or in the BLL?
2.Second, it feels too messy to me, any chance anyone could provide me a simple example of how caching is done in a good way with all 3 parts of the 3tier?
3.Last, do u find okay to use 3tier for my need?
Personally I really like 3-tier structure and I can only recommend it. Let's see a simple example with some minor caching. We focus on the structure now.
Let's suppose we have the following code-first structure.
public class BlogEntry
{
public int Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Blog> Blogs { get; set; }
}
public BlogContext : DbContext
{
public DbSet<Category> Category { get; set; }
public DbSet<BlogEntry> Entry { get; set; }
}
Mind that EF will create the primary and foreign keys in the DB by naming conventions (like "Id"). You can use Db-first model as well, not a problem.
Let's have some DTO objects (see MSDN or wiki) like:
public class CategoryDto
{
// To use in LINQ .Selector()
public Expression<Func<Category, CategoryDto>> Selector = efCategory => new CategoryDto
{
Id = efCategory.Id,
Name = efCategory.Name,
}
public int Id { get; set; }
public int Name { get; set; }
}
Of course categories are not changing often, so we may create some sort of cache for them. The caching in this case is clearly in BLL-level, using Dto objects. Update: This is good only if you have some data that is very unlikely to change but accessed very frequently. Otherwise don't bother with it.
public class MainCache
{
// Make it singleton
// Create some init and a refresh method, watch for thread-safety
public List<CategoryDto> Categories { get; set; }
}
So the controller level can access the cache like this: Update: Now the result of the action itself is cached, see details here. This is a safe solution, the framework does everything.
[HttpGet]
[OutputCache(Duration=10, VaryByParam="none")]
public ActionResult DisplayCategories()
{
// Calling BLL, receiving Dto objects
var model = MainCache.Instance.Categories;
return View(model);
}
Hope, you get it. I think this is a general structure that can be used in various situations. If something is not clear just ask.
Update: Minor code fix, and about caching

asp.net MVC 4 - Many to Many data not updating - Updating Entity Framework with ViewModel Edit View

asp.net mvc 4, Entity Framework 5, SQL Server 2012 Express
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; }
many to many relationship.
There is a view with the following ViewModel associated. In it I can edit place details - and edit what tags are associated with the place (for example, the place might have a 'restaurant' tag and a 'bar' tag - and perhaps I want to add a 'cafe' tag, and remove 'restaurant' tag).
PlacesWithTagsViewModel:
public Place place { get; set; }
public ICollection<Tag> SelectedTags { get; set; }
When the view does an httpost back to controller - I update tags like this:
place.Tags = SelectedTags
db.Entry(ptvm.place).State = EntityState.Modified;
db.SaveChanges();
However, place properties update (eg Name) - but Tags always stay the same.
How can I update tags?
Avoid db.Entry(ptvm.place).State = EntityState.Modified; as it causes conflicts with no updation.
You must use UpdateModel(table object, "model");
Full Example is as belows :
[HttpPost]
public ActionResult PlaceTag(PlacesWithTagsViewModel model)
{
if (ModelState.IsValid)
{
tag tagtest = GetTagById(model.tagid);
tag.name= model.tag.name;
tag.nameplural = model.tag.nameplural;
UpdateModel(tag, "model");
db.SaveChanges();
return RedirectToAction("Index", "Dashboard", new { id = 5 });
}
}
The advantage of UpdateModel is that you have to mention only those fields which you update avoiding those which remain static. In this way you can update your related data with Viewmodel in Edit View.

Creating 2 Relationships Between Two Entities Using Entity Framework Code First

I'm trying to define a one to many relationship, as well as a one to one relationship between the same 2 entities "UserProfile" and "Blog". I think I have succeeded with the following code, however, it results in creating a new column in "Blog" table called "UserProfile_UserId" (FK). I don't understand why it does this.
The relationships in English are:
1. "A UserProfile has many Blogs"
2. "A UserProfile has one main optional (nullable) Blog"
So ultimately I'd like to see a FK from Blog.UserId to UserProfile.UserId
And a nullable FK from UserProfile.BlogId to Blog.Id
And I think that is all... I especially don't want additional columns added by EF.
public class UserProfile
{
[Key]
public int UserId { get; set; }
public int? BlogId { get; set; }
public virtual Blog Blog { get; set; } // This is a user's main blog
public virtual ICollection<Blog> AllUsersBlogs { get; set; }
}
public abstract class Blog
{
[Key]
public int Id { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile User { get; set; }
}
That's pretty tricky thing to make - by default CF puts all the relationships / FK-s on one side. And that's for a reason, because it simplifies things, avoid cyclical references and contradicting 'constraints' on two sides
what often happens is the error reporting that from one FK ir requires
to be of multiplicity '1' and from the other FK it has to be * -
resulting in an exception.
But this works all you want I think - you just have to 'feed it' the data carefully...
public class UserProfile
{
[Key]
public int UserId { get; set; }
[ForeignKey("Blog")]
public int? BlogId { get; set; }
public virtual Blog Blog { get; set; } // This is a user's main blog
public virtual ICollection<Blog> AllUsersBlogs { get; set; }
}
//abstract
public class Blog
{
[Key]
public int Id { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
// [ForeignKey("UserId")]
public virtual UserProfile User { get; set; }
}
In your fluent config...
modelBuilder.Entity<Blog>()
.HasRequired(x => x.User)
.WithMany(x => x.AllUsersBlogs)
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete(false);
And use it like this...
var user = db.UserProfiles.Add(new UserProfile
{
//Blog = mainblog,
AllUsersBlogs = new List<Blog>
{
new Blog{},
new Blog{},
new Blog{},
new Blog{},
new Blog{},
}
});
db.SaveChanges();
var mainblog = new Blog { User = user, };
user.Blog = mainblog;
db.SaveChanges();
Note that for the main blog - you have to explicitly specify the User for your blog now - plus set it as the user's main blog.
That's because you have two different relationships now - one is mandatory (User in the Blog) - and another is the optional main blog.
Anyhow, if this doesn't satisfy your demands (though it looks it
should I think) - then I'd suggest that you let it create things by
default and have FK-s on the Blog side, you lose the BlogId but it
simplifies things a lot.

Referencing the creator of a page blog (the connected user) in my entity

I have an entity for my accessing my pages (pages of a blog).
Here it is:
public class Page
{
[Key]
public int PageId { get; set; }
public string AuthorName { get; set; }
[ForeignKey("AuthorName")]
public virtual MembershipUser Author { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Content { get; set; }
public DateTime? PublishDate { get; set; }
public bool Published { get; set; }
public DateTime LastModified { get; set; }
}
As you can see, I would like to keep a reference to the person who created the page (in fact this is the connected user). So I defined an Author member in my class (of type MembershipUser). I also try to define the foreign key but it doesn't work because there is no primary key in the MembershipUser entity. I cannot add one to this entity because if is an aspnet object.
How can I proceed in order to have a reference to the creator of the page in my Page entity?
Thanks.
You can extend the MembershipUser by creating a CustomMembershipUser that will inherit from MembershipUser.
You can add any fields you want to your CustomMembershipUser, you will then also have to create a table of your own with both the original fields and your extra fields.
Here is some documentation that describes how you can do this.
What ORM are you using?
Anyway, you won't be able to reference a class that is not mapped in your ORM. You could create an entity like SystemUser, map it to a table and reference it at your Page entity. When you log in using Membership, you could query that SystemUser and store it in HttpSession so you can use it later.

Consolidate/Merge data from TWO IEnumerable Lists into ONE

I have following domain objects in my application:
[Serializable]
public class Supplier
{
public virtual string SupplierType { get; set; }
public virtual string SupplierCode { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual string Rating { get; set; }
public virtual string WebsiteURL { get; set; }
public virtual IList<Address> Address { get; set; }
}
[Serializable]
public class CargoSupplier : Supplier
{
public virtual IList<Image> Images { get; set; }
public virtual string OpeningTime { get; set; }
public virtual string ClosingTime { get; set; }
public virtual IList<Product> Products { get; set; }
}
I have to goto two seperate repositories for getting descriptive content (from database) and pricing (from external webservice) and will have two seperate enumerations populated with data (descriptive content & pricing):
IEnumerable<CargoSupplier> cargoSuppliers_Pricing
IEnumerable<CargoSupplier> cargoSuppliers_Content
cargoSuppliers_Content will have all fields populated with data EXCEPT IList<Product> and cargoSuppliers_Pricing will have SupplierType, SupplierCode and IList<Product> fields populated with data. Combination of "SupplierType" amd "SupplierCode" would be the key.
Now I have to merge the content and pricing into one enumeration so I can return IEnumerable<CargoSupplier> cargoSuppliers to my Controllers & Views. What is the best way to merge/consolidate these two lists into one?
Are you looking for Union or am i missing something in the question?
var mergedList = list1.Union(list2).ToList();
remember to import the System.Linq namespace
It sounds like you don't want one list that just contains all the items in either list - it sounds like you need to merge individual items. Something like this:
var query = from pricing in cargoSuppliers_Pricing
join content in cargoSuppliers_Content
on pricing.SupplierCode equals content.SupplierCode
select new CargoSupplier
{
// Copy properties from both objects here
};
Within the "select" part you would take the pricing parts from "pricing" and the content parts from "content", building a CargoSupplier object which has both bits.
Is that what you were after?
Use Enumerable.Concat:
Concatenates two sequences.
Example:
var result = cargoSuppliers_Content.Concat(cargoSuppliers_Pricing);
Seen the Union() extension method? That should do what your after
IEnumerable<CargoSupplier> allCargoSuppliers = cargoSuppliers_Pricing.Union(cargoSuppliers_Content);

Resources