-1
\$\begingroup\$

I currently have two projects. One called Models, a second called TaskEntityModels, a model first Entity Framework 6 project.

Here is an example of a class in my models project:

public class ImportRequest
{
    private readonly TaskEntities _taskEntities;

    public ImportRequest(TaskEntities taskEntities)
    {
        _taskEntities = taskEntities;
    }

    public TASK Get(Guid guid)
    {
        return _taskEntities.TASKS.FirstOrDefault(t => t.TASK_SYS_GUID.Equals(guid));
    }

Does it make sense to have this LINQ to SQL code in the models project?

Or would it be better to be encapsulated in the EF project, perhaps in a partial class with a method GetTask(guid)? I'm trying to choose the best layers for my application.

\$\endgroup\$
2
\$\begingroup\$

Your models project should contain only functionality related to data models such as: data models themselves, data context, repositories.

Provided code does a simple thing: gets a model based on its identifier, so this can be included in a Repository. E.g:

public class Repository<T> : IRepository<T> 
    where T : class, new()
{
    // for DI, ITaskEntities can be used and your context implements the interface
    private TaskEntities _context;

    public Repository(TaskEntities context)
    {
        this._context = context;
    }

    public IQueryable<T> All
    {
        get { return _context.Set<T>().AsQueryable(); }
    }

    public IQueryable<T> AllNoTracking
    {
        get { return _context.Set<T>().AsNoTracking(); }
    }

    public T Get(int id)
    {
        return _context.Set<T>().Find(id);
    }

    // works only if guid is marked as Key, otherwise a special method must be used
    public T Get(Guid guid)
    {
        return _context.Set<T>().Find(id);
    }

    public void Delete(T entity)
    {
        if (_context.Entry(entity).State == EntityState.Detached)
            _context.Set<T>().Attach(entity);
        _context.Set<T>().Remove(entity);
    }

    public void Insert(T entity)
    {
        _context.Set<T>().Add(entity);
    }

    public void Update(T entity)
    {
        _context.Set<T>().Attach(entity);
        _context.Entry(entity).State = EntityState.Modified;
    }
}

where IRepository contains definitions for all public methods in the class above, thus allowing the repository to be injected.

So, your service layer should not work directly with EF data context, but rather using the appropriate repository:

// normally, context and repository should be injected using DI
var taskRepo = new Repository<TASK>(context);
var theTask = taskRepo.Get(myUid);

If the guid is not a key, the search can be performed like this:

// use AllNoTracking, if the model may not be tracked (is not changed and the changes persisted)
var theTask = taskRepo.All.Where(t => t.TASK_SYS_GUID.Equals(guid))

A possible approach is to put all the repositories in a UnitOfWork class, so that multiple changes can be done in a single unit of work (transaction). Something like this:

public class UnitOfWork : IUnitOfWork
{
    #region Members
    private TaskEntities _context;
    #endregion

    #region Properties
    public IRepository<TASK> TaskRepository { get; private set; }
    #endregion

    #region Constructor
    public UnitOfWork(TaskEntities context,
        IRepository<TASK> taskRepository)
    {
        this._context = context;

        TaskRepository = taskRepository ;
    }
    #endregion

    #region Methods
    public IRepository<T> GetRepository<T>()
        where T: class
    {
        Type thisType = this.GetType();
        foreach (var prop in thisType.GetProperties())
        {
            var propType = prop.PropertyType;

            if (!typeof(IRepository).IsAssignableFrom(propType))
                continue;

            var repoType = propType.GenericTypeArguments[0];
            if (repoType == typeof(T))
                return (IRepository<T>) prop.GetValue(this);
        }

        throw new ArgumentException(String.Format("No repository of type {0} found", typeof(T).FullName));
    }

    public void SaveChanges()
    {
        _context.SaveChanges();
    }
    #endregion
}

Other repositories can be added here and, if DI is used you should never instantiate this class yourself, so that all constructor parameters are injected.

So, your flow should be like this:

  • instantiate a UnitOfWork
  • fetch and change models as needed
  • finally, call SaveChanges

I would also mention a little about your naming. C# uses a CamelCase notation for most identifiers (class names, public methods etc.), so you should try to use also for your structures:

TASK -> Task
TASK_SYS_GUID -> TaskSysGuid
\$\endgroup\$
  • \$\begingroup\$ Upvoted, but please, read this answer on Programmers.SE, which says everything I'd have to say about #region. \$\endgroup\$ – Mathieu Guindon Feb 18 '16 at 18:01
  • \$\begingroup\$ I'm using the Entity Framework with Oracle, and it stupidly makes the casing all upper case \$\endgroup\$ – Hoppe Feb 18 '16 at 20:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.