views:

171

answers:

3

Hi. I'm working with MVC ASP.NET 3.5 SP1 on VS2008.
I'm looking for a way to abstract this three methods I have in my Users repository.

public User GetUser(Log log)
{
  return db.Users.FirstOrDefault(u => u.Logs.Any(l => l.id.Equals(log.id)));
}

public User GetUser(Product product)
{
  return db.Users.FirstOrDefault(u => u.Products.Any(pr => pr.id.Equals(product.id)));
}

public User GetUser(Photo photo)
{
  return db.Users.FirstOrDefault(u => u.Photos.Any(ph => ph.id.Equals(photo.id)));
}

My DB.edmx contains the models

User    [id, username, ...]  
Product [id, name, ...]
Photo   [id, name, ...]
Log     [id, data, ...]

Is it possible to have only ONE method for all of these (and future) methods based upon model.id search?

A: 
public User GetUser(Expression<Func<User, bool>> restriction)
{
  return db.Users.Where(restriction).FirstOrDefault();
}

Now use it:

var u = Repository.GetUser(u => u.Logs.Any(l => l.id.Equals(log.id)));

You can also use MS DynamicQuery:

using System.Linq.Dynamic;

//...
public User GetUser(string propertyName, int id)
{
    var restriction = propertyName + ".Any(id = @0)";
    return db.Users.Where(restriction, id).FirstOrDefault();
}

var u = Repository.GetUser("Logs", log.id);

I may not have the syntax quite correct, but you get the idea.

Craig Stuntz
Great. But I need a "more automated" expression, maybe using the class name.
Sig. Tolleranza
Then use MS Dynamic LINQ to do the same thing. I'll update the example.
Craig Stuntz
Thanks, I think I'll try the last one you suggest.
Sig. Tolleranza
A: 

If all the associated entities (Log, Product and Photo) will be searched by a common property (id INT) then maybe you could try something like this...

First, create an interface:

public interface IUserAssociation
{
    int id { get; }
}

Then each of the three classes would implement this interface like so:

public partial class Product : IUserAssociation
{
}

The the GetUser method would look like so:

public User GetUser<T>(T entity) where T : IUserAssociation
{
    var type = typeof(T);
    if (type == typeof(Log))
    {
        return db.Users.FirstOrDefault(u => u.Logs.Any(l => l.id.Equals(entity.id)));
    }
    else if (type == typeof(Product))
    {
        return db.Users.FirstOrDefault(u => u.Products.Any(pr => pr.id.Equals(entity.id)));
    }
    else if (type == typeof(Photo))
    {
        return db.Users.FirstOrDefault(u => u.Photos.Any(ph => ph.id.Equals(entity.id)));
    }
    else
    {
        throw new ArgumentException();
    }
}

Then you should be able to call GetUser and pass it a Log, Photo or Product entity from one method. It is not very elegant but would work for this specific situation.

300 baud
A: 

I like Craig's solution better but I'd suggest this:

Repository.GetUser(u => u.Logs, log);

Which will be possible if all your entities derive from

public interface IEntity { public int Id { get; } }

Then method will be like

public User GetUser<T, Y>(Func<T, IList<Y>> getlist, Y sample)
   where T: IEntity
   where Y: IEntity
{
  return db.Users.Select(x => getlist(x).Any(y => y.Id == sample.Id)).FirstOrDefault();
}

Also if we take idea of S#arp Architecture that if entity1.Id == entity2.Id (for persistent entities) then Equals(entity1, entity2) - we can use getlist(x).Contains(sample).

queen3