Context: ASP.NET MVC 2.0, C#, SQL Server 2008, IIS7
I have 'scheduledMeetings' table in the database.
There is a one-to-many relationship: scheduledMeeting -> meetingRegistration
So that you could have 10 people registered for a meeting.
meetingRegistration has fields Name, and Gender (for example).
I have a "calendar view" on my site that shows all coming events, as well as gender count for each event.
At the moment I use Linq to Sql to pull the data:
var meetings = db.Meetings.Select(
m => new {
MeetingId = m.Id,
Girls = m.Registrations.Count(r => r.Gender == 0),
Boys = m.Registrations.Count(r=>r.Gender == 1)
});
(actual query is half-a-page long)
Because there is anonymous type use going on I cant extract it into a method (since I have several different flavors of calendar view, with different information on each, and I don't want to create new class for each).
Any suggestions on how to improve this?
Is database view is the answer?
Or should I go ahead and create named-type?
Any feedback/suggestions are welcome. My DataLayer is huge, I want to trim it, just don't know how.
Pointers to a good reading would be good too.
I'd extend your Meetings class by adding 2 properties:
public partial class Meeting
{
#region Properties
public int BoyCount { get; set; }
public int GirlCount { get; set; }
#endregion
}
With deferred loading:
var items = db.Meetings.Select(
m => new {
Meeting = m,
Girls = m.Registrations.Count(r => r.Gender == 0),
Boys = m.Registrations.Count(r = >r.Gender == 1)
}).ToList();
items.ForEach(i =>
{
i.Meeting.BoyCount = i.Boys;
i.Meeting.GirlCount = i.Girl;
});
List<Meeting> = items
.Select(i => i.Meeting)
.ToList();
With eager loading, one of the solutions is to load Registrations with your Meeting entity:
DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Meeting>(m = > m.Registrations);
db.LoadOptions = loadOptions;
In this case the partial class properties above are became getters:
public partial class Meeting
{
#region Properties
public int BoyCount
{
get
{
return this.Registrations
.Count(r => r.Gender == 1);
}
}
public int GirlCount
{
get
{
return this.Registrations
.Count(r = > r.Gender == 0);
}
}
#endregion
}
Related
I have a Date field in my DB and I'm trying to update it to the current Date when I press the submit button on my webpage but it does not update. I believe I'm doing the correct steps but here is my code.
Controller:
public ActionResult TakeInventory(int? AssetNum, string owners, string locationId, string clientId)
{
ViewBag.LocationId = new SelectList(db.Locations, "LocationKey", "LocationName");
ViewBag.ClientId = new SelectList(db.ClientSites, "ClientSiteKey", "ClientSiteName");
var records = from s in db.Assets select s;
if (AssetNum != 0)
{
records = records.Where(c => c.AssetKey == AssetNum);
}
if (!String.IsNullOrEmpty(owners))
{
records = records.Where(x => x.InventoryOwner.Equals(owners));
}
if (!String.IsNullOrEmpty(locationId))
{
int locnum = Convert.ToInt32(locationId);
records = records.Where(x => x.LocationKey == locnum);
}
if (!String.IsNullOrEmpty(clientId))
{
int clinum = Convert.ToInt32(clientId);
records = records.Where(x => x.ClientSiteKey == clinum);
}
else
{
return View(records);
}
return View(records);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult TakeInventory([Bind(Include = "InventoryDate")] Asset asset)
{
if (ModelState.IsValid)
{
db.Entry(asset).State = EntityState.Modified;
asset.InventoryDate = DateTime.Now;
db.Assets.Add(asset);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(asset);
}
View:
#foreach (var items in Model)
{
<p>Last Inventory Date: #Html.DisplayFor(modelItem => items.InventoryDate) </p>
<input type="submit" value="Submit" />
Model:
public partial class Asset
{
public System.DateTime InventoryDate { get; set; }
public Asset()
{
InventoryDate = DateTime.Now;
}
}
You want to retrieve the Asset entity again before updating again.
For example,
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult TakeInventory([Bind(Include = "InventoryDate")] Asset asset)
{
if (ModelState.IsValid)
{
var entity = (from s in db.Assets where AssetNum == asset.AssetNum Select s).FirstOrDefalt();
entity.InventoryDate = DateTime.Now;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(asset);
}
Is a bad practice:
asset.InventoryDate = DateTime.Now;
At least, you need:
1. SaveChanges() your DbContext
2. Your DateTime field in backend must be Nullable or NotNull in Db (there is no inmpicit conversion)
But the real trouble is timezones. Its all works fine, if you have only one instance in only one datacenter and all your clients is from only one small and beauty country (one timezone wide)
DateTime.Now returns you local mashine timezone time.
If you use your 'entity.InventoryDate' in any kind of requests query it can return confused rezults, and can be surprized with funny result: for ex., value with tomorrow datetime relatively to you :)
For Web-services always cast to UTC that kind of fields, or use triggers or default expression for this kind of fields inside your DB engine
P.S. Russia is 11 timezones wide, i know what i'm talking about
Why you are passing the Current date , there is no need for that you can you Sql build in function "GETDATE()" to Get the current Date
I am trying to concatenate IQueryable where T can be different types.
So ((IQueryable<Person>)people).Concant((IQueryable<Account>)accounts).
I have created a structure like so:
public struct PerformanceStructure
{
public Entity Entity { get; set; }
public Entity Performance { get; set; }
}
I am building dynamic queries that follow this template:
var result = context.Accounts.Where(a => a.id == 1).Select(s => new PerformanceStructure { Entity = s });
result = result.Concat(context.Person.Where(p => p.id = 1).Select(s => new PerformanceStructure {Entity = s});
Execution looks like this:
var list = result.Skip(pageSize * (pageNumber - )).Take(pageSize);
When executing the query, I get the error Types in Union or Concat have different members assigned
What can I do to resolve this error but retrieve the two objects from that database?
In the end I want to paginate the query (thus the skip/take) based on some order.
I've got a class T with an association to a cross reference table. I'd like to have a method on the class that adds an entry to the cross reference by receiving an entity representing the other side. For some reason, though, while I can add the item to the collection it doesn't get added to the change set for the data context.
So the class in question looks like:
class T
{
public EntitySet<T_U> t_users
{
get
{
if ((this.serializing && (this.t_user.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this.t_user;
}
set
{
this.Users.Assign(value);
}
}
public AddUser(U user)
{
this.t_users.Add( new T_U() { TID = this.ID, UID = user.ID );
}
}
The client using this class would basically do something like:
var db = new DBDataContext();
var t = db.Ts.FirstOrDefault( t => t.ID = 100);
var u = db.Us.FirstOrDefault(u => u.ID == 3);
t.AddUser(u);
db.SubmitChanges();
Shouldn't that succesfully add a record to the cross reference table?
I think you want InsertOnSubmit instead of Add. Are you using an outdated version of Linq to sql?
You are adding new T_U to this.Users... Does that work? Isn't that the wrong type?
this.Users.Add( new T_U() { TID = this.ID, UID = user.ID );
I have been building a Motion Picture application to manage actors or "Talents". I have a TALENT table and a LANGUAGES table. I also have a TALENTLANGUAGES table that shows the many to many relationship between the two.
Here is the SQL i can write to show the different languages a given talent speaks.
Select t.TalentID, t.FirstName, tl.LanguageID, l.Name from Talent t
inner join TalentLanguage tl on tl.TalentID = t.TalentID
inner join Language l on l.LanguageID = tl.LanguageID
where t.TalentID = 10000;
Im in my C# application I'm using Linq to sql classes. How might I do the above code with linq to sql. Thanks.
Here's one way you can do it:
Start by creating a "results" object, something that will hold the information that you need in one object. Let's call it "TalentLanguagesContainer"
public class TalentLanguagesContainer
{
public int TalentID { get; set; }
public string FirstName { get; set; }
public int LanguageID { get; set; }
public string LanguageName { get; set; }
}
Then, create a Select statement that will map your needs appropriately:
public IQueryable < TalentLanguagesContainer > GetTalentLanguages()
{
MyDataContext _dataContext = new MyDataContext();
return _dataContext.TalentLanguages
.Where(t => t.TalentID == 10000)
.Select(tl => new TalentLanguagesContainer() {
TalentID = tl.TalentID,
FirstName = tl.Talent.FirstName,
LanguageID = tl.LanguageID,
LanguageName = tl.Language.Name });
}
Also, you may want to consider writing stored procedures for more complex scripts such as this one - you may find an SQL script to perform faster too.
Remus, I think I'm gonna answer this myself because it's such a clean solution. Check this out...
var languages = from tl in talentDB.TalentLanguages
where tl.TalentID == id
select new { lang = tl.Language.Name, tal_id = tl.TalentID }; // could get more values if needed..
foreach (var l in languages)
{
string language = l.lang;
string talentID = l.tal_id;
// etc...
}
This is pretty cool! Linq did the join for me!!
Is it possible to extend LINQ-to-SQL entity-classes with constructor-methods and in the same go; make that entity-class inherit from it's data-context class?--In essence converting the entity-class into a business object.
This is the pattern I am currently using:
namespace Xxx
{
public class User : Xxx.DataContext
{
public enum SiteAccessRights
{
NotRegistered = 0,
Registered = 1,
Administrator = 3
}
private Xxx.Entities.User _user;
public Int32 ID
{
get
{
return this._user.UsersID;
}
}
public Xxx.User.SiteAccessRights AccessRights
{
get
{
return (Xxx.User.SiteAccessRights)this._user.UsersAccessRights;
}
set
{
this._user.UsersAccessRights = (Int32)value;
}
}
public String Alias
{
get
{
return this._user.UsersAlias;
}
set
{
this._user.UsersAlias = value;
}
}
public User(Int32 userID)
{
var user = (from u in base.Users
where u.UsersID == userID
select u).FirstOrDefault();
if (user != null)
{
this._user = user;
}
else
{
this._user = new Xxx.Entities.User();
base.Users.InsertOnSubmit(this._user);
}
}
public User(Xxx.User.SiteAccessRights accessRights, String alias)
{
var user = (from u in base.Users
where u.UsersAccessRights == (Int32)accessRights && u.UsersAlias == alias
select u).FirstOrDefault();
if (user != null)
{
this._user = user;
}
else
{
this._user = new Xxx.Entities.User
{
UsersAccessRights = (Int32)accessRights,
UsersAlias = alias
};
base.Users.InsertOnSubmit(this._user);
}
}
public void DeleteOnSubmit()
{
base.Users.DeleteOnSubmit(this._user);
}
}
}
Update:
Notice that I have two constructor-methods in my User class. I'd like to transfer those to the User entity-class and extend the User entity-class on it's data-context class, so that the data-context is available to the entity-class on "new-up".
Hope this makes sense.
Rick Strahl has a number of really good articles that address what I think you are looking for. Check out his list of Linq Articles Here
Inheriting an entity from a data context is a bad idea. They are two discrete objects and are designed to operate that way. Doing this will cause all sorts of issues least of all problems with trying to submit a bunch of related changes together at the same time - going through multiple data contexts will cause this to fail as each tries to work independently.
It doesn't seem to make sense to make an entity a type of DataContext. It doesn't need to be a DataContext in order to be considered a business object, nor do you necessarily need to create a type that contains the original entity. It might be better to just extend the entity class and contain a reference to a DataContext using composition:
namespace Xxx.Entities
{
public partial class User : IDisposable
{ DataContext ctx;
public static GetUserByID(int userID)
{ var ctx = new DataContext();
var user = ctx.Users.FirstOrDefault(u=>u.UsersID == userID);
if (user == null)
{
user = new User();
ctx.Users.InsertOnSubmit(user);
}
user.ctx = ctx;
return user;
}
public void Dispose() { if (ctx != null) ctx.Dispose(); }
}
}
If you just want the property names to be different than the database column names, do that in the mapping file.