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?
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.
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.
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).
Related
I am designing something similar to facebook groups. I have user table and Community with one relationship table name CommunityUser. I want to know if the user already joined a community or not? This can take if the communityID exist against that userID in CommunityUser Table.
Here is my IsMember() function code.
public bool IsMember(string UserID, int CommunityID)
{
var MembershipUser = db.Users.Include(x => x.CommunityUsers).Where(s => s.Id == UserID).FirstOrDefault();
var Membership = MembershipUser.CommunityUsers.Count();
if (Membership > 0)
return true;
else
return false;
}
I have virtual connection in my User Table
public virtual ICollection<CommunityUser> CommunityUsers { get; set; }
public ApplicationUser()
{
CommunityUsers = new Collection<CommunityUser>();
}
If you are planning on returning a boolean value, just consider using the Any() method:
public bool IsMember(string UserID, int CommunityID)
{
// This will return if a given user exists
return db.Users.Any(u => u.Id == UserId);
}
Likewise, you could also consider pulling in the related CommunityUsers collection and checking that within your query as well:
public bool IsMember(string UserID, int CommunityID)
{
// This will return true if a given user exists and if a specific
// CommunityId is present for that user
return db.Users.Include("CommunityUsers")
.Any(u => u.Id == UserId && u.CommunityUsers.Any(c => c.Id == CommunityId));
}
public IEnumerable<Temp_Order> Get_Temp(string id)
{
//List<Temp_Order> data = new List<Temp_Order>();
IEnumerable<Temp_Order> data = db.Temp_Order
.Join(db.Items,
t_id => t_id.ItemId,
I_id => I_id.ItemId,
(t_id, I_id) => new { t_id.Quantity, I_id.ItemName })
.Where(x => x.ItemName == id);
return data;
}
In this method I want IEnumerable<Temp_Order>. So I will use this in controller and return to the view.
I'm getting this error:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?) E:\WORK\Projects\RMS_Live\RMS_Live\Models\Order.cs
The Join is converting your query to an IEnumerable of an anonymous type. Add a Select on the end to cast is back to Temp_Order:
public IEnumerable<Temp_Order> Get_Temp(string id)
{
//List<Temp_Order> data = new List<Temp_Order>();
IEnumerable<Temp_Order> data = db.Temp_Order
.Join(db.Items, t_id => t_id.ItemId, I_id => I_id.ItemId, (t_id, I_id) => new { t_id.Quantity, I_id.ItemName })
.Where(x => x.ItemName == id)
.Select(a => new Temp_Order
{
ItemName = a.ItemName,
Property2 = a.Property2,
//snip
});
return data;
}
EDIT:
You indicate in the comments that you want all properties from both Temp_Order and Item which means you need another class. You can get away without creating another class, but it's much simpler in the long run. So first make your class, 2 ways spring to mind, you either replicate all the properties you need or just return the 2 objects, I'll use the latter:
public class OrderItem
{
public Temp_Order Temp_Order { get; set; }
public Item Item { get; set; }
}
Now your function becomes this:
public IEnumerable<OrderItem> Get_Temp(string id)
{
IEnumerable<OrderItem> data = db.Temp_Order
.Join(db.Items,
t_id => t_id.ItemId,
I_id => I_id.ItemId,
(t_id, I_id) => new OrderItem
{
Temp_Order = t_id,
Item = I_id
})
.Where(x => x.ItemName == id);
return data;
}
And in your view, make sure you set the model type to IEnumerable<OrderItem> and you can access all the properties like this:
#Model.Temp_Order.ItemName
I am using MVC and want to pass values, controller to controller
My code:
public ActionResult Index()
{
List<string> SportsName = new List<string>();
var sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
var spt = Db.Departments.Where(i => i.UniversityID == sport.UniversityID && i.DepartmentCodeID == 4).SingleOrDefault();
unvId = int.Parse(sport.UniversityID.ToString());
List<Sport> dept = Db.Sports.Where(s => s.DepartmentID == spt.DepartmentID).ToList();
foreach (var sname in dept.ToList())
{
var name = Db.SportsCodes.Where(s => s.SportsCodeID == sname.SportsCodeID).First();
SportsName.Add(name.SportsName);
}
ViewBag.SportsName = SportsName;
return View();
}
public ActionResult Create(string sports)
{
ViewBag.SportsName = sports;
int s = unvId;
return View();
}
I want the 'sport' value in create action also. How to get the value of 'sport' in create action?
What I guess from your question is You want to pass the SportsName from Index to Create.
From Index View (.cshtml) when you call the Create method through AJAX call, pass the value of the ViewBag.Sports as a parameter.
For Example :
$('#Link').click(function () {
$.ajax({
url: http://localhost/Sports/Create,
type: 'GET',
data: {
sports: "#ViewBag.SportsName"
},
success: function () {
},
error: function () {
}
});
[Note : Here it is considered that name of your controller is Sports]
This answers to your question.
You have a number of options. If only individual action methods need that value, fetch the value in each action method that needs it:
public ActionResult SomeMethod()
{
var sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
// ...
}
If the repeated code seems unsightly, abstract it to a helper method:
public ActionResult SomeMethod()
{
var sport = GetSport();
// ...
}
private SomeType GetSport()
{
return Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
}
If it should be accessible anywhere in the class and is logically a class-level member, make it a class-level member:
private SomeType sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
public ActionResult SomeMethod()
{
// sport is accessible here
// ...
}
Though now that I notice it, this begs the question "What is Db"? If that's a database context then it looks like you've expanded the scope of it beyond what it really should be. Database contexts and connections should be kept in as small a scope as possible. Each method that needs one should create it, use it, and destroy it. Sharing them at a larger scope without explicitly knowing what you're doing is inviting a whole host of problems. In this case, the class-level member would be initialized in the constructor:
private SomeType sport;
public YourController()
{
using (var Db = BuildYourDBContext())
sport = Db.Universities.Where(ud => ud.Contact.UserName.ToLower().Trim() == User.Identity.Name.ToLower().Trim()).SingleOrDefault();
}
public ActionResult SomeMethod()
{
// sport is accessible here
// ...
}
Any way you look at it, the point is that being a controller doesn't really make a difference. The controller is an object like any object in an object-oriented system. It shares members exactly the same way.
It is understood that:
Expression<Func<string, bool>> first = x => x.Length == 4;
Expression<Func<string, bool>> second = x => x.Length == 4;
Console.WriteLine(first.Equals(second)); // Output is "False"
However, examining the strings of each expression does show equality:
Expression<Func<string, bool>> first = x => x.Length == 4;
Expression<Func<string, bool>> second = x => x.Length == 4;
Console.WriteLine(first.ToString().Equals(second.ToString())); // Output is "True"
This idea was a culmination of different posts...
http://www.codethinked.com/Comparing-Simple-Lambda-Expressions-With-Moq
Moq'ing methods where Expression<Func<T, bool>> are passed in as parameters
Verify method was called with certain linq expression (moq)
The intent:
I am writing an MVC application using the repository pattern such that
public class MyController : Controller
{
public Repository.IRepository Repository { get; set; }
public MyController()
{
this.Repository = new Repository.CommonRepository();
}
public MyController(Repository.IRepository repository)
{
this.Repository = repository;
}
[HttpPost]
public ActionResult Create(Domain.Common.Object1 o1)
{
if (ModelState.IsValid)
{
// Additional validation
o1.Name = o1.Name.Trim();
if (this.Repository.Any<Domain.Common.Object1>(a => a.Name.ToLower() == plant.Name.ToLower()))
this.ModelState.AddModelError("Name", "Duplicate found.");
}
if (ModelState.IsValid)
{
var entity = this.Repository.Add(o1);
if (Request.IsAjaxRequest())
return this.Json(new { Completed = true, Id = entity.Id });
return RedirectToAction("Details", new { id = entity.Id });
}
if (Request.IsAjaxRequest())
return PartialView("_Create", o1);
return View("Create", o1);
}
}
Repository is a completely separate project as is the domain. My repository code is setup so that I can use the one repository to query any object based upon the generic:
public IQueryable<T> GetAll<T>() where T : AbstractEntity
{
return this.DbContext.Set<T>();
}
Note: AbstractEntity is a domain abstract class all of my POCO objects inherit from.
Everything is fine when using Moq to unit test the controller :
[TestMethod]
public void Create_Post_DuplicateNameAddsError()
{
// Arrange
var repository = new Mock<Repository.IRepository>();
repository.Setup(a => a.Any<Domain.Common.Object1>(It.IsAny<System.Linq.Expressions.Expression<Func<Domain.Common.Object1, bool>>>()))
.Returns(true);
var controller = ControllerFactory<MyController>.GetController();
controller.Repository = repository.Object;
var model = new Domain.Common.Object1()
{
Id = Guid.NewGuid()
,
Name = "Name"
};
// Act
var result = controller.Create(model) as ViewResult;
// Assert
Assert.IsFalse(controller.ModelState.IsValid);
Assert.IsNotNull(result);
Assert.AreEqual("Create", result.ViewName, false);
Assert.AreEqual(model, result.Model);
}
Note: ControllerFactory is a way to generate a controller with certain properties filled, such as Request, Response, User, Request.Headers ect...
Where this fails is if I have to use IRepository.Any(predicate) more than once, or any method that uses expressions that is called more than once. I need it to say true for one and false for another. If the expression strings were a match, this would be a non-issue, but as everything is in different projects the expression strings come out as:
a => (a.Name.ToLower() == value(foo.Web.Tests.Controllers.Object1ControllerTests+<>c__DisplayClass3).ob1.Name.ToLower())
a => (a.Name.ToLower() == value(foo.Controllers.MyController+<>c__DisplayClass1).ob1.Name.ToLower())
The difference lies in the value function. I have tried matching from Regular Expressions, which works, but is ugly as you have to escape every .<>(), which in turn makes it very difficult to maintain.
I tried using Matt Meber's Expression Equality Comparer, but they are not equal due to that value function (my belief).
Suggestions?
i have trivial mapping for two entities: poll and polloption
Poll:
public class PollMap : ClassMap<Poll>
{
public PollMap() {
Id(x => x.Id);
Map(x => x.Content);
HasMany(x => x.PollOptions).Cascade.All();
}
}
PollOption:
public class PollOptionMap : ClassMap<PollOption>
{
public PollOptionMap() {
Id(x => x.Id);
Map(x => x.Content);
References(x => x.Poll);
}
}
in test code im trying to remove the first polloption of poll entity
Test code:
[Transaction]
public ActionResult Add() {
var poll = new Poll() {
Content = "poll",
PollOptions = new List<PollOption>() {
new PollOption(){
Content="PollOption#1"
},
new PollOption(){
Content="PollOption#2"
}
}
};
GetSession.Save(poll);
return Content("Added");
}
[Transaction]
public ActionResult Removed() {
var poll = GetSession.Query<Poll>().FirstOrDefault();
poll.PollOptions.RemoveAt(0);
GetSession.Update(poll);
return Content("Updated");
}
when the remove action fired it not deleting polloption from db instead it set null in my foreign key :(
ps. google not helped
Cascade.All() only deletes the child object if the parent is deleted. If you want the childs to get deleted when they are removed from the collection, you need Cascade.AllDeleteOrphan().
Additional note: You also have to mark one side of your bidirectional association as Inverse(). More info about that here: http://nhibernate.info/doc/nh/en/index.html#collections-bidirectional