3

I'm developing an Asp.Net MVC Application and trying to make a generic method to check if a specific record exists in DB or not by using passed entityId to this method. Something like below:

public bool CheckIfUserExistsByUserId(int userId)
{
    return _userRepository.DbSet().Any(u => u.Id == userId);
}

But this method only checks _userRepository and accepts an integer as entityId.

My problem is where I want to put this generic method as a general method in my BaseService just like other general methods that I write below:

public class BaseService<TModel> : IBaseService<TModel> where TModel : class
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IBaseRepository<TModel> _baseRepository; 

    public BaseService(IUnitOfWork unitOfWork, IBaseRepository<TModel> baseRepository)
    {
        _unitOfWork = unitOfWork;
        _baseRepository = baseRepository;
    }

    public BaseService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public void Add(TModel entity)
    {
        this._baseRepository.Add(entity);
    }

    public void Remove(TModel entity)
    {
        this._baseRepository.Remove(entity);
    }

    /// <summary>
    /// Remove All Provided Items At Once .
    /// </summary>
    /// <param name="itemsToRemove"></param>
    public void RemoveRange(IEnumerable<TModel> itemsToRemove)
    {
        _baseRepository.RemoveRange(itemsToRemove);
    }

    public TModel FindById<T>(T key)
    {
        return _baseRepository.FindById(key);
    }

    public void Commite()
    {
        this._unitOfWork.Commite();
    }
}

But in this case, I can't get the primary key field of the entity that passed to my method from Entity Framework. Also, I don't want to pass the primary key field of the entity to my method in everywhere I use the mentioned method. Is it possible to getting the primary key field of the entity in EF?

Edit: According to request of the other users I added my IBaseRepository source code here:

public interface IBaseRepository<TModel> where TModel : class
{
    /// <summary>
    /// Add New Entity .
    /// </summary>
    /// <param name="entity"></param>
    void Add(TModel entity);

    /// <summary>
    /// Remove Item From Database .
    /// </summary>
    /// <param name="entity"></param>
    void Remove(TModel entity);

    /// <summary>
    /// Remove All Provided Items At Once .
    /// </summary>
    /// <param name="itemsToRemove"></param>
    void RemoveRange(IEnumerable<TModel> itemsToRemove);

    /// <summary>
    /// Get The Underlying Type's DbSet .
    /// </summary>
    /// <returns></returns>
    DbSet<TModel> DbSet();

    /// <summary>
    /// Find Item By Id .
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    TModel FindById<T>(T key);

}
Milad Rashidi
  • 1,296
  • 4
  • 22
  • 40
  • https://stackoverflow.com/questions/2958921/entity-framework-4-how-to-find-the-primary-key https://stackoverflow.com/questions/7253943/entity-framework-code-first-find-primary-key – Tibin Thomas Nov 01 '17 at 08:13
  • Thanks a lot Tibin Thomas for your help. But you sure about that this solution work for me? because of identity field of my tables haven't Id in all cases. Somewhere I have primary key field names like "AcademicFieldTypeID" or so on. – Milad Rashidi Nov 01 '17 at 08:27
  • 1
    You don't need yet another "generic repository". Those were needed in languages like Java that *didn't* have generics or LINQ and IQueryable. In C# they are a serious design flaw - for example `GetAllItems()` will load everything - why? You never need to do that. While something returing an `IQueryable` allows you to build a query. You *don't* need a generic `Find` when `.Any()` already works with *any* property. You don't need *another* Unit of Work, the *context* provides a large part of it already – Panagiotis Kanavos Nov 01 '17 at 08:38
  • If you insist though, google or search SO for other attempts to create generic repositories. At the very least, change `GetAllItems()` to either return an IList<> or an IQueryable. Enumerating an IEnumerable reruns the enumerator, iterator, or whatever code is behind that IEnumerable. By returning an `IList<>` with ToList() or ToArray() you avoid wasted enumerations and get indexing. By returning an IQueryable you can create other LINQ queries without loading anything until you call `ToList()` or `ToDictionary()` as needed. IQueryable won't load unnneeded properties either – Panagiotis Kanavos Nov 01 '17 at 08:42
  • @PanagiotisKanavos And on top of that, the service is yet another anemic wrapper around the repository and UoW. – Gert Arnold Nov 01 '17 at 08:44
  • If the repository ends up delegating everything to the *context*, it's a very strong sign that it should *be* the context. You *can* extend a context by the way, it's just a class. You can add your own methods, or you can implement an interface with domain specific methods. – Panagiotis Kanavos Nov 01 '17 at 08:52
  • I'm using UoW because of I used Autofac IOC in my project so I need to use it i think. – Milad Rashidi Nov 01 '17 at 10:06
  • I think it is not efficient if I use this solutions: https://stackoverflow.com/questions/7253943/entity-framework-code-first-find-primary-key or: https://stackoverflow.com/questions/2958921/entity-framework-4-how-to-find-the-primary-key because of those solutions make EF to load the frame work of context of all entities in memory and it is not an optimized way to get primary key field of an entity! – Milad Rashidi Nov 01 '17 at 10:13
  • I think your question title is a little bit misunderstanding – Babak Fakhriloo Nov 11 '17 at 12:26

3 Answers3

7

What you want is a generic method to see if a record of an entity exists or not. You can use Any() method of DBSet class, and pass it the condition to search for the record.

public bool CheckIfEntityExistsByEntityId<T>(Expression<Func<T,bool>> expr)
{
    return _baseRepository.DbSet().Any(u => expr);
}
Milad Rashidi
  • 1,296
  • 4
  • 22
  • 40
Babak Fakhriloo
  • 2,076
  • 4
  • 44
  • 80
2

You can use abstract base class for your Entities to always know key property

protected abstract class BaseEntity<TKey> where TKey: IConvertable
{
    public TKey Id {get;set;}
} 
Sonikas
  • 131
  • 1
  • 1
  • 11
1

You can use EF6 .Find method which

Finds an entity with the given primary key values.

Just as easy as:

public bool Exists(object key)
{
    return _baseRepository.DbSet().Find(key) != null;
}
Yeldar Kurmangaliyev
  • 33,467
  • 12
  • 59
  • 101