Set collection to modified Entity Framework - c#

How can I set a collection to modified in the same way that I would do
_context.Entry(thing).Property(x => x.MyProperty).isModified = true;
like:
_context.Entry(thing).Collection(x => x.MyCollection).isModified = true;
EDIT: The purpose of this, is that my collection is a list of objects stored in a lookup table. I will only have a list of stubs with their id's in this collection and I would like to update the relationships without messing with the audit values and whatever else is contains within the lookup objects.
For instance, a contact will have multiple contact types, which for whatever reason are complex objects in this scenario. I want to be able to add and remove types using only the FKs and let EF handle the relationship fixups.
public class Contact
{
public int Id {get;set;}
public list<ContactTypes> ContactTypes {get;set;}
//audit values/other properties
}
public class ContactType
{
public int Id {get;set;}
public string Value {get;set;}
}

context.Entry represents a single entity, never a collection. So you have to loop through the collection and mark each entity as modified.

If you have a list of ForeignKey objects, you probably know how frustrating it is to force EF's Relationship Fixup on them. Here's slick way to do that.
public void SetContactTypesToUnchanged(Contact contact)
{
contact.ContactTypes.Each(type => _context.Entry(type).State = EntityState.Unchanged);
_context.SaveChanges();
}

Related

Mapping with Dapper cascading objects

I'm refactoring an old query made with EF that's taking so much time.
I was wondering with Dapper if I can automatically map such objects
public class Chest
{
public Item Item {get;set;}
}
public class Item
{
public IList<Property> Properties {get;set;}
}
public class Property
{
public int Id {get;set;}
public string Description {get;set;}
}
Is there a way I can retrieve all those items as I would do with EF?
I've seen the Query and so on but I don't understand if it meets the case
Your model is pretty straight forward, since there's only 1 collection - IList<Property>, let's assume your query is Select Id, Description from PropertyTable, then using Dapper, you can do the following:
IList<Property> PropertyList = conn.Query<Property>("Select Id, Description from PropertyTable").ToList();
After that its simple assignment:
Chest chest = new Chest{Item = new Item{Properties = PropertyList}};
This still need extra assignment, since from Dapper you get IEnumerable<T> as result, there could be a Dapper Extension, which can directly fill the Chest object, if you provide explicit object mapping, though in my view its not required, since the solution is simple

EF6 Code First / Fluent API throwing error with Complex Types

I am experiencing problems in mapping a complex type on a model class (called Assignment) using EntityFramework 6.
I have the following Assignment model class (only the relevant members are shown):
public class Assignment
{
private AssignmentDueByInfo _dueIn;
public Assignment() {
_dueIn = new AssignmentDueByInfo(this)
}
public virtual AssignmentSettingInfo DueIn
{
get { return _dueIn; }
protected set { _dueIn = value; }
}
}
where AssignmentSettingInfo is defined as:
public class AssignmentSettingInfo
{
protected AssignmentSettingInfo(Assignment assignment)
{
Assignment = assignment;
}
protected readonly Assignment Assignment;
public virtual int? LessonId { get; protected set; }
public virtual Lesson Lesson { get; protected set; }
}
In Entity Framework 6, I have the following CodeFirst / Fluent API mapping for the Assignment class to a table in a database:
Property(t => t.DueIn.LessonId).HasColumnName("DueByLessonId");
HasOptional(x => x.DueIn.Lesson)
.WithMany(x => x.AssignmentsDue)
.HasForeignKey(x => x.DueIn.LessonId)
.WillCascadeOnDelete(true);
The mapping is throwing the following error:
The expression 'x => x.DueIn.Lesson' is not a valid property expression. The expression should represent a property: C#: 't => t.MyProperty'
Why is this happening and how should it be fixed?
Check out the documentation (it's quite old, but still holds true):
https://msdn.microsoft.com/en-us/library/bb738472(v=vs.100).aspx
This states that complex types cannot contain navigation properties.
I guess this has to do with the fact they do not have primary keys and they are not managed separately by the context. If they have no PK and cannot be identified by the context, they cannot be 'relationship ends' either.
You are trying to make SQL object oriented. The classes you define that represent a database table should be POCOs: fairly simple classes with only get and set properties.
You are doing way too much with fields. If you have to fill a field in one of the table classes, think again. I've never seen an example where the class that represents a database table needed any field, other than a simple get/set property.
If one of the classes relates to another class, this is done by foreign key, not by this.
Class Assignment represents a database table. This table is supposed to have certain columns and relations to other tables using foreign keys. Class Assignment should not contain smart things that are not part of the table.
for example I see that Assignment has a field named _dueIn of type AssignmentDueByInfo. Outsiders can access this field, but for them this field is not an AssignmentDueByInfo, but an AssignmentSettingInfo.
Decide for yourself what columns the Assignment should have.
Is there a reason to put the columns for an AssignmentSettingInfo in a different table? If I look at your code there can't be an AssignmentSettingInfo without an 'Assignmentand there can't be anAssignmentwithout anAssignmentSettingInfo`. So why put them in separate tables?
Another problem is the relation between AssignmentSettingInfo and Lesson. Is this a zero-or-one to one? Follow these guide lines
If you'd put 'AssignmentandAssignmentSettingInfo` in one table your code would be like
class Assignment
{
public int Id {get; set;}
// every assignment has exactly one AssignmentSettingInfo
// use aggregation (in entity framework terms: complextype)
public AssignmentSettingInfo AssignmentSettingInfo {get; set;}
}
[ComplexType]
class AssignmentSettingInfo
{
// Every AssignmentSettingInfo has zero or one Lesson
public virtual Lesson Lesson { get; set; }
}
class Lesson
{
public int Id {get; set;}
// relation between Line and AssignmentSettingInfo
...
}
I couldn't figure out the relation between Line and AssignmentSettingInfo. Visit:
One-to-One
one-to-many?
If you really want the AssignmentSettingInfo in a different table, configure it as a one-to-one method as shown in the links

Insert again elements from one list to the prior list

Here is my situation. I have 2 list of the same type. Imagine the names like these. FullList and ElementsRemoved. So in order to avoid the database roundtrip, anytime I delete an element from the fulllist I added to the list of ElementsRemoved in case of regret's user so he can revert the deletion.
I was thinking to loop inside my ElementsRemoved to insert them again into the FullList from where initially were removed.
There is any way to do this as simple with List Methods.
Something like
FullList.Insert, Add, ..... (x =>
in order to reduce line code and optimized?
Instead of deleting the item from your database consider using a flag in the table.
For example consider this entities table (written in TSQL):
CREATE TABLE Entity
(
Id INT IDENTITY PRIMARY KEY
,Name NVARCHAR(20) NOT NULL
,IsDelete BIT NOT NULL DEFAULT 0
);
This way you can set the IsDelete bit when the user deletes the entity which will prevent the data from being lost. The data can be pruned on a job in the off hours.
The would lead to only needing one list instead of keeping track of two lists.
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDelete { get; set; }
}
public static void UndoDelete(IEnumerable<Entity> fullList, int[] removedIds)
{
foreach(var entity in fullList.Where(e => removedIds.Contains(e.Id)))
{
entity.IsDelete = false;
}
}
In case you cannot modify your application.
You can simply add the entities back in.
See List(T).AddRange
var entitiesToAdd = new[] { 2, 3, 4 };
var entitiesToInsert = ElementsRemoved.Where(e => entitiesToAdd.Contains(e.Id));
FullList.AddRange(entitiesToInsert);
In your front end make a class that holds a bool and your object:
public class DelPair<T>{
public bool IsDeleted{get;set;}
public T Item{get;set;}
}
Now instead of using a list of objects use a list of DelPair<YourClass> and set IsDeleted=true when deleting.
This pattern will also allow you to track other things, such as IsModified if it comes to that.
Based on OP comment that he's using an ENTITY class and needs it to function as such:
One option is to make your DelPair class inherit ENTITY. Another may be to put implicit casting operator:
...
// not exactly sure about the signature, trial/error should do :)
public static implicit operator T(DelPair<T> pair)
{
return pair.Item;
}
Suppose you have an element having a field id which uniquely identifies it.
class Element{public int id;}
In that case you can do this
FullList.Add(ElementsRemoved.FirstOrDefault(e=>e.id==id));
In case you want to add all elements use AddRange
FullList.AddRange(ElementsRemoved);
You can use the AddRange method
FullList.AddRange(ElementsRemoved);
But consider doing this
public class YourClass
{
public string AnyValue{get;set;}
public bool IsDeleted{get;set;}
}
And you have list like this List < YourClass> FullList. Now whenever user removes any item you just set the
IsDeleted = true
of the item that is removed. This will help you in keeping just one list and adding removing from the list

C Sharp Object creating and referencing only 1 object in another class

I need to implement 1..* and 1..1 relationships in a store scenario application.(Classes: Member, Order, OrderLine, Product, Program, User) How do i go about a 1 user only having 1 Order that can have many OrderLines (preferably using a List structure?
This is my User class:
namespace ConsoleApplication1
{
public class User
{
private string ffName;
private string llName;
private int id = 0;
//Constructor
public User(string firstName, string lastName)
{
fName = firstName;
lName = lastName;
}
public User() {}
//Overrides
public override bool Equals(object obj)
{
return obj.ToString() == this.ToString();
}
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}
public override string ToString()
{
string myUser;
myUser = string.Format("First Name: {0}; Last Name: {1}", fName, lName);
return myUser;
}
// Properties
public string fName
{
get
{
return ffName;
}
set
{
ffName = value;
}
}
public string lName
{
get
{
return llName;
}
set
{
llName = value;
}
}
}
}
You can have an Order class and an OrderLine class. The Order class will have a List of OrderLines and the User class can have a Order member.
Something like:
public class User
{
private string ffName;
private string llName;
private int id = 0;
private Order order = null;
//Constructor
public User(string firstName, string lastName)
{
fName = firstName;
lName = lastName;
}
...
}
public class Order
{
List<OrderLine> orderLines = null;
}
public class OrderLine
{
}
You have to implement the Order and the OrderLine class as:
class OrderLine
{
//some code
}
class Order
{
List<OrderLine> lstOrderLine;
//some code
}
Then add the Order class to your user class.
Edit: Removed snarkyness and attitude :)
First you need an order (hint you are going to need a class for that). Now the order needs to be attched to a user. So add a field of type User. That takes care of one order one user. (Note that a user can make more than one order)
So now you order is missing lines. Add another member variable that is a list of line types. Now in your order you need to add methods to add, remove and query order lines.
Edit: The question was raised what was meant by "add a field". Add a field means add a property or private member. When you are doing this you are doing the technical term of composition. Composition is commonly explained as a "has a" relationship. So an order "has a user" and "has a list of order lines"
Class User()
{
public string firstName { get; set; }
public string lastName {get; set; }
public int id { get; set;}
}
Class OrderLine()
{
}
Class Order()
{
private List<OrderLine> orderLines;
public User submitter { get; set;}
public Order()
{
orderLines = new List<OrderLine>();
}
public void AddOrderLine(OrderLine newOrderLine)
{
this.orderLines.Add(newOrderLine);
}
public IList<OrderLine> GetOrderLines()
{
return this.orderLines;
}
}
Example
User customer1 = new User();
// Initialize customer1 values...
Order someOrder = new Order();
someOrder.submitter = customer1;
someOrder.AddOrderLine(new OrderLine());
EDIT: Changed Member class to User class
Your most recent comment cleared up your question:
Its not hard to create each one i just dont understand how to get the relationship to work with 1..* or 1..1. If i create an Order i can always create another order
So, let's talk about the types of relationships.
Relationship types
Relationship types don't talk about absolute numbers of entities in the system. They just talk about numbers of entities in relation to other entities.
1:1 Relationship
This means that the two entity types must exist in pairs. If one entity of type A exists, then only one entity of type B can exist. For example, your User and Order. An order can't exist without a User, and a User can only have one Order. This doesn't mean there is only one User - there could be 42 users. This just means that if an Order exists, a User must also exist, and that the User can only have one Order.
There is a strict and less strict version of this. Technically, I just described something like a 1:{0 or 1} relationship. In a real 1:1 relationship you would require that the Order exists if the User exists. Neither could exist if the other didn't exist. However this constraint is usually relaxed when talking about relational databases (but only in one direction - in this case you still can't have an Order without a User).
You can model this relationship with code like this:
public class User
{
public Order Order { get; set; }
}
public class Order
{
// You could put a reference here back to the User if you want...
}
Note that it is a bit weird to only support only one Order for a User. It makes more sense to make it 1:*. But if that is a requirement of your assignment, then this is how you'd model it.
1:* Relationship
This is similar to the 1:1 relationship. But it relaxes some of the restrictions so that if an entity of type A exists, then any number (including zero) of type B can exist. The example is the Order and OrderLine. Again, there is no restriction on how many of either entity type exist. There could be 57 orders in the system. You just can't have an OrderLine without an Order, and there could be multiple OrderLines per Order.
You can model this relationship with code like this:
public class Order
{
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
// You could put a reference here back to the Order if you want...
}
Enforcing relational concepts in code
I can't speak for your assignment, so make sure you back up what I am saying here against what your assignment requires.
You should not try to enforce basic relational concepts like these in code. The database is better at it, has better (declarative) language to describe the relationships, and is going to be your ultimate source of data for the system.
Instead, you should just do a soft model that follows the relationships (as the code samples above do), and let the database do the real policing of those constraints.
Examples:
You should not try to restrict construction of Order types in code, and you shouldn't require a User to exist to construct an Order (as code entities).
You should not require an Order to exist to create an OrderLine (as code entities).
Trying to put these sorts of restrictions in code buys you nothing. When you persist the entities to the database, the database will ensure these relationships for you (assuming you've set it up correctly, which you will learn to do). Your error will be caught, and you'll learn habits that avoid these types of errors very quickly.
Trying to put these sorts of restrictions in code hurts you. It will be harder to write your program, and it will be harder to write unit tests for your code.
For example, consider an algorithm or test that compares OrderLine values. Maybe you want it to compare to a hypothetical OrderLine. If you had relational restrictions in place in your code, you'd also have to create a hypothetical Order and User. Would you also compare the hypothetical User and Order to the real ones? What if your algorithm shouldn't care what User or Order it originated from? If you're not going to compare them, why bother creating them to begin with?
So: Don't worry about it. Softly model your relationships so that it is easy to navigate between your objects, and let the database do your strict relationship validations for you.

EF4.1 POCO: Why should I use ICollection

In nearly all examples of POCO classes created for Entity Framework 4.1 collections are defined using the ICollection interface:
public class TravelTicket
{
public virtual int Id { get; set; }
public string Destination { get; set; }
public virtual ICollection<Person> Members { get; set; }
}
However, this causes an issue in my code where I need to access a member of the collection by index, e.g.:
Person Paul = TravelTicket.Members[3];
Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.ICollection
So how do I get round this problem and should I always use ICollection for my POCO collections?
It is because once you mark your navigation property virtual the proxy of your entity is created and it uses HashSet for navigation properties - hash set doesn't allow indexing. Accessing related entities by index doesn't seem like good approach because retrieving entity from database doesn't ensure that related entities will always have the same index in the collection.
Just use ToList():
Person Paul = TravelTicket.Members.ToList()[3];
EF isn't going to query data until you actually try to access it - and a collection doesn't try until you iterate it, while ToList must instantiate each instance.
Even better, be more specific:
Person Paul = TravelTicket.Members.Where(m=>m.Id == 3); // or some such similar filter
Then you only instance ONE Member - the one you want.
Note you may need Members.AsQueryable().Where instead - I never remember...
ICollection implements IEnumerable, you should be able to get the item by index using Enumerable.ElementAt<TSource> method

Categories