generalize dbcontext AddOrUpdate - entity-framework

My code :
public class BaseController
{
public object AddUpdate(object obj)
{
using (var db = new StoreModel())
{
string nameObj = obj.ToString().Substring(obj.ToString().LastIndexOf(".") + 1);
var property = db.GetType().GetProperty(nameObj);
((DbSet<CrmTicket>)property.GetValue(db)).AddOrUpdate((CrmTicket)obj);
db.SaveChanges();
return obj;
}
}
}
I would like generalize AddOrUpdate.
This code work but it's not generic, you can see CrmTicket.
I can not put a Type in his place.
((DbSet<obj.GetType()>)property.GetValue(db)).AddOrUpdate((obj.GetType())obj);
Could you help me ?
Thank you.

You could simply use generics. There are multiple ways of doing this pretty easily. Here's one way:
public class BaseController
{
protected T AddOrUpdate<T>(T obj) where T : BaseEntity
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
using (StoreModel context = new StoreModel())
{
T entity = context.Set<T>().Find(obj.Id);
// the entity doesn't exists yet, so we add it
if (entity == null)
{
context.Set<T>().Add(entity);
}
// the entity exists, so we must update it
else
{
// do you update logic, like : entity.MyString = obj.MyString
// ...
// Note : there is no need to attach the entity because the Find method has already done it.
}
// Everything is done.
context.SaveChanges();
}
return obj;
}
}
// This is your base class for all entity.
// If you want to use generics and have an AddOrUpdate method,
// you must have something to rely on where you want to check if the object you want to insert is already in Db.
public class BaseEntity
{
public int Id { get; set; } // you should configure this as an Primary Key with Identity
}
But I think this is not a good idea... With that kind of considerations, you should look at repositories: http://www.tugberkugurlu.com/archive/generic-repository-pattern-entity-framework-asp-net-mvc-and-unit-testing-triangle

Related

How can I return the ID of the Inserted Record in Entity Framework Generic Insert Method?

Here is the generic insert method. I need your suggestion to return the ID of the inserted record.
public static void Create<T>(T entity) where T : class
{
using (var context = new InformasoftEntities())
{
DbSet dbSet = context.Set<T>();
dbSet.Add(entity);
context.SaveChanges();
}
}
Arturo Martinex is correct in his comment.
Entity framework fixes up the ID's during SaveChanges so it's already updated in the entity you passed in to the method.
To do specifically what you ask you could change your generic constraint from class to a new abstract class that all your entities inherit, which defines the key in that class.
public static int Create<T>(T entity) where T : BaseEntity
{
using (var context = new InformasoftEntities())
{
DbSet dbSet = context.Set<T>();
dbSet.Add(entity);
context.SaveChanges();
return entity.Id;
}
}
public abstract class BaseEntity
{
int Id { get; set;}
}
This technique is more useful in an InsertOrUpdate method
Another way to work with keys inside generic methods is to interrogate the MetaData as described here:
The key to AddOrUpdate
You need a little modification:
You need to create an IHasAutoID that implemented by Entity
public interface IHasAutoID {
int getAutoId();
}
In Entity Class
public class EntityA : IHasAutoID {
public int getAutoId() {
return pk; // Return -1 If the entity has NO Auto ID
}
}
In your Create function
public static int Create<T>(T entity) where T : class
{
using (var context = new InformasoftEntities())
{
DbSet dbSet = context.Set<T>();
dbSet.Add(entity);
context.SaveChanges();
if (entity is IHasAutoID) {
return ((IHasAutoID)entity).getAutoId();
}
return -1; // entity is NOT IHasAutoID)
}
}
NOTES:
If you are sure all tables have Auto ID with named "Id". You don't need to create Interface IHasAutoID. In Create function, after SaveChanges, You use REFLECTION to get value of Id property, but this way is not recommended!
public async Task<int> Add(TEntity entity)
{
await _context.Set<TEntity>().AddAsync(entity);
await Save();
return Task.FromResult(entity).Id;
}

How to create generic EF Insert method?

I'd like to create a generic C# class with a method that will add a row to a database using Entity Framework.
I have one table called Address. I've written the following code to add an address to the database:
public class AddressExchange
{
public int Insert(Address address)
{
using (var db = new DemoWebEntities())
{
//db.AddObject("Address", address);
db.Addresses.AddObject(address);
db.SaveChanges();
return address.Id;
}
}
}
I would like to write a generic class that will perform this operation for any entity in my EDMX. I think that it should look something like this:
public class EntityExchange<T, KeyType>
{
public KeyType Insert(T t)
{
using (var db = new DemoWebEntities())
{
// The entity set name might be wrong.
db.AddObject(typeof(T).Name, t);
// EF doesn't know what the primary key is.
return t.Id;
}
}
}
I think it may be possible to use the AddObject method to add the object to the database, but the entityset name is not necessarily the same as the type name, especially if it has been pluralized!
I also want to return the primary key to the caller, but I don't know how to tell which field contains the primary key.
I have a generic InsertOrUpdate method in a generic repository that also ensures proxies are created. (Proxies are required to support lazy loading and if you create an entity using "new", then proxies are not created). See the question here
public class RepositoryBase<T> : IRepository<T> where T : ModelBase
{
public virtual T InsertOrUpdate(T e)
{
DbSet<T> dbSet = context.Set<T>();
//Generate a proxy type to support lazy loading
T instance = dbSet.Create();
DbEntityEntry<T> entry;
if (e.GetType().Equals(instance.GetType()))
{
//The entity being added is already a proxy type that
//supports lazy loading just get the context entry
entry = context.Entry(e);
}
else
{
//The entity being added has been created using the "new" operator.
//Attach the proxy
//Need to set the ID before attaching or we get
//The property 'ID' is part of the object's key
//information and cannot be modified when we call SetValues
instance.ID = e.ID;
entry = context.Entry(instance);
dbSet.Attach(instance);
//and set it's values to those of the entity
entry.CurrentValues.SetValues(e);
e = instance;
}
entry.State = e.ID == default(int) ?
EntityState.Added :
EntityState.Modified;
return e;
}
}
public abstract class ModelBase
{
public int ID { get; set; }
}
Note that all the models inherit ModelBase so that handles the ID issue and I return the entity rather than just the ID. That is probably not strictly necessary since a reference to the entity is passed in and EF performs fixup on the ID anyway so you can always access it from the refernce passed in.
This might be reliant on a particular version on Entity framework however this is how I do it
public void Create(T entity)
{
using (var db = new DemoWebEntities())
{
db.Set<T>().Add(entity);
}
}
For the primary key issue, can you use partial classes to make your entities implement an interface, something like this:
public interface IEntity
{
Guid PrimaryKey { get; }
}
Your entity classes would then return the appropriate value:
public partial class EntityType : IEntity
{
public Guid PrimaryKey
{
get
{
return this.WhateverId; // Return the primary key
}
}
}
Then, constrain your method to only accept IEntity:
public class EntityExchange<T, KeyType> where T : IEntity
And finally return the primary key after the insert:
return t.PrimaryKey;
May be it can help you.
public T Add(T model)
{
using (BigConceptEntities entity = new BigConceptEntities())
{
entity.Set<T>().Add(model);
entity.SaveChanges();
return model;
}
}

Entity Framework 5 - Immediately refresh DbContext after saving changes

I have an MVC application that uses Entity Framework 5. In few places I have a code that creates or updates the entities and then have to perform some kind of operations on the updated data. Some of those operations require accessing navigation properties and I can't get them to refresh.
Here's the example (simplified code that I have)
Models
class User : Model
{
public Guid Id { get; set; }
public string Name { get; set; }
}
class Car : Model
{
public Guid Id { get; set; }
public Guid DriverId { get; set; }
public virtual User Driver { get; set; }
[NotMapped]
public string DriverName
{
get { return this.Driver.Name; }
}
}
Controller
public CarController
{
public Create()
{
return this.View();
}
[HttpPost]
public Create(Car car)
{
if (this.ModelState.IsValid)
{
this.Context.Cars.Create(booking);
this.Context.SaveChanges();
// here I need to access some of the resolved nav properties
var test = booking.DriverName;
}
// error handling (I'm removing it in the example as it's not important)
}
}
The example above is for the Create method but I also have the same problem with Update method which is very similar it just takes the object from the context in GET action and stores it using Update method in POST action.
public virtual void Create(TObject obj)
{
return this.DbSet.Add(obj);
}
public virtual void Update(TObject obj)
{
var currentEntry = this.DbSet.Find(obj.Id);
this.Context.Entry(currentEntry).CurrentValues.SetValues(obj);
currentEntry.LastModifiedDate = DateTime.Now;
}
Now I've tried several different approaches that I googled or found on stack but nothing seems to be working for me.
In my latest attempt I've tried forcing a reload after calling SaveChanges method and requerying the data from the database. Here's what I've done.
I've ovewrite the SaveChanges method to refresh object context immediately after save
public int SaveChanges()
{
var rowsNumber = this.Context.SaveChanges();
var objectContext = ((IObjectContextAdapter)this.Context).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, this.Context.Bookings);
return rowsNumber;
}
I've tried getting the updated object data by adding this line of code immediately after SaveChanges call in my HTTP Create and Update actions:
car = this.Context.Cars.Find(car.Id);
Unfortunately the navigation property is still null. How can I properly refresh the DbContext immediately after modifying the data?
EDIT
I forgot to originally mention that I know a workaround but it's ugly and I don't like it. Whenever I use navigation property I can check if it's null and if it is I can manually create new DbContext and update the data. But I'd really like to avoid hacks like this.
class Car : Model
{
[NotMapped]
public string DriverName
{
get
{
if (this.Driver == null)
{
using (var context = new DbContext())
{
this.Driver = this.context.Users.Find(this.DriverId);
}
}
return this.Driver.Name;
}
}
}
The problem is probably due to the fact that the item you are adding to the context is not a proxy with all of the necessary components for lazy loading. Even after calling SaveChanges() the item will not be converted into a proxied instance.
I suggest you try using the DbSet.Create() method and copy across all the values from the entity that you receive over the wire:
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
this.Context.Entry(newEntry).CurrentValues.SetValues(obj);
return newEntry;
}
UPDATE
If SetValues() is giving an issue then I suggest you try automapper to transfer the data from the passed in entity to the created proxy before Adding the new proxy instance to the DbSet. Something like this:
private bool mapCreated = false;
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
if (!mapCreated)
{
Mapper.CreateMap(obj.GetType(), newEntry.GetType());
mapCreated = true;
}
newEntry = Mapper.Map(obj, newEntry);
this.DbSet.Add(newEntry;
return newEntry;
}
I use next workaround: detach entity and load again
public T Reload<T>(T entity) where T : class, IEntityId
{
((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
return _dbContext.Set<T>().FirstOrDefault(x => x.Id == entity.Id);
}

ObjectContext.CreateObject<T>: Create ComplexObject

Could someone please explain the following:
I have the following code that creates an instance of a type specified:
MethodInfo methodInfo = this._dbContext.GetType().GetMethod("CreateObject").MakeGenericMethod(instanceType);
object invokedObject = methodInfo.Invoke(this._dbContext, null);
Where the _dbContext in my Entity Model.
When I try to create an instance of the following class all is happy and it works:
public partial class User : EntityObject
{
....
}
But when I try the same within the following class - I receive an error:
The member with identity 'MyNamespace.Account' does not exist in the metadata collection.
public partial class Account : ComplexObject
{
....
}
Could someone please explain why I'm able to create an instance / object of an EntityObject but not a ComplexObject ?
Many thanks!!
[UPDATE]
public ObjectSet<User> Users
{
get
{
if ((_Users == null))
{
_Users = base.CreateObjectSet<User>("Users");
}
return _Users;
}
}
private ObjectSet<User> _Users;
There are actually 2 problems:
As noted in your updated question, your context doesn't contain reference to Account, you need to add it before requesting it (using the 'CreateObject' method) from the context:
public ObjectSet<Account> Accounts
{
get
{
if ((_Accounts == null))
{
_Accounts = base.CreateObjectSet<Account>("Accounts");
}
return _Accounts;
}
}
private ObjectSet<Account> _Accounts;
Since your Account is a ComplexType i don't think you'll be able to use it as an ObjectSet<>, since ComplexType does not have an Identity and can only be used as a part of a parent Entity.

Can I hide my ICollection<T> fields when I have a one-to-many mapping in EF4 code-only?

My domain classes that have one-to-many mappings generally take the following form (untested code):
public Customer Customer
{
// Public methods.
public Order AddOrder(Order order)
{
_orders.Add(order);
}
public Order GetOrder(long id)
{
return _orders.Where(x => x.Id).Single();
}
// etc.
// Private fields.
private ICollection<Order> _orders = new List<Order>();
}
The EF4 code-only samples I've seen expose a public ICollection when dealing with one-to-many relationships.
Is there a way to persist and restore my collections with exposing them? If not, it would appear that my domain objects will be designed to meet the requirements of the ORM, which seems to go against the spirit of the endeavour. Exposing an ICollection (with it's Add, etc. methods) doesn't seem particularly clean, and wouldn't be my default approach.
Update
Found this post that suggests it wasn't possible in May. Of course, the Microsoft poster did say that they were "strongly considering implementing" it (I'd hope so) and we're half a year on, so maybe there's been some progress?
I found that whatever was done, EF requires the ICollection<T> to be public. I think this is because when the objects are loaded from the database, the mapping looks for a collection property, gets the collection and then calls the Add method of the collection to add each of the child objects.
I wanted to ensure that the addition was done through a method on the parent object so created a solution of wrapping the collection, catching the add and directing it to my preferred method of addition.
Extending a List and other collection types was not possible because the Add method is not virtual. One option is to extend Collection class and override the InsertItem method.
I have only focussed on the Add, Remove, and Clear functions of the ICollection<T> interface as those are the ones that can modify the collection.
First, is my base collection wrapper which implements the ICollection<T> interface
The default behaviour is that of a normal collection. However, the caller can specify an alternative Add method to be called. In addition, the caller can enforce that the Add, Remove, Clear operations are not permitted by setting the alternatives to null. This results in NotSupportedException being thrown if anyone tries to use the method.
The throwing of an exception is not as good as preventing access in the first place. However, code should be tested (unit tested) and an exception will be found very quickly and a suitable code change made.
public abstract class WrappedCollectionBase<T> : ICollection<T>
{
private ICollection<T> InnerCollection { get { return GetWrappedCollection(); } }
private Action<T> addItemFunction;
private Func<T, bool> removeItemFunction;
private Action clearFunction;
/// <summary>
/// Default behaviour is to be like a normal collection
/// </summary>
public WrappedCollectionBase()
{
this.addItemFunction = this.AddToInnerCollection;
this.removeItemFunction = this.RemoveFromInnerCollection;
this.clearFunction = this.ClearInnerCollection;
}
public WrappedCollectionBase(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) : this()
{
this.addItemFunction = addItemFunction;
this.removeItemFunction = removeItemFunction;
this.clearFunction = clearFunction;
}
protected abstract ICollection<T> GetWrappedCollection();
public void Add(T item)
{
if (this.addItemFunction != null)
{
this.addItemFunction(item);
}
else
{
throw new NotSupportedException("Direct addition to this collection is not permitted");
}
}
public void AddToInnerCollection(T item)
{
this.InnerCollection.Add(item);
}
public bool Remove(T item)
{
if (removeItemFunction != null)
{
return removeItemFunction(item);
}
else
{
throw new NotSupportedException("Direct removal from this collection is not permitted");
}
}
public bool RemoveFromInnerCollection(T item)
{
return this.InnerCollection.Remove(item);
}
public void Clear()
{
if (this.clearFunction != null)
{
this.clearFunction();
}
else
{
throw new NotSupportedException("Clearing of this collection is not permitted");
}
}
public void ClearInnerCollection()
{
this.InnerCollection.Clear();
}
public bool Contains(T item)
{
return InnerCollection.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
InnerCollection.CopyTo(array, arrayIndex);
}
public int Count
{
get { return InnerCollection.Count; }
}
public bool IsReadOnly
{
get { return ((ICollection<T>)this.InnerCollection).IsReadOnly; }
}
public IEnumerator<T> GetEnumerator()
{
return InnerCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return InnerCollection.GetEnumerator();
}
}
Given that base class we can use it in two ways. Examples are using the original post objects.
1) Create a specific type of wrapped collection (For example, List)
public class WrappedListCollection : WrappedCollectionBase, IList
{
private List innerList;
public WrappedListCollection(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.innerList = new List<T>();
}
protected override ICollection<T> GetWrappedCollection()
{
return this.innerList;
}
<...snip....> // fill in implementation of IList if important or don't implement IList
}
This can then be used:
public Customer Customer
{
public ICollection<Order> Orders {get { return _orders; } }
// Public methods.
public void AddOrder(Order order)
{
_orders.AddToInnerCollection(order);
}
// Private fields.
private WrappedListCollection<Order> _orders = new WrappedListCollection<Order>(this.AddOrder, null, null);
}
2) Give a collection to be wrapped using
public class WrappedCollection<T> : WrappedCollectionBase<T>
{
private ICollection<T> wrappedCollection;
public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.wrappedCollection = collectionToWrap;
}
protected override ICollection<T> GetWrappedCollection()
{
return this.wrappedCollection;
}
}
which can be used as follows:
{
public ICollection Orders {get { return _wrappedOrders; } }
// Public methods.
public void AddOrder(Order order)
{
_orders.Add(order);
}
// Private fields.
private ICollection<Order> _orders = new List<Order>();
private WrappedCollection<Order> _wrappedOrders = new WrappedCollection<Order>(_orders, this.AddOrder, null, null);
}
There are some other ways to call the WrappedCollection constructors
For example, to override add but keep remove and clear as normal
private WrappedListCollection<Order> _orders = new WrappedListCollection(this.AddOrder, (Order o) => _orders.RemoveFromInnerCollection(o), () => _orders.ClearInnerCollection());
I agree that it would be best if EF would not require the collection to be public but this solution allows me to control the modification of my collection.
For the problem of preventing access to the collection for querying you can use approach 2) above and set the WrappedCollection GetEnumerator method to throw a NotSupportedException. Then your GetOrder method can stay as it is. A neater method however may be to expose the wrapped collection. For example:
public class WrappedCollection<T> : WrappedCollectionBase<T>
{
public ICollection<T> InnerCollection { get; private set; }
public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.InnerCollection = collectionToWrap;
}
protected override ICollection<T> GetWrappedCollection()
{
return this.InnerCollection;
}
}
Then the call in the GetOrder method would become
_orders.InnerCollection.Where(x => x.Id == id).Single();
Another way to accomplish this would be to create an associated interface for each of your POCOs to expose only what you want outside of the persistence/domain layers. You can also interface your DbContext class to also hide and control access to the DbSet collections. As it turns out, the DbSet properties can be protected, and the model builder will pick them up when it's creating tables, but when you try to access the collections they will be null. A factory method (in my example, CreateNewContext) can be used instead of the constructor to get the interfaced DbContext to conceal the DbSet collections.
There's quite a bit of extra effort in coding, but if hiding implementation details within the POCOs is important, this will work.
UPDATE: It turns out you CAN populate DBSets if they are protected, but not directly in the DBContext. They can't be aggregate roots (i.e. accessibility of the entity has to be through a collection in one of the public DBSet entities). If hiding the implementation of DBSet is important, the interface pattern I've described is still relevant.
public interface ICustomer
{
void AddOrder(IOrder order);
IOrder GetOrder(long id);
}
public Customer : ICustomer
{
// Exposed methods:
void ICustomer.AddOrder(IOrder order)
{
if (order is Order)
orders.Add((Order)order);
else
throw new Exception("Hey! Not a mapped type!");
}
IOrder ICustomer.GetOrder(long id)
{
return orders.Where(x => x.Id).Single();
}
// public collection for EF
// The Order class definition would follow the same interface pattern illustrated
// here for the Customer class.
public ICollection<Order> orders = new List<Order>();
}
public interface IMyContext
{
IEnumerable<ICustomer> GetCustomers();
void AddCustomer(ICustomer customerObject);
ICustomer CreateNewCustomer()
}
public class MyContext : DbContext, IMyContext
{
public static IMyContext CreateNewContext() { return new MyContext(); }
public DbSet<Customer> Customers {get;set;}
public DbSet<Order> Orders {get;set;}
public IEnumerable<ICustomer> GetCustomers()
{
return Customers;
}
public void AddCustomer(ICustomer customerObject)
{
if (customerObject is Customer)
Customers.Add((Customer)customerObject);
else
throw new Exception("Hey! Not a mapped type");
}
public ICustomer CreateNewCustomer()
{
return Customers.Create();
}
// wrap the Removes, Finds, etc as necessary. Remember to add these to the
// DbContext's interface
// Follow this pattern also for Order/IOrder
}
If you change the name of your _orders collection to the name of the orders table in your database, this should work. EF maps table/field names to collections/properties by convention. If you want to use a different name you could edit the mappings in the edmx file.
AFAIK you can just leave the private modifier as it is. Collections do not need to be public.