have very simple relationship between two entities and I am trying to expose them with asp.net webapi odata controllers but it seems that something is wrong with $metadata.
When I run jaydatasvcutil.exe on the $metadata I get warning: inverseProperty other side missing.
When I use breezejs loadNavigationProperty I get similar error.
I have the problem even with official example.
http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations
You can observe the $metadata here http://sdrv.ms/Z5Klfw
Please help.
When we are generating navigation properties we don't reuse the relationships.
For example, lets say you have simple model,
public class Product
{
public int Id { get; set; }
public Supplier Supplier { get; set; }
}
public class Supplier
{
public int Id { get; set; }
public Product[] Products { get; set; }
}
The $metadata for the navigation properties that we generate looks like this,
<NavigationProperty Name="Supplier" Relationship="ProductsService.Models.ProductsService_Models_Product_Supplier_ProductsService_Models_Supplier_SupplierPartner" ToRole="Supplier" FromRole="SupplierPartner" />
<NavigationProperty Name="Products" Relationship="ProductsService.Models.ProductsService_Models_Supplier_Products_ProductsService_Models_Product_ProductsPartner" ToRole="Products" FromRole="ProductsPartner" />
Notice that we are generating two relationships instead of one. The reason we do that is that it is a hard problem to figure out if two navigation properties represent the same relationship. Take the instance of Product and Manufacturer.
public class Manufacturer
{
public int Id { get; set; }
public Product[] RawMaterials { get; set; }
public Product[] Produces { get; set; }
}
public class Product
{
public int Id { get; set; }
public Manufacturer[] Producers { get; set; }
public Manufacturer[] Consumers { get; set; }
}
It is not trivial to figure out that Maufacturer.RawMaterials and Product.Consumers should share the same relationship and Manufaturer.Produces and Product.Producers should share the same relationship. We chose not to do it because the clients that we know of don't make much out of this information.
All this happens because OData uses the same EDM model as the entityframework. Entityframework requires this information as it maps these relationships to association sets which would become tables in the database.
Another reason we chose not to do it is that this could be going away in OData V4. Check out the working draft here (page 23 and page 57 would be of interest). In short, navigation properties in $metadata in OData V4 would look more like this,
<NavigationProperty Name="Category" Type="Self.Category" Nullable="false" Partner="Products" />
Notice that there is no relationship and there would be no association sets.
Related
I'm having trouble implementing the many-to-many relationship using the Entity Framework Core 5 in Visual Studio.
I have the classes:
public class Medico
{
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public int Id { get; set; }
public string Nome { get; set; }
public int CRM { get; set; }
public List<Especialidade>Especialidades { get; set; }
public class Especialidade
{
public int Id { get; set; }
public string Descricao { get; set; }
public IList<Medico>Medicos { get; set; }
}
And the Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Nome,CRM")] Medico medico)
{
var lstTags = Request.Form["chkTags"];
if (!string.IsNullOrEmpty(lstTags))
{
int[] splTags = lstTags.ToString().Split(',').Select(Int32.Parse).ToArray();
if (splTags.Count() > 0)
{
var medicoEspecialidades = await _context.Especialidades.Where(t => splTags.Contains(t.Id)).ToListAsync();
foreach (var me in medicoEspecialidades)
{
medico.Especialidades.Add(me);
}
}
}
if (ModelState.IsValid)
{
_context.Medicos.Add(medico);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(medico);
}
But when I run Create, it returns me with the following error:
"Cannot insert explicit value for identity column in table 'Especialidades' when IDENTITY_INSERT is set to OFF."
If I turn off the Identity_Insert of the Specialty table in the bank, it even inserts, but duplicates the records in the Specialty table.
I've been researching and trying to find a solution for 2 days now. Can someone who has been through this give me a hand?
The application source code is here: https://www.dropbox.com/s/xn6b95h7amfpuqa/AppCompleta%205.0.rar?dl=1
The approach looks Ok, though I would check to ensure that the medico being passed in does not have any Especialidade somehow coming in from the client as these would be detached entities. The error seems to imply that Medico may have a detached Especialidade in its collection. If the checked values represents everything that should be tracked, then this collection should be cleared and the Especialidade references added.
Do you have any explicit mapping configuration for either of these entities? If not I would highly recommend using one for Many-to-Many relationships as sometimes EF can default to unexpected schema assumptions when working off convention, especially in Code First if that is the case. I would look at your schema carefully to ensure it is matching what would be expected for a Many-To-Many. For example, what is the linking table name for Medico-Especialidade? Is there an entity defined for it in the configuration? This is entirely optional and EF should work it out, however if you do have explicit mapping that might not be configured correctly, tripping up the relationships.
One other detail giving off a smell:
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public List<Especialidade>Especialidades { get; set; }
This should be:
public ICollection<Especialidade> { get; set; } = new HashSet<Especialidade>();
EF can work with lists, but when it comes to proxies and the behind the scenes EF is doing with entities it is generally better to declare your collection references as ICollection rather than concrete classes. ToListing a HashSet merely produces a List, so either = new HashSet<Especialidade>() or = new List<Especialidade>() will do. The difference would merely be the behaviour of the collection when you are populating it after "newing" up a Medico, or deserializing one.
I am trying to dig into ASP.NET MVC 3, using the standard tutorials in the web, and I encounter a strage problem.
Currently, I am following the samples in a book, using a "Movie" class with movie genres stored in a separate entity, connected with a foreign key (okay, I am from Germany, so my class is named in German). I show only the relevant properties here. It's a database first approach using DbContext, my model was created from the edmx by using the EF 4.x DbContext Generator and the edmx was automatically created from the data base.
public partial class Film
{
public Film() { }
public int ID { get; set; }
public string Titel { get; set; }
public int GenreID { get; set; }
public virtual Genre Genre { get; set; }
}
public partial class Genre
{
public Genre() { }
public int GenreID { get; set; }
public string Name { get; set; }
}
When I create a new Controller with CRUD Views for the Film class, using a DBContext that provides a DBSet, I get an Edit view that uses a DropdownList to edit GenreID, labelled "Genre". Fine. That's what I want.
But then, I tried to create another edit view, separately. So I right-clicked into my Edit Action-Method, selected "Add View", called it "Edit2", used Film as model and "Edit" as scaffold template. In this view, I got a simple "EditorFor(m->m.GenreID)", labelled GenreID. That's not what I want.
Of course, I can change that manually. Of course, I can download a slew of scaffolding tools that claim to do better.
But I want to understand if this is a bug in the EF templates, or if my model is built wrong so that Genre / GenreID gets confused. When I create everything at once, scaffolding creates a DropDown, so there must be "just" some detail that's missing.
You will need to call your Action in your controller "Edit2".
I have 2 models, employee and person:
public class Employee
{
[Key]
public int Id { get; set; }
public int? PersonId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Person { get; set; }
}
public class Person
{
public IList<PhoneNumber> PhoneNumbers { get; set; }
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public Person()
{
PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber()
};
}
}
Editor Template for Phone:
#Html.TextBoxFor(x => x.Number)
#Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(typeof (WebMVC.Core.Common.PhoneType))))
To reduce clutter, I removed the other (non-pertinent) properties.
The difficulty I am having is while in the Employee Create(), I can bind the person FName & LName, I cannot bind the PhoneNumbers collection.
I know about the 2008 Haack blog but I do not think it mirrors this situation.
Does anyone know a solution to bind the person phone numbers collection in the employee's Create()?
I'm not exactly sure if PhoneNumber is a custom class that you created, or one that is built into the framework. But if you're having problems with MVC3 mapping posted data to the Employee class like you specified, you might want to look at creating a custom binding. Keep in mind that if your editor template code is incorrect this wont really matter, so I would take a look at that using fiddler first.
Here are a few good sites to get you started, I found them all on SO at one point.
http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx
Creating a custom binder gives you complete control over the way that MVC parses your posted model data and populates the object. There are 2 main functions that most people override, CreateModel and BindModel. BindModel is the function you will most likely want to override if this is the way you would like to go.
I don't know what the html from the editor template looks like, but to bind to a collection of custom types it should look something like this:
<input name="[0].Number">
<input name="[0].PhoneType">
<input name="[1].Number">
<input name="[1].PhoneType">
<input name="[2].Number">
<input name="[2].PhoneType">
I'll let the code do the talking here, I have something like this:
class Problem
{
public string Title { get; set; }
public string Description { get; set; }
public virtual IList<Symptom> Symptoms { get; set; }
}
class Symptom
{
public string Comments { get; set; }
public virtual Category Category { get; set; }
}
class Category
{
public string Name { get; set; }
}
I have a modal that allows users to add a list of symptoms on my view. Each symptom being added produces an INPUT that looks like this (where N is the index):
<input type="text" name="Symptom[N].Name" value="#Model.Symptom[N].Name">
<input type="text" name="Symptom[N].Category" value="#Model.Symptom[N].Category">
Once I POST the data to my controller, the model contains a valid list of Symptom (if I add 3, my Product.Symptom list has 3 entities) and the [Comments] of each symptom has persisted, but the [Category] property of each is NULL. What am I doing wrong here? I've tried numerous things but I still end up with NULL as the [Category] for each.
I'm using Entity Framework 4.1 Code First with Fluent API developing in MVC 3 using Razor syntax.
Try this:
<input type="text"
name="Symptom[N].Category.Name"
value="#Model.Symptom[N].Category.Name">
What I think is happening is that it's trying to bind a string to a Category which is invalid. If you want to map the text to the Name property on the Category class, you will need to specify it one level deeper.
I am trying to implement a foreign key connection between the built-in User model and my models in ASP.NET MVC 3.
How to assign ownership or some other roles to various entries represented with my models. Example of how my models look like:
public class Entry
{
public int Id { get; set; }
public string Value { get; set; }
public User Owner { get; set; }
public User SomeoneElse { get; set; }
}
Where to find the model for users, what do I need to import? Or is there a better approach to accomplish this?
Do you use Entity Framework ?? If so...
Simple solution
You could simply keep the Guid from the Built-In User model. You won't have a "real relationship" but it will do the trick for what you want to do. You can always get the UserId with Membership.GetUser().ProviderUserKey
Other more complex
Completely rewrite and override the MembershipProvider and login module. That way you can use your own User object and add other properties to it aswell.
Not Sure about this one
Not sure if this one will work with the auto generated tables from the MembershipProvider but you can add the Foreign Key Property this way:
[ForeignKey("User")]
public Guid UserId { get; set; }