EF Core 5 Duplicating Records in the Many-to-Many Relationship - visual-studio

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.

Related

Using Automapper to map object to realm object with Xamarin and c#

I am creating an Xamarin.iOS app and a Realm database.I would like to keep my POCO objects separate from my RealmObject so what I did was use a repository pattern and within the repository I tried to use AutoMapper to map the POCO to the RealmObject
e.g. (subset)
public class PlaceRepository : IPlaceRepository
{
private Realm _realm;
public PlaceRepository(RealmConfiguration config)
{
_realm = Realm.GetInstance(config);
}
public void Add(Place place)
{
using (var trans = _realm.BeginWrite())
{
var placeRealm = _realm.CreateObject<PlaceRealm>();
placeRealm = Mapper.Map<Place, PlaceRealm>(place);
trans.Commit();
}
}
So, if I debug my code everything maps OK and placeRealm is populated OK but when I commit nothing gets saved to the Realm db. The following is my RealmObject
public class PlaceRealm : RealmObject
{
[ObjectId]
public string Guid { get; set; }
public string Title { get; set; }
public string Notes { get; set; }
}
and this is my POCO Place
public class Place
{
public string Guid { get; set; }
public string Title { get; set; }
public string Notes { get; set; }
}
And AutoMapper is initialized like so:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Place, PlaceRealm>();
cfg.CreateMap<PlaceRealm, Place>();
});
All standard stuff. Has anyone else managed to get something similar working?
Your poco 'Place' is called 'PlaceRealm'. I suspect that is a typo. (made edit)
I suspect that Automapper is instantiating a new object overwriting your original 'placeRealm' object.
Perhaps you could try
Mapper.Map(place, placeRealm);
in place of your current mapping.
which should just copy the values to your already instatiated and tracked object.
(no need to store the return value).
You might also want to make explicit which properties (3) you are mapping as currently Automapper will map all including those in the base class.
On a side note, you may run into performance issues with Automapper. I found it to be the performance bottleneck in some apps. ExpressMapper is a nice alternative.

View and Domain model, where to perform calculation

I just started using View & Domain model design in my MVC web app but got the question where to perform calculations and other View related actions. I will try to give example below.
My Domain Model (Linq2Sql)
public class Product
{
public int Id;
public string Name;
}
The View Model with new UserCount property which I would like to calculate.
public class ProductViewModel
{
public int Id;
public string Name;
public int UserCount;
}
My controller action looks like
public ActionResult _SelectionClientSide_Products()
{
IQueryable<Product> products = _repository.GetProducts(true);
var model = Mapper.Map<IEnumerable<Product>, IEnumerable<ProductViewModel>>(products);
return View(model);
}
I query for data using repository method and get IQueryable<Product> and map it to ProductViewModel list. But I also need to perform another query operation to count users for every queried product and assign all values to ProductViewModel. What design I should follow to achieve this?
The relationship between tables
Products -> Orders - > Users
EDIT
I have decided to remove AutoMapper because it gives more problems than benefits and created my own Builder which contain everything what I need. I make field assign and also add calculation.
public ActionResult _SelectionClientSide_Products()
{
Data = new ProductViewModelBuilder(_repository).Build();
return View(Data);
}
namespace PC.Models
{
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int UsersCount { get; set; }
}
public class ProductViewModelBuilder
{
private readonly IDataAccess _repository;
public ProductViewModelBuilder(IDataAccess repository)
{
_repository = repository;
}
public IQueryable<ProductViewModel> Build()
{
return _repository.GetProducts().Select(p=> new ProductViewModel
{
Id = p.Id,
Name = p.Name,
UsersCount = _repository.CountUsers(p.Id)
});
}
}
}
In my opinion, by the time your Controller has begun it's model setup, there should be no more "logic" or talking to the model. Having a "builder" which queries the model again is bad practice. Not really "breaking" the MVC pattern, but still bad practice. Your ViewModel has a dependency on your DAL. Bad bad boy. :)
If you have to run another query, well that should be encapsulated in the original query. Instead of calling _repository.GetProducts, call a different method which not only gets the products, but get's the count as well. Create a DTO if necessary.
Then your controller should look like:
public ActionResult _SelectionClientSide_Products()
{
var someDto = _repository.GetProductsAndUserCount(true);
var model = Mapper.Map<IEnumerable<SomeDto>, IEnumerable<ProductViewModel>>(someDto);
return View(model);
}
Personally i go for generic repositories over specialized ones, as i don't want a IProductRepository interface with 50 signatures. I opt for LINQ IEnumerable<T> extensions (aka "pipes and filters") which allows complex queries to be built and remain in my domain.
So my version of the above would be:
public ActionResult _SelectionClientSide_Products()
{
var someDto = _productRepository.Find().WithSomeCondition(true).ToSomeDto();
var model = Mapper.Map<IEnumerable<SomeDto>, IEnumerable<ProductViewModel>>(someDto);
return View(model);
}
On a side note, why do you say AutoMapper causes you more problems that benefits? AutoMapper has saved me tons and tons of repetitive code. If you know how to use it properly, it's your best friend. Hands down the most important NuGet package in my current application.

Ignoring properties when serializing

I'm pulling my hair out on this one.
I am trying to implement a multi-step wizard, and i'm using the Html.Serialize html helper in MVC3 Futures. This works well, except one of the properties in my model is a SelectList. I don't want this property serialized (and it blows up when it tries anyways).
I can't use [NonSerialized] because that only works on fields, not properties. I've even tried some of the other normal ways such as [XmlIgnore] (which I didn't think would work anyways).
Can anyone suggest an attribute that will ignore a property in a model when using Html.Serialize?
EDIT:
The error I get when I try to serialize is a InvalidDataContractException. There is this message:
Type 'System.Web.Mvc.SelectList' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
However, if I do this then I have to mark all the members with [DataMember] just to exclude 1 property, which seems kind of stupid.
UPDATE:
A quick example of this is this bit of code (make sure to add reference to System.Runtime.Serialization.dll):
Test.cs
[Serializable]
public class Test
{
public int ID { get; set; }
[IgnoreDataMember]
public SelectList TestList { get; set; }
}
HomeController.cs
public ActionResult About()
{
return View(new Test() { ID = 0, TestList = new SelectList(new [] {""})});
}
Home/About.cshtml
#using Microsoft.Web.Mvc
#model MvcApplication3.Models.Test
#Html.Serialize("Test", Model)
This generates the InvalidDataContractException
public class MyViewModel
{
[IgnoreDataMember]
public SelectList Items { get; set; }
...
}
or simply:
public class MyViewModel
{
public IEnumerable<SelectListItem> Items { get; set; }
...
}

"An object with the same key already exists in the ObjectStateManager..." exception is thrown when setting an entity state to modified

I followed some examples(including such books as "Pro ASP.NET MVC 3" and "Professional ASP.NET MVC 3") to create simple ASP.NET MVC 3 apps using EF 4.1 (since I'm new to these technologies).
I'm using the following repository(single instance of it is used by all action methods of the controller) to access the DB:
public class ProductRepository : IProductRepository
{
private readonly EFDbContext _context = new EFDbContext();
#region Implementation of IProductRepository
....
public void SaveProduct(Product product)
{
if (product.ProductId == 0)
{
_context.Products.Add(product);
}
else
{
_context.Entry(product).State = EntityState.Modified;
}
_context.SaveChanges();
}
....
}
This repository performs updating as it was shown in the examples I used.
Product class:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
In case of updating the product, I'm getting the exception "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key"
I know that the similar questions have been already discussed here but my question is a bit different:
Why this code which was taken from examples is not working (though it looks pretty simple and straightforward)? What wrong might I have done or missed something.
After searching for hours for a solution, I have found one that seems suitable after doing enough reading.
The fix is here:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
Basically, fetch the record from the Context and call:
var currentProduct = _context.Products.Find(product.ProductId);
_context.Entry(currentProduct).CurrentValues.SetValues(product);
This seems like a bad idea and something I've always hated about EF in my previous workings, but cccording to Ladislav Mrnka (who apparnently answers every EF related question on Stackoverflow) in this post:
Entity Framework and Connection Pooling
EF will store a request for an entity internally, so ideally, it will already be there and it won't be making an additional call back to the database.
The root cause of the problem seems to be that once a product is fetched from the Context, the context is keeping track of it and that's what is causing all the trouble. So merging your changes back in is the only way.
Hope that helps.
It looks like you're not updating product.ProductId when the item is saved for the first time. This means that when you come back to save the item again it's adding it to the context again, hence the error.
As the Id will be added by database (I'm assuming it's the autogenerated Id) then you'll need to read your product data back onto the client.
From a Generics standpoint, here's how I have resolve the same problem very recently:
public TEntity Update(TEntity model, bool persist)
{
if (model == null)
{
throw new ArgumentException("Cannot update a null entity.");
}
var updateModel = Get(model.Id);
if (updateModel == null)
{
return model;
}
this.context.Entry<TEntity>(updateModel).CurrentValues.SetValues(model);
this.Save(persist);
return model;
}

Serializing EF4.1 Entities using JSON.Net

I am building an application using MVC3, Razor view engine, Repository Pattern with Unit of Work and using EF4.1 Code First to define my data model.
Here is a bit of background (gloss over it if you want).
The application itself is just an Intranet 'Menu'.
The 2 main entities are MenuItem and Department of which:
MenuItem can have many Departments
Departments can have many MenuItems
MenuItem may have a MenuItem as a parent
This is how I have defined my Entities
public class MenuItem
{
public int MenuItemId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual ICollection<Department> Departments { get; set; }
public int? ParentId { get; set; }
public virtual MenuItem ParentMenuItem { get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public virtual ICollection<MenuItem> MenuItems { get; set; }
}
I am using the FluentAPI to define the Self Reference Many-to-Many for the MenuItem.
The issue I am having is passing a MenuItem to the view via JSON.
The central issues are that I have a circular reference between my entities that the built in JSON parser can't deal with and I have lazy loading and proxy generation still enabled.
I am using JSON.net library from Nuget as my JSON Serializer as this seems to be a nice way round the circular reference issue. I now am unsure how to 'fix' the proxy generation issue. Currently the serializer throws The RelationshipManager object could not be serialized. This type of object cannot be serialized when the RelationshipManager belongs to an entity object that does not implement IEntityWithRelationships.
Can anyone help me with this? If I turn off proxy generation, I am going to have a hell of a time loading all of the MenuItem children so I am keen leave this on. I have read a fair amount and there seems to be a variety of different answers including projecting the entities into another object and serialize that, etc, etc. Ideally there would be some way of configuring JSON.net to ignore the RelationshipManager object?
Update
Here is what I have used as a Custom ContractResolver for JSON.Net serializer. This seems to have sorted out my issue.
public class ContractResolver : DefaultContractResolver
{
private static readonly IEnumerable<Type> Types = GetEntityTypes();
private static IEnumerable<Type> GetEntityTypes()
{
var assembly = Assembly.GetAssembly(typeof (IEntity));
var types = assembly.GetTypes().Where(t => String.Equals(t.Namespace, "Namespace", StringComparison.Ordinal));
return types;
}
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
if (!AllowType(objectType))
return new List<MemberInfo>();
var members = base.GetSerializableMembers(objectType);
members.RemoveAll(memberInfo => (IsMemberEntityWrapper(memberInfo)));
return members;
}
private static bool AllowType(Type objectType)
{
return Types.Contains(objectType) || Types.Contains(objectType.BaseType);
}
private static bool IsMemberEntityWrapper(MemberInfo memberInfo)
{
return memberInfo.Name == "_entityWrapper";
}
}
IEntity is an interface all my Code First entity objects implement.
I realise this question has an accepted answer, but I thought I would post my EF Code First solution for future viewers. I was able to get around the error message with the contract resolver below:
class ContractResolver : DefaultContractResolver
{
protected override List<System.Reflection.MemberInfo> GetSerializableMembers(Type objectType)
{
if (objectType.Namespace.StartsWith("System.Data.Entity.Dynamic"))
{
return base.GetSerializableMembers(objectType.BaseType);
}
return base.GetSerializableMembers(objectType);
}
}
This works because EF Code First classes inherit from the POCO class that you actually want serialized, so if we can identify when we are looking at an EF generated class (by checking the namespace) we are able to just serialize using the properties from the base class, and therefore only serialize the POCO properties that we were really after in the first place.
Well, you used powerful serialization API which serializes references and all members as well and now you complains that it serializes all members :)
I didn't test it but I believe this will bring you close to the solution.
JSON.NET is quite powerful tool and it should offer you the extensibility point to avoid this behavior but you will have to code it yourselves. You will need custom DataContractResolver where you define which members should be serialized. Here is the similar example for NHibernate.
You can implement some logic which will take only members present in the parent class of dynamic proxy. I hope this will not break lazy loading. To validate that current entity is proxy you can use this code to get all known proxy types:
IEnumerable<Type> types = ((IObjectContextAdapter)dbContext).ObjectContext.GetKnownProxyTypes();

Resources