I've got a set of DB objects sitting in an EntitySet on my main object definition. This handles additions and updates fine, but I found the removing items from the list didn't result in the database records being deleted, so I had to create a method in the data repository object to delete the records as the data object doesn't have access to the data-context in which it is being used.
I was looking to see if I could bring this delete into the main object and I found the DeleteOnNull attribute to the association, but when I use it, I get an error "DeleteOnNull can only be true for singleton association members mapped to non-nullable foreign key columns". My code is:
private EntitySet<UserSite> _userSites = new EntitySet<UserSite>();
[Association(Name = "User_UserSites", Storage = "_userSites", ThisKey = "UserID", OtherKey = "UserID", DeleteOnNull=true)]
public IList<UserSite> UserSites { get { return _userSites; } set { } }
my usersite object is
[Table(Name="UserSite")]
public class UserSite
{
[Column]//(IsPrimaryKey = true)]
public int UserID { get; set; }
[Column]//(IsPrimaryKey = true)]
public string Site { get; set; }
[Column]
public bool DefaultSite { get; set; }
[Column(IsPrimaryKey = true, AutoSync = AutoSync.OnInsert)]
public int UniqueID { get; set; }
}
Can I use DeleteOnNull to keep all my data update methods within my main user object, or do I have to handle the deletes at the repository level?
DeleteOnNull is only for singleton associations. So you can put it on UserSite.User but not on User.UserSites. It's still not quite as automatic as you'd like it to be, though. There is an example here.
It's hard for LINQ to SQL to infer the behavior you want, because it can't guess if you want composition or aggregation, so it chooses the safe guess (aggregation).
Related
I have a scenario where if certain features are deployed then a number of columns will be there in some tables otherwise won't so the mapping of Entities and Columns is not static. I need to add/remove the mapping at runtime. Is there any way?
Prepare a new MappingSchema and pass to DataConnection constructor.
Consider you have the following class:
[Table]
class SampleClass
{
[Column] public int Id { get; set; }
[Column] public int Value { get; set; }
}
To remove column from full object materialization, do the following:
var ms = new MappingSchema();
ms.GetFluentMappingBuilder()
.Entity<SampleClass>().Property(e => e.Value).IsNotColumn();
// cache somewhere this schema
using (var db = new DataConnection(ms))
{
var result = db.GetTable<SampleClass>().ToArray();
}
Remember, better to cache this new MappingSchema and reuse. Otherwise you will never have cache hit and you'll lose performance.
I'm pretty sure this isn't a duplicate question (or I don't know how to ask it properly) so here goes. I have an ASP.NET MVC project using EF 6.1. In it I have an entity called Member. The Member has a property called Races that can contain one or more Race entities. The Races property on Member:
public virtual ICollection<Race> Races { get; set; }
The Race entity:
public class Race : ILookupListItem {
public int Id { get; set; }
public string Description { get; set; }
public bool Active { get; set; }
[JsonIgnore]
public virtual ICollection<Member> Members { get; set; }
}
The Race entity is what I have always called a lookup.
My issue arises when I try to save the entity after updates have occurred on the client. My SaveMember controller method is below along with my business logic method for actually saving the entities. (To avoid any confusion, InjectFrom is the ValueInjecter method and the JSONResponseError and JSONResponseSuccess classes are our custom wrappers around a JsonResult.)
public JsonResult SaveMemberInfo(MemberInfoModel model) {
try {
// retrieve member
var member = new Member();
member.InjectFrom(model);
var races = new List<Race>();
races.InjectFromList(model.Races);
member.Races = races;
// save member
var savedMember = _members.UpdateMember(member, out errId);
var savedModel = GetMemberChartViewModel(savedMember);
// notify user of success
return new JsonResponseSuccess(savedModel);
}
catch (Exception ex) {
return new JsonResponseError(message);
}
}
The _members.UpdateMember method is below.
public Member UpdateMember(Member contract, out int? errId) {
try {
using (_db = new ApplicationContext(_currentUserID)) {
// updates entity state in the context using the entity's State field from Julie Lerman's example on MSDN.
_db.FixState();
_db.SaveChanges();
errId = null;
return contract;
}
}
catch (Exception ex)
{
// log error to db and return id
errId = LogError(ex, "MemberLogic.UpdateMember");
return null;
}
}
When I get the data contract back from the UpdateMember method, the Races property has all the races saved to the memeber before the update, plus any that I just saved (so it's adding but not doing a diff to determine which items were already there and which were removed). In addition the Races table will now have duplicate entries for the "new" races. My assumption is that this isn't EF's default behavior but I've never worked with a dropdown list that populated a list of items, usually it's just a single property (like Gender or Country). Is there something obvious I'm missing?
Thanks!
EDIT: It just occurred to me that the EntityConfiguration for the Member and Race intersection table would be useful, added below. Obviously, this is only the part I thought to be relevant to the question.
HasMany(t => t.Races).WithMany(t => t.Members).Map(m =>
{
m.ToTable("MemberRaces", "dbo");
m.MapLeftKey("MemberId");
m.MapRightKey("RaceId");
});
I have problem with updating entites that have many-to many relationship. Below my User and category class:
public class User : IEntity
{
[Key]
public virtual long Id { get; set; }
private ICollection<Category> _availableCategories;
public virtual ICollection<Category> AvailableCategories
{
get { return _availableCategories ?? (_availableCategories = new List<Category>()); }
set { _availableCategories = value; }
}
}
public class Category : IEntity
{
[Key]
public long Id { get; set; }
/// <summary>
/// Full name or description of a category
/// </summary>
[StringLength(255)]
public string FullName { get; set; }
}
This is code snippet from my repository
public override void Edit(User user)
{
var dbUser = _context.Users.Include(x => x.AvailableCategories)
.Single(x => x.Id == user.Id);
var categories = _context.Categories;
dbUser.AvailableCategories.Clear();
foreach (var cat in user.AvailableCategories)
{
dbUser.AvailableCategories.Add(cat);
}
_context.Entry(dbUser).State = EntityState.Modified;
}
However the categories don't get updated. What EF does is insert empty rows into category table and sets relations to this new rows with user.
How can I update User so that I change only categories that already exist in the database?
User that I pass to Edit method has AvailableCategories with only Ids set (rest of properties are empty).
When you're doing something like posting back M2M relationships, you either must post the full object, as in every single property on those objects, or simply post a list of ids and then use those to query the associated objects back from the database. Otherwise, Entity Framework understands your purpose to be to update the properties on the objects as well, in this case with empty values.
Obviously the first option is quite unwieldy, so the second way is the preferred and standard way. Generally, for this, you'd want to use a view model so you could have a property like the following, that you would post into:
public List<long> SelectedCategories { get; set; }
But, if you insist on using the entity directly, you can get much the same result by simply doing:
var selectedCategories = user.AvailableCategories.Select(m => m.Id)
Once you have the ids:
var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));
And then finally set that on your user:
dbUser.AvailableCategories = newAvailableCategories;
I notice you are also adding the user.AvailableCategories directly into dbUser.AvailableCategories. I've noticed when binding back complex objects from an MVC view that DB Entities are no longer attached to the DbContext. If you look at the entity, you can verify by checking dbContext.Entry(cat).State is "detached" (or something unexpected) I believe.
You must query those entities back out of the dbContext (possibly by using the returned cat.Id's). Or otherwise manually set the entities as "unchanged". And then add those "non-detached" items into dbUser.AvailableCategories. Please see Chris's answer as it shows with specific code how to get this done.
Also, I might use a linking entity. Possibly something like this:
public class UserCategory
{
public User User {get;set;}
public Category Category {get;set;}
}
And add it to DB context. Also, drop the linking lists in your current User and Category class. This way you can manipulate the UserCategory class (and DbSet) to manage your many-to-many relationship.
I am getting this error "Self referencing loop detected" while serializing using 'Json.NET'
I have a Book model
public class Book
{
public Book()
{
BookPersonMap = new List<BookPersonMap>();
}
public int BookId { get; set; }
public virtual ICollection<BookPersonMap> BookPersonMap { get; private set; }
(And many other virtual Icollections)
}
And this is the BookPerson Mapping class:
public class BookPersonMap
{
public int BookId { get; set; }
public string PersonName { get; set; }
public int PersonTypeId { get; set; }
public virtual Book Book { get; set; } // Foreign keys
public virtual PersonType PersonType { get; set; }
}
When I try to Serialize the Book object it throws:
"Self referencing loop detected for property 'Book' with type 'System.Data.Entity.DynamicProxies.Book_57F0FA206568374DD5A4CFF53C3B41CFDDC52DBBBA18007A896 08A96E7A783F8'. Path 'BookPersonMap[0]'."
I have tried the things suggested in some of the similar posts
Example:
PreserveReferencesHandling = PreserveReferencesHandling.Objects in Serializer settings returned a string with length 3 million!
ReferenceLoopHandling = ReferenceLoopHandling.Ignore in Serializer settings :
"An exception of type 'System.OutOfMemoryException' occurred in Newtonsoft.Json.dll but was not handled in user code"
^ Same luck with "ReferenceLoopHandling.Serialize"
MaxDepth = 1 : Infinite loop again.
Putting [JsonIgnore] on the virtual properties is working but it is a tedious task (because of numerous FK references) and not efficent, since if I miss one property and it will throw exception.
What is missing from above Json settings for them be not working?
services.AddMvc().AddJsonOptions(opt => {
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
I have found the best way to solve this type of error is to flatten your model using a view model.
Put a break point on your object before it is serialized and start drilling into the child properties. You will probably find that you can go on indefinitely.
This is what the serializer is choking on.
Create a Constructor for your controller and put on it this line of code :
db.Configuration.ProxyCreationEnabled = false;
//db is the instance of the context.
For asp.net mvc 5 use this
Add the code below to your Application_Start method inside globax.asax file or startup file.
protected void Application_Start()
{
..
GlobalConfiguration.Configuration.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
Disable lazy loading and
ensure your controller does not return
Json(..obj)
rather it should return
Ok(..obj)
I am trying to update an entity which but when I call save changes nothing happens. Here's the code how I attached the entity. I am using EF6.1 and MVC5.1 by the way
var entity = db.Entity.Attach(existingEntity);
entity = new Entity(args0, args1, args2)
entity.Id = existingEntity.Id;
db.SaveChanges()
Here's the entity class:
public class Entity : Entity
{
public int Id { get; set; }
public string PropertyA { get; private set; }
public string PropertyB { get; private set; }
public string PropertyC { get; private set; }
}
public Entity(string args0, string args1, string args2)
{
this.PropertyA = args0;
this.PropertyB = args1;
this.PropertyC = args2;
}
Note that I did set the properties setter to private as I have some inner workings within the class. I'm thinking that the reason why EF can't update the entity is because I instantiate a new one though I did set its primary key. I did also try changing the State to Modified but it just running the "update script" but it doesn't really reflect the changes I made on each property
Any possible resolution for this scenario?
I am myself new to MVC and EF. But here goes.
Are you not mixing up your DAL and BLL by using private set?
Also, when you already have the existing entity why cant you just update the properties for entity directly? The way your class has been set up it can only be created, not modified.
Lastly, I think you have to explicitly tell EF that existingEntity has been modified.
var entity = new Entity(args0, args1, args2)
entity.Id = existingEntity.Id;
db.Entity.Attach(entity);
db.SaveChanges();