LINQ: With Many to many relationship, select from list - asp.net

I've having trouble figuring out how to select based on a list in a many to many relationship.
I've created with entities-framework the following entities and many to many relationship (please correct me if I'm going about this wrong) :
public class Foo {
public int FooID { get; set; }
public string FooName { get; set; }
public virtual ICollection<FooBar> Foo_Bar { get; set; }
}
public class Bar {
public int BarID { get; set; }
public string BarName { get; set; }
public virtual ICollection<FooBar> Foo_Bar { get; set; }
}
public class FooBar {
public int FooBarID{ get; set; }
public virtual int BarID { get; set; }
public virtual int FooID { get; set; }
}
In my code my controller will receive a list of Foo and I need to find all the Bar with those foo ( both with only and with any )
I'm at a loss for where to start really... this is all I've come up with:
public PartialViewResult SearchAnyBar(List<Foo> foos) {
List<FooBar> foobars = _db.FooBar.Select(fb => fb).ToList<FooBar>();
List<Bar> searchedBars = new List<Bar>();
foreach (Foo f in foos)
{
foreach (FooBar fXb in foobars)
{
if (fXb.FooID == f.FooID)
{
searchedBars.Add(_db.Bar.Where(b => b.BarID == fXb.BarID).FirstOrDefault());
}
}
}
return PartialView("The View", searchBars);
}
This works for the grabbing any Bar however:
I'm pretty positive there's a much better way of doing this, is there a way to select based on a list instead of going about 2 foreach loop?
I'm not sure how to go about getting a list of Foos where the Foo has ALL the Bars and not just ANY.

Remove FooBar class.
Just create a public virtual ICollection<Foo> Foos {get;set;} in you Bar class
and a public virtual ICollection<Bar> Bars {get;set;} in your Foo Class
This will create a many to many relationship (with a relation table named [Foo-Bar] or something like that in your db... but who minds, you will be using objects).
then
any query :
var listOfFooId = <a list of Foo>.Select(m => m.FooId).ToList;
return _db.Bar.Where(m => m.Foos.Any(x => listOfFooId.Contains(x.FooId)));
not sure I understood well the "only" and "any", but if you have problems with the other query... ask.

Untested, but it looks like you just need a join here... Joining on all the Bars off of all the FooBars joined to the Foos passed in, right?
public PartialViewResult SearchAnyBar(List<Foo> foos) {
var bars = (from f in foos
join fb in _db.FooBars on f.Id equals fb.FooId
join b in _db.Bars on fb.BarId equals b.BarId
select b).ToList();
return PartialView("The View", bars);
}

Related

A circular reference was detected while serializing entities with one to many relationship

How to solve one to many relational issue in asp.net?
I have Topic which contain many playlists.
My code:
public class Topic
{
public int Id { get; set; }
public String Name { get; set; }
public String Image { get; set; }
---> public virtual List<Playlist> Playlist { get; set; }
}
and
public class Playlist
{
public int Id { get; set; }
public String Title { get; set; }
public int TopicId { get; set; }
---> public virtual Topic Topic { get; set; }
}
My controller function
[Route("data/binding/search")]
public JsonResult Search()
{
var search = Request["term"];
var result= from m in _context.Topics where m.Name.Contains(search) select m;
return Json(result, JsonRequestBehavior.AllowGet);
}
When I debug my code I will see an infinite data because Topics will call playlist then playlist will call Topics , again the last called Topic will recall playlist and etc ... !
In general when I just use this relation to print my data in view I got no error and ASP.NET MVC 5 handle the problem .
The problem happens when I tried to print the data as Json I got
Is there any way to prevent an infinite data loop in JSON? I only need the first time of data without call of reference again and again
You are getting the error because your entity classes has circular property references.
To resolve the issue, you should do a projection in your LINQ query to get only the data needed (Topic entity data).
Here is how you project it to an anonymous object with Id, Name and Image properties.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you have a view model to represent the Topic entity data, you can use that in the projection part instead of the anonymous object
public class TopicVm
{
public int Id { set;get;}
public string Name { set;get;}
public string Image { set;get;}
}
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new TopicVm
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you want to include the Playlist property data as well, you can do that in your projection part.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image,
Playlist = x.Playlist
.Select(p=>new
{
Id = p.Id,
Title = p.Title
})
});
return Json(result, JsonRequestBehavior.AllowGet);
}

ASP.NET MVC: How to Insert Data Into Multiple Tables?

I have two tables (NPG_Chemical and NPG_Chemical_Synonym):
public partial class NPG_Chemical
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(256)]
public string Chemical { get; set; }
}
public partial class NPG_Chemical_Synonym
{
[Key]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_Synonym_ID { get; set; }
[ForeignKey("NPG_Chemical_ID")]
[Column(TypeName = "numeric")]
public decimal NPG_Chemical_ID { get; set; }
[StringLength(512)]
public string Synonym { get; set; }
}
In the NPG_ChemicalController I have something like:
[HttpPost]
public ActionResult Create(NPG_ChemicalViewModel model)
{
using (var context = new NPG_Model())
{
var chemical = new NPG_Chemical();
chemical.Chemical = model.NPG_Chemical.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.NPG_Chemical_Synonym.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
context.SaveChanges();
}
return View();
}
and NPG_ChemicalViewModel:
namespace NPG_Administrative_Utility.Models
{
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
}
When I try to create a view based on NPG_ChemicalViewModel, it shows:
Can any one help me on this?
You'll need a view model. At the simplest, you can just do something like:
public class NPG_ChemicalViewModel
{
public NPG_ChemicalViewModel()
{
NPG_Chemical = new NPG_Chemical();
NPG_Chemical_Synonym = new NPG_Chemical_Synonym();
}
public NPG_Chemical NPG_Chemical { get; set; }
public NPG_Chemical_Synonym NPG_Chemical_Synonym { get; set; }
}
Then, change your action to accept this:
public ActionResult Create(NPG_ChemicalViewModel model)
In your view, you would generate the individual properties like:
#Html.EditorFor(m => m.NPG_Chemical.Chemical)
However, it's far better to only include the properties on your view model that you want to be edited:
public class ChemicalViewModel
{
public string Chemical { get; set; }
public string Synonym { get; set; }
}
Then, in your action, you just map this posted data where it should go:
var chemical = new NPG_Chemical();
chemical.Chemical = model.Chemical;
context.NPG_Chemical.Add(chemical);
var synonym = new NPG_Chemical_Synonym();
synonym.Synonym = model.Synonym;
synonym.NPG_Chemical_ID = chemical.NPG_Chemical_ID;
context.NPG_Chemical_Synonym.Add(synonym);
That said, there's some significant issues with your code here. First, it looks like you're dealing with a one-to-one or one-to-many relationship here between Chemical and Synonym, but right now, you have no foreign keys being utilized. You should add a navigation property to your synonym class:
[ForeignKey("NPG_Chemical_ID")]
public NPG_Chemical Chemical { get; set; }
That tells Entity Framework that you have a relationship and among other things allows it to automatically fill in IDs as necessary. For example, with that, you could now simply do:
synonym.Chemical = chemical;
Instead of directly referencing the ID. That way, if the id is autogenerated or otherwise unknown before saving, the relationship will still be preserved. Whereas, without it, you'd have to save chemical first, set the autogenerated id on synonym and then save the synonym in a separate transaction.
Second, if you're going to use keys typed as "numeric". Then, you're going to be responsible for generating a unique numeric string for each record. That's a huge pain, as it's going to require checking a proposed id against other existing record ids before actually saving. Otherwise, you run the risk of a primary key collision. It's far better to use a standard autoincrementing PK or barring that, at least a GUID, where you're assured a reasonably low risk of collisions occurring.
Third, you should absolute not use using with your context. Here it's not a big deal, since, you're only saving and not reading data from the database, but in a typical view, lazy-loading will kick you in the posterior quick doing that. Your context should be request-scoped, either as an instance variable on your controller (since the controller is newed up and disposed with each request) or using dependency injection. You never want to create an instance of your context anywhere else, including an action method.

#Html.DisplayNameFor not showing data MVC5

I have searched around and not had much luck finding a solution to my exact problem.
Model
public class PageDetailsViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Image { get; set; }
}
Controller
public ActionResult Search(int SysID)
{
var query = from r in _db.Auctions
from d in _db.Product_Details
where SysID == d.Id && r.BidStatus == "Open" && d.Id == r.Product_DetailsId
select new PageDetailsViewModel
{
Name = d.Name,
Description = d.Description,
Image = d.Image
};
return View(query);
}
View
#model IEnumerable<ProjectT.Models.PageDetailsViewModel>
#Html.DisplayNameFor(x => x.Name)
This fails to bring the name through. However, if I use a foreach
#foreach (var item in Model)
{
#item.Name
}
It brings through the name no problem.
Any help is much appreciated.
This extension method shows the value of the DisplayNameAttribute from DataAnnotations namespace. Consider this a label. Typically it is used like this:
[DisplayName("The Name")]
public string Name { get; set; }
And in the view:
#Html.DisplayNameFor(x => x.Name) <-- displays The Name
The code above will work only if the model is a single item. For the list case, as you have, you need to do some tricks, say a for loop, so you could do something like:
#Html.DisplayNameFor(x => x[i].Name): #Model[i].Name <-- displays The Name: Bill

Only return selected fields in Web API results

First of all, this is not exactly a duplication of the dozens of other posts and I have tried all of them and none of them work.
I have a model that contains many more values than my web api consumers need.
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
public DateTime CreatedDt { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedDt { get; set; }
public string UpdatedBy { get; set; }
}
I only want say a few of the fields to be passed in the API. I've tried this code but instead of leaving out say UpdateBy in the Json result, it returns it with a null value. How do I get rid of that? I've tried several dozen variations but they either fail to compile or fail to return results.
public IQueryable<Publication> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new Publication {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}
Don't serialize your DAO. Create a complete contract and then serialize it selectively. To creating different contracts for different cases, you could simplify it using Json.Net; you could just create a custom contract resolver and use it as a parameter of SerializeObject() like so
static void Main(string[] args)
{
var person = new TestContract {FirstName = "John", LastName = "Doe", Age = 36};
var firstNameContract = new SelectiveSerializer("firstname");
var allPropertiesContract = new SelectiveSerializer("firstname, lastname, age");
var allJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = allPropertiesContract});
var firstNameJson = JsonConvert.SerializeObject(
person,
Formatting.Indented,
new JsonSerializerSettings {ContractResolver = firstNameContract});
Console.WriteLine(allJson);
// {
// "FirstName": "John",
// "LastName": "Doe",
// "Age": 36
// }
Console.WriteLine(firstNameJson);
// {
// "FirstName": "John",
// }
}
public class SelectiveSerializer : DefaultContractResolver
{
private readonly string[] _fields;
public SelectiveSerializer(string fields)
{
var fieldColl = fields.Split(',');
_fields = fieldColl
.Select(f => f.ToLower().Trim())
.ToArray();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize = o => _fields.Contains(member.Name.ToLower());
return property;
}
}
public class TestContract
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Without much effort, you could probably work this into your default mediatype formatter (in the pipeline) to look for a parameter in the request called 'fields' or whatever and then use the custom contract resolver if present, and then it would be seamless default behavior to limit fields if specified or serialize the entire object if not specified.
On the academic side, here is the justification:
Any modification to the data is considered a "view concern" which means, in an API, it should controlled by query parameters and accept header. In this case, the "representation" of the data is application/json and you've chose to "filter" the returned fields. All of this can (and should be, imo) be handled during serialization. So your "model" in this case will always be the full model vs. some subset of the model. The full model in this example contains first name, last name, and age. In reality, this could be hundreds of properties. If you want to allow the client to choose a subset of the complete model, this is how you could do it with selective serialization.
You can similar behaviors in graph apis. There, the default for large models is that you get an empty object if you don't specify fields, forcing the client to be very specific about what it asks for, which is great when payload size matters (e.g. mobile applications). And, there's nothing stopping from creating field presets like 'name' which could mean 'firstname, lastname' or 'all' which includes all properties.
I've never been a fan of having hundreds of data objects that all serve some ad hoc requirement for a data set that is used in 20 different contexts where some cases require more data while others require less. IMO if you have to go through the same process to get the data, whether it complete or not, you shouldn't waste your time creating additional objects to frame the data for the sake of the client, and this should help you achieve that.
It's because you're returning a collection of Publication objects so you will get every property that is contained in that class, whether you populate it or not. If you want to return a subset of the properties then create a class that has only the properties you want to return and create an instance of that class in your query.
public IQueryable<WhatIReallyWantToReturn> GetPublications()
{
return db.Publications
.ToList()
.Select(p => new WhatIReallyWantToReturn {
PublicationID = p.PublicationID,
PublicationTitle = p.PublicationTitle,
Frequency = p.Frequency,
NextIssueDate = p.NextIssueDate
})
.AsQueryable();
}
private class WhatIReallyWantToReturn
{
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
}
using Newtonsoft.Json;
public class Publication
{
[Key]
public int PublicationID { get; set; }
public string PublicationTitle { get; set; }
public string Frequency { get; set; }
public DateTime NextIssueDate { get; set; }
public DateTime SpaceDeadline { get; set; }
public DateTime MaterialsDeadline { get; set; }
[JsonIgnore]
public DateTime CreatedDt { get; set; }
[JsonIgnore]
public string CreatedBy { get; set; }
[JsonIgnore]
public DateTime UpdatedDt { get; set; }
[JsonIgnore]
public string UpdatedBy { get; set; }
}
as Craig W. said you can use viewmodel ,also you can use anonymous type
(notice viewmodel is better way because you can use some utilities like automapper for mapping your property automatically)
JsonIgnore annotation has worked for me
[JsonIgnore]
public int Ranking { get; set; }
Here is a great article (Dec 2019) on the subject. It offers a solution for data shaping by making use of ExpandoObject and Type Reflection. The properties that the client requires can then be passed through the request as a query parameter (i.e. separated by a comma). The article also offers solution to the JSON Serialization problem.
Startup.cs file:
services.AddControllers(config =>
{
config.RespectBrowserAcceptHeader = true;
config.ReturnHttpNotAcceptable = true;
})
.AddXmlDataContractSerializerFormatters()
.AddNewtonsoftJson();
+1 for Sinaesthetic's answer.
I just finished reading an article, about GraphQL which solves exactly this problem. You can define exactly which fields do you need in the same request. No need for creating new endpoints every single time, when the caller needs just a specific subset of the properties.
If you can do this in .NET WEB API too without creating new models and endpoints, with just a very little extra effort, why wouldn't you (instead of exchanging Web Api for GraphQL).
Actually his SelectiveSerializer could be upgarded with reflection, so if you want to define which props you need in
C#, you can do this by providing property expressions, so you don't have to worry about misstyping prop names.
I bet there are other solutions for this, but the basic concept is the most important that we can define which fields we need in our json without creating new models.

EF Code First 0..1 to 0..1 relationship

I'm trying to make an optional relationship between to entities, but I'm having trouble finding the correct syntax. Actually I need a 0..1 to 0..2 relationship, but I'm guessing that once I find the way to do the 0..1 to 0..1 that will be trivial.
Simplified what I have is this:
class Foo
{
int Id { get; set; }
//navigation property
virtual Bar Bar { get; private set; }
}
class Bar
{
int Id { get; set; }
int? LeftFooId { get; set; }
[ForeignKey("LeftFooId")]
Foo LeftFoo { get; set; }
int? RightFooId{ get; set; }
[ForeignKey("RightFooId")]
Foo RightFoo { get; set; }
}
A Foo can be connected to zero or one Bar, never more. A Bar can have a LeftFoo and a RightFoo, or one or neither. The Bar property of Foo should be null if it is not referenced by a Bar, and it should contain the Bar that references it when it is referenced by a Bar.
With the code above the Bar references the Foo correctly, but EF gives the Foo table a Bar_Id and the Bar property is always null.
I've tried several different ways to setup these entity classes and I've used differenct Fluent API calls to make this work, but I don't get the wanted results.
Try this:
public class Foo
{
public int Id { get; set; }
public virtual Bar Bar { get; set; }
}
public class Bar
{
public int Id {get; set;}
public virtual Foo LeftFoo {get;set;}
public virtual Foo RightFoo {get;set;}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Bar>()
.HasOptiona(b => b.LeftFoo)
.WithOptionalDependent()
.Map(k = k.MapKey("LeftFooId"));
modelBuilder.Entity<Bar>()
.HasOptional(b => b.RightFoo)
.WithOptionalDependent()
.Map(k => k.MapKey("RightFooId"));
...
}
Result
Edit for seeding
I would do the following, didn't test the code, but it should work:
var bars = new List<Bar>();
bars.Add(new Bar());
bars.Add(new Bar());
...//this means as many as you need/want
bars.ForEach(b => context.Bars.AddOrUpdate(b));
context.SaveChanges();
var leftFoos = new List<Foo>();
leftFoos.Add(new Foo());
leftFoos.Add(new Foo());
...//this means as many as you need/want
leftFoos.ForEach(f => context.Foos.AddOrUpdate(f));
context.SaveChanges();
var rightFoos = new List<Foo>();
rightFoos.Add(new Foo());
rightFoos.Add(new Foo());
...//this means as many as you need/want
rightFoos.Foreach(f => context.Foos.AddOrUpdate(f));
context.SaveChanges();
int i=0;
foreach(var bar in bars)
{
bar.LeftFoo = leftFoos.ElementAt(i);
bar.RightFoo = rightFoos.ElementAt(i);
i++;
}
context.SaveChanges();
For simplicity bars, leftFoos and rightFoos have the same number of elements.
I modified the code like this:
class Foo
{
int Id { get; set; }
int? BarId { get; set; }
[ForeignKey("BarId")]
virtual Bar Bar { get; set; }
}
class Bar
{
int Id { get; set; }
int? LeftFooId { get; set; }
[ForeignKey("LeftFooId")]
Foo LeftFoo { get; set; }
int? RightFooId{ get; set; }
[ForeignKey("RightFooId")]
Foo RightFoo { get; set; }
}
I may be wrong, but automatic assignment of a pair of FK's my be impossible, at least from the Foo to the Bar: How would EF know if it should assign the FK to the LeftFoo or the RightFoo. So for now I'm just doing both assignments manually:
Foo myFirstFoo = new Foo();
Foo mySecondFoo = new Foo();
Bar myBar = new Bar();
context.Add(myFirstFoo); context.Add(mySecondFoo); context.Add(myBar);
context.SaveChanges();
myFirstFoo.BarId = myBar.Id;
myBar.LeftFooId = myFirstFoo.Id;
mySecondFoo.BarId = myBar.Id;
myBar.RightFooId = mySecondFoo.Id;
context.Update(myFirstFoo); context.Update(mySecondFoo); context.Update(myBar);
context.SaveChanges();
This seems to work, but I do hope it can be done better.
Try make your properties virtual and use POCO proxies (using Create instead of new ...). This would ensure the automatic update on the side properties. i.e when assigning a Foo to a Bar the POCO proxy will assign the corresponding Bar to the Foo but, ... and this should be the main problem: what do you expect if assign a Bar to a Foo? Which property do you want to be automatically assigned, left of right? Annotate the Bar property [InverseProperty("...")] to indicate this and think about you may need another property of type Bar in Foo for the other relationship.
Hope this helps

Resources