2

I'd like to insert my UserCompany object into the database trought a single method. Such as passing the element to this function, take it and "insert in the right table".

Usually in Entity (such as LINQ to XML) I do somethings like:

db.Company.UsersCompany.Add(UserCompany);
db.SubmitChanges();

but the problem here is that I need to specify the table UsersCompany and Company before using the .Add(). I'd like (since I want to do ONE function for the insert for each type of object/table) get rid of this. Such as having a:

UserCompany.InsertMySelf();

or

db.SmartAdd(UserCompany);

and it know how to insert the table, where and how, automatically.

Is it possible to do this? Is there any strategies?

markzzz
  • 47,390
  • 120
  • 299
  • 507
  • 1
    Repository pattern on top of your Context? Switch based on Object type within that method? – Eric Hotinger Oct 03 '13 at 13:26
  • Can you give to me an example? – markzzz Oct 03 '13 at 13:27
  • 1
    yep, give me a few minutes to type some code. – Eric Hotinger Oct 03 '13 at 13:27
  • What do you mean by: `Usually in Entity (such as LINQ to XML) I do somethings like`? Entity Framework and LINQ to XML are not the same thing. In general, have you tried simply doing `db.UserCompanies.Add(UserCompany)`? If your `Company` object is part of the `UserCompany` object, the whole object graph should be inserted into the database correctly (that is both `Company` and `UserCompany` tables). If that's not the case, please provide your database structure and mapped entities, as well as the code you're trying. – Yakimych Oct 03 '13 at 13:36
  • @Yakimych: it was just an example. In the same way I do `db.UserCompanies.Add(UserCompany)`, I have to specify `UserCompanies`. So I need to specify the "parent" table every time. This means I cannot do a general "insert" function, because I need to pre-select which parent table is the target. Do you know what I mean? – markzzz Oct 03 '13 at 14:20

3 Answers3

2

You can solve this with generics:

Public Sub AddEntities(Of TEntity)(entities As IEnumerable(Of TEntity))
   For Each ent In entities
       _db.Set(Of TEntity).Add(ent)
   Next
   _db.SaveChanges()
End Sub

Sorry for using VB... In C#:

public void AddEntities<TEntity>(IEnumerable<TEntity> entities)
   {
     foreach(ent in entities)
     {
         _db.Set<TEntity>.Add(ent);
     }
     _db.SaveChanges();
   }
Dabblernl
  • 15,831
  • 18
  • 96
  • 148
1

In your Controller define a repository for yourself along these lines:

public class CompanyController : ApiController
{
    private readonly CompanyRepository _companyRepository;

    public CompanyController()
    {
        _companyRepository= new CompanyRepository(User);
    }

    [HttpPost]
    public Company PostCompany(Company comp)
    {
        _companyRepository.SmartAdd(comp);
    }
}

Define a repository with the definition:

public class CompanyRepository : EFContextProvider<CompanyContext>
{
    // Fields which can be used for security within the repository.
    public IPrincipal User { get; private set; }
    public string UserName { get; set; }

    public CompanyRepository (IPrincipal user)
    {
        User = user;
        UserName = user.Identity.Name;
    }

    public DbQuery<Object> SmartAdd(Object obj)
    {
        switch (obj.GetType)
        {
            case "":  // TODO...
              Context.Company.UsersCompany.Add(UserCompany);
                break;

            default:
                break;
        }
    }

There will have to be some adaption to suite your own needs, but this is the general idea.

Although there could potentially be a lot of cases within the switch, I assume you will do object validation and other things regardless, so you can easily do that here too.

Relevant links:

Eric Hotinger
  • 8,957
  • 5
  • 36
  • 43
  • This is what I'd like to avoid: a `select case`. What if I have 25 Tables? A switch with 25 case? Uhm... inconsistent... – markzzz Oct 03 '13 at 14:18
  • How is that inconsistent? It's all in one place and it's certainly more manageable for the people working with the actual API. You can break it up even more and keep abstracting it if you wanted, but I don't think that'll help your cause. Otherwise, define a type for each object in your database and then run SQL against every table's schema based on the object you're trying to save just to lose performance and space. – Eric Hotinger Oct 03 '13 at 14:25
0

You need to look at the generic repository. This pattern feeds all the CRUD through one base class. You can then inherit from this class to implement custom repositories where it's necessary

public class RepositoryBase<T> : IRepository<T> where T : ModelBase
{
    private readonly IUnitOfWork _UnitOfWork;
    //https://stackoverflow.com/questions/4442828/entity-framework-4-ctp-4-ctp-5-generic-repository-pattern-and-unit-testable/4458250#4458250


    protected MyContext Context { get { return _UnitOfWork.Context; } }

    public RepositoryBase(IUnitOfWork unitOfWork)
    {
        _UnitOfWork = unitOfWork;
    }

    public virtual T InsertOrUpdate(T e)
    {
        DbSet<T> dbSet = Context.Set<T>();

        DbEntityEntry<T> entry;
        if (e.GetType().BaseType != null && e.GetType().Namespace == "System.Data.Entity.DynamicProxies")
        {
            //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. 
            //Generate a proxy type to support lazy loading  and attach it
            T instance = dbSet.Create();
            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 virtual IQueryable<T> All
    {
        get
        {
            return Context.Set<T>(); 
        }
    }

    public virtual IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
    {
        IQueryable<T> query = All;
        foreach (var includeProperty in includeProperties)
        {
            query = query.Include(includeProperty);
        }
        return query;
    }

    public virtual T Find(int id)
    {
        T e = Context.Set<T>().Find(id);
        if (e == null)
            return null;

        return e;
    }

    public virtual void Delete(int id)
    {
        var e = Context.Set<T>().Find(id);

        if (e != null)
            Context.Set<T>().Remove(e);

    }
}

public abstract class ModelBase
{
    public int ID { get; set; }
}

References:

The repository and unit of work patterns

John Papa's original source

How to ensure proxies are created when using the repository pattern

Generic Repository Pattern

Community
  • 1
  • 1
Colin
  • 22,328
  • 17
  • 103
  • 197