Having difficulty trying to come up with an effective linq lambda expression - asp.net

Stats:
ASP.NET 4.5.2
MVC 5
Identity 2.2
I am able to make the SQL statement, as I know SQL decently enough, but I am rather new to the whole LINQ Lambda thing.
I have also looked into the two tools most often cited for this job, but sorry -- Linqer is unable to run because the Microsoft tool it uses to create the SQL connection (the dbml file) refuses to install on my Win8.1 system, and LinqPad doesn’t provide an actual translation until you actually buy the product (which makes the “trial” fundamentally broken in the first place, IMHO: how can you possibly evaluate something that prevents you from conducting the action that you are evaluating?).
I am hoping that someone can take my SQL statement below and convert it into a proper LINQ Lambda expression that I can more effectively plug into my controller. This is also going to be a part of a multi-model display, such that multiple lambda expressions will be collated into a model that displays multiple outputs (very similar but slightly different outputs, one for users that shows only the user's dashboard, one for managers that also need the user's dashboard in addition to their managerial dashboard).
My expression is thus:
SELECT
co.CompanyId
, co.CompanyName
, co.CompanyCity
, co.NumberOfEmployees
, co.ProspectingScore
, po.ProvinceAbbr
, cd.PDFResourceLibrary
, cd.PresentationDone
, cd.MOUDone
FROM Company AS co
LEFT JOIN Province AS po ON co.ProvinceId = po.ProvinceId
OUTER APPLY (SELECT TOP 1 MAX(CycleDate) AS CycleDate, PDFResourceLibrary, PresentationDone, MOUDone FROM Cycle AS cy (NOLOCK) WHERE cy.CompanyId = co.CompanyId GROUP BY PDFResourceLibrary, PresentationDone, MOUDone) AS cd
WHERE co.RegionId = '66BD50DD-B6CB-E511-8265-14FEB5FBEAE8'
ORDER BY co.ProspectingScore DESC
For simplicity's sake, the po.ProvinceAbbr and its associated left join can be dropped, the RegionID will be brought in via a variable pulled from a claim, and if need be you can use wildcards (*) instead of named fields for all tables except for the MAX requirement on the Outer Apply (there are more than one cycles per company, I just want to bring back the most recent cycle by date). What you see above is the absolute minimum number of items needed to be pulled to populate the front end.
I have created the appropriate Models in my model namespace:
public class HomeViewModel {
public IEnumerable<DashboardUserData> RegionalCompanies { get; set; }
public IEnumerable<Company> AllOtherCompanies { get; set; }
}
public class DashboardUserData {
public Guid CompanyId { get; set; }
public string CompanyName { get; set; }
public string CompanyCity { get; set; }
public string ProvinceAbbr { get; set; }
public int? NumberOfEmployees { get; set; }
public int? ProspectingScore { get; set; }
public bool? PDFResourceLibrary { get; set; }
public bool? PresentationDone { get; set; }
public bool? MOUDone { get; set; }
}
And I hope to attach the lambda expression to the RegionalCompanies as shown in the model.
As an example, the AllOtherCompanies (which is the Managerial dashboard) has as its original lambda expression the following:
viewModel.AllOtherCompanies = await db.Company.Where(c => c.RegionId != regionId).Include(c => c.Province).ToListAsync();
So you can see that I am at least halfway there -- I am able to bring in the Company table, but I have no clue how to include the Cycle table for each company such that only the most recent Cycle is included with the company and all others are dropped.
Frankly, with the lambda I am still unsure as to how to connect the Cycle table such that only the most recent cycle (by date) is chosen. All other cycles for any company MUST be ignored and dropped, so from what I can tell neither a plain Join nor a GroupJoin would be effective here.
Suggestions?

Why don't you run it as SQL? Like:
var results = ctx.Companies.SqlQuery("SELECT ...", "66BD50DD-B6CB-E511-8265-14FEB5FBEAE8");
Where the GUID is passed as a parameter and the results translated into Company. You need to declare it in the SQL as #p0.

Related

Unwanted unique constraint in many to many relationship

I'm trying to set up a Tagging tool for images. Basically I have two tables, one for pictures, and one for tags. Both are connected with a many to many setup. I can already add a single tag to a picture, and the same tag to different pictures. However, when I try to add a second tag to an image I get an exception complaining about a unique constraint that I simply don't see.
public class MediaEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<TagEntity> Tags { get; set; }
}
public class TagEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<MediaEntity> MediaEntities { get; set; }
}
public void updateMedia(MediaEntity model)
{
using (var db = new MediaContext(_dbLocation))
{
db.Update(model);
db.SaveChanges();
}
}
public class MediaContext : DbContext
{
private const string DB_NAME = "PT.db";
private string _path;
public DbSet<MediaEntity> MediaTable { get; set; }
public DbSet<TagEntity> TagTable { get; set; }
public MediaContext(string path)
{
_path = path;
ChangeTracker.AutoDetectChangesEnabled = false;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={Path.Combine(_path, DB_NAME )}");
}
As far as I can tell my setup should create a normal many-to-many relationship, and it the database I also see pretty much this. EF automatically creates a TagTable, MediaTable, and MediaEntityTagEntityTable. But when I try to add a second tag I get this:
SqliteException: SQLite Error 19: 'UNIQUE constraint failed:
MediaEntityTagEntity.MediaEntitiesId, MediaEntityTagEntity.TagsId'.
Data from the table showing I can have the same tag on different pictures:
MediaEntitiesId
TagEntitiesId
1B48E85B-F097-4216-9B7A-0BA34E69CBFF
CF581257-F176-4CDF-BF34-09013DCEAA27
CE33F03F-5C80-492B-88C6-3C40B9BADC6C
CF581257-F176-4CDF-BF34-09013DCEAA27
523178A1-C7F8-4A69-9578-6A599C1BEBD5
0C45C9D1-7576-4C62-A495-F5EF268E9DF8
I don't see where this unique constaint comes in. How can I set up a proper many-to-many relationship?
I suspect the issue you may be running into is with the detached Media and associated Tags you are sending in. You are telling EF to apply an 'Update' to the media, but the DbContext will have no idea about the state of the Tags attached. Assuming some tags may have been newly attached, others are existing relationships. If the Context isn't tracking any of these Tags, it would treat them all as inserts, resulting in index violations (many to many) or duplicate data (many to one / one to many)
When dealing with associations like this, it is generally simpler to define more atomic actions like: AddTag(mediaId, tagId) and RemoveTag(mediaId, tagId)
If you are applying tag changes along with potential media field updates in a single operation I would recommend rather than passing entire entity graphs back and forth, to use a viewModel/DTO for the tag containing a collection of TagIds, from that apply your tag changes against the media server side after determining which tags have been added and removed.
I.e.:
public void updateMedia(MediaViewModel model)
{
using (var db = new MediaContext(_dbLocation))
{
var media = db.Medias.Include(x => x.Tags).Single(x => x.MediaId = model.MedialId);
// Ideally have a Timestamp/row version number to check...
if (media.RowVersion != model.RowVersion)
throw new StaleDataException("The media has been modified since the data was retrieved.");
// copy media fields across...
media.Name = model.Name;
// ... etc.
var existingTagIds = media.Tags
.Select(x => x.TagId)
.ToList();
var tagIdsToRemove = existingTagIds
.Except(model.TagIds)
.ToList();
var tagIdsToAdd = model.TagIds
.Except(existingTagIds)
.ToList();
if(tagIdsToRemove.Any())
media.Tags.RemoveRange(media.Tags.Where(x => tagIdsToRemove.Contains(x.TagId));
if(tagIdsToAdd.Any())
{
var tagsToAdd = db.Tags.Where(x => tagIdsToAdd.Contains(x.TagId)).ToList();
media.Tags.AddRange(tagsToAdd);
}
db.SaveChanges();
}
}
Using this approach the DbContext is never left guessing about the state of the media and associated tags. It helps guard against stale data overwrites and unintentional data tampering (if receiving data from web browsers or other unverifiable sources), and by using view models with the minimum required data, you improve performance by minimzing the amount of data sent over the wire and traps like lazy load hits by serializers.
I always explicitly create the join table. The Primary Key is the combination of the two 1:M FK attributes. I know EF is supposed to map automatically, but since it isn't, you can specify the structure you know you need.

Entity Framework 6 - How to count items in a many-to-many relationship via a single query?

Apologies for the noob EF question but I'm struggling to get this working and have definitely reached the Ask For Help stage :)
Here are two simplified classes:
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public IList<CourseTag> CourseTags { get; set; }
...
}
public class CourseTag
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Course> Courses { get; set; }
...
}
I simply want to Count the number of CourseTags that are related to a course, in a single query.
I can SELECT the courses for a particular CourseTagId (as below), which SELECTS the courses just fine...
from course in Courses
from tag in course.CourseTags
where tag.Id == 3
select course
BUT I can't work out how to Count the number of Courses that are related to a particular CourseTag (without using an additional, separate query)
I've tried variations along the lines of the following, but just can't figure the correct syntax...
from course in Courses
from tag in course.CourseTags
where tag.Id == 3
**course.Count()??**
I've worked out how to separate this into two queries to get the result - but it seems unnecessary to perform two queries just to count something - there must be a way to simplify this into a single query?
var courseResult = from course in Courses
from tag in course.CourseTags
where tag.Id == 3
select course;
var totalNumber = courseResult.Count();
I'm sure this must be simple (it would be easy if NOT using EF/Linq) but I have a total brain block with EF/Linq. Any pointers would be much appreciated, thank you.
Okay it seems that this is one way to reduce it down to a single query. Not sure if I've actually gained anything now though but since no-one else has any suggestions hopefully this might help someone else.
var result = Courses.SelectMany(c => c.CourseTags.Select(t => new { c, t })) //anon obj of course with tag
.Where(c => (c.t.Id == 3)) //filter by TagId == 3
.Select(c => c.c.Id) //limit result to a single field - no need to pull all the data just to count it
.Count();
This selects the Course AND it's related (many to many) Tags as an anonymous object - (c, t) is the Course and Tag.
SelectMany flattens the resulting data, so that it can be counted.

Audit.net data models example

Does any one have a working example of how to added audit models to an existing project, for Audit.Net.
It is one fantastic component to use, and up until now, my team and I have gotten by with the standard JSON files, however, we'd like to migrate our current solution to our Xamarin application, and would like to store the auditing in the local SQLite database on the device.
However, the documentation for this project is somewhat lacking and there is no concise examples of how to get custom auditing working with Entity Framework.
We have worked through the MD files on the github repo, but we still cannot get auditing to work.
Another question, similar to this has been asked HERE, but there is no definitive example of what the Audit_{entity} table should look like, what fields it MUST contain, and how to set up relationships for it.
We tried to reverse engineer the JSON files into a relational structure, but at the time of asking this question, we have not gotten any auditing to write to the SQLite database.
Sorry about the documentation not helping too much, hope I (or anybody) can provide better documentation in the future.
I am assuming you are using EntityFramework to map your entities
to a SQLite database, and you want to use the EF data
provider
to store the audits events in the same database, in Audit_{entity} tables.
There is no constraint on the schema you want to use for your Audit_{entity} tables, as long as you have a one-to-one relation between your {entity} table and its Audit_{entity} table. Then the mapping can be configured on several ways.
The recommendation for the Audit_{entity} tables is to have the same columns as the audited {entity} table, with any common additional column needed, like a User and a Date defined on an Interface.
So, if all your Audit_{entity} tables has the same columns/properties as its {entity}, and you added some common columns (defined on an interface), the configuration can be set like this:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Audit_User : IAudit
{
public int Id { get; set; }
public string Name { get; set; }
// IAudit members:
public string AuditUser { get; set; }
public datetime AuditDate { get; set; }
public string Action { get; set } // "Insert", "Update" or "Delete"
}
Audit.Core.Configuration.Setup()
.UseEntityFramework(x => x
.AuditTypeNameMapper(typeName => "Audit_" + typeName)
.AuditEntityAction<IAudit>((ev, ent, auditEntity) =>
{
auditEntity.AuditDate = DateTime.UtcNow;
auditEntity.AuditUser = evt.Environment.UserName;
auditEntity.AuditAction = ent.Action;
});
Note the interface is not mandatory, but using it makes the configuration cleaner. Also note you can make your Audit_{entity} inherit from your {entity} if you wanted to.
Update
Maybe my assumption at the beginning is incorrect and you are not auditing EF entities, but any other type of audit. If that's the case, what you are looking for is a Data Provider that stores the audit events into your SQLite database.
At the time being, there is no built-in data provider that stores to SQLite, and if there was one, it would store just the JSON representation of the event in one column (like the SQL/MySql providers). But it looks like you want to have a custom schema, so you will need to implement your own data provider.
Check the documentation here.
Here is a sample skeleton of a data provider:
public class SQLiteDataProvider : AuditDataProvider
{
public override object InsertEvent(AuditEvent auditEvent)
{
// Insert the event into SQLite and return its ID
}
public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
{
// Replace the event given its ID (only used for CreationPolicies InsertOnStartReplaceOnEnd and Manual)
}
// async implementation:
public override async Task<object> InsertEventAsync(AuditEvent auditEvent)
{
// Asynchronously insert the event into SQLite and return its ID
}
public override async Task ReplaceEventAsync(object eventId, AuditEvent auditEvent)
{
// Asynchronously replace the event given its ID
}
}
Then you just set it up with:
Audit.Core.Configuration.Setup()
.UseCustomProvider(new SQLiteDataProvider());

Update database items from the website

My current problem is (probably) not necessarily directly related to MVC 6, but how working with database actually works, and therefore any help/suggestions in this matter would be more than appreciated.
For the sake of this question, let's say that we have a very simple database with the following tables (C# classes) [we are using Entity Framework to work with the database]:
public class ShoppingUser
{
public int Id { get; set; }
public string UserName { get; set; }
public ICollection<ShoppingItem> Items { get; set; }
}
public class ShoppingItem
{
public int Id { get; set; }
public string Quantity { get; set; }
public string Text { get; set; }
public bool ToRemove { get; set; }//if item has been bought, it can be removed from the shopping list
}
This demo will be for a super duper simple shopping list app, where user (ShoppingUser who is registered in the system can have a List of ShoppingItem where user can decide on what is the text of the item (e.g. Bread, Butter, Tomatoes, ...) and also a quantity (3 pieces, 5kg, ... simple string)
Afterwards in my ASP.NET Core app, I have defined a repository which is communicating with the database and has access to the ShoppingItem class (as we are only interested in shopping items of currently logged in user).
Example of some method we could use here:
public IEnumerable<ShoppingItem> ReturnUserItems(string sUsername)
{
if (string.IsNullOrWhiteSpace(sUsername))
return null;
var result = _context.ShoppingUsers.Include(n => n.Items).Where(n => n.UserName == sUsername).FirstOrDefault();
if (result != null)
return result.Items;
else
return null;
}
Finally we have an API controller with JsonResult for either GET, POST, DELETE, ..., which is used for communication between client side AngularJs App and our server side logic.
Example of GET Method:
// GET: /<controller>/
[HttpGet("")]
public JsonResult Get(string sUserName)
{
try
{
var results = _repository.ReturnUserItems(User.Identity.Name);
if (results != null)
{
var result = Mapper.Map<IEnumerable<ShoppingItemViewModel>>(results);
return Json(result);
}
Response.StatusCode = (int)HttpStatusCode.OK;
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = ex.Message });
}
return null;
}
Here comes the tricky part (at least for me). From video tutorials I have learned, that I should never (or almost never) expose my real database model to the website (I guess it's for security reasons). Due to that (as visible from my GET method above) I have declared my ShoppingItemViewModel which contains only properties I want to expose to the user (e.g. meaning that Id of my item is not visible).
This is how it looks like:
public class ShoppingItemViewModel
{
public string Quantity { get; set; }
[Required]
public string Text { get; set; }
[Required]
public bool ToRemove { get; set; }//if item has been bought, it can be removed from the shopping list
}
And for the communication from my AngularJS App I am using simple $http.get and $http.post calls for retrieving / posting updated data.
Finally the question:
My problem is, that if a user decides to either delete an item from his shopping list, or decides to change the content of either text / quantity (meaning that originally in the database it was tomatoes - 5 kg but he manages to buy only 2 kg and therefore changes the quantity to tomatoes - 3kg), how can the app understand which elements have actually been changed and how? The problem I have in this case is, that we are no longer exposing the database Id of the items.
If I was writing a desktop app, where I wouldn't have to create this sub view (ShoppingItemViewModel), my EntityFramework is intelligent enough to check & update all the changes in my database. Unfortunately in this case, I do not understand how this is achievable.
When I was thinking about it I came with the following: Add a new property into the ShoppingItem and ShoppingItemViewModel: public string sCustomKey {get; set; }, which would serve as a unique key for every item. This way, we no longer need to expose our database Id, but we are exposing the 'fake' one.
Second question:
I case my solution would be accurate, what is the best way to update items in the database? The only way I can think of is iterating through all the items in the database and manually check for changes?
Example of what I have in mind:
//IEnumerable<ShoppingItem> would be re-mapped result of ShoppingItemViewModel we have received back from the website
public void UpdateValues(IEnumerable<ShoppingItem> items, string sUserName)
{
//retrieves list of shopping items for specified customer
var allItems = _context.ShoppingUsers
.Include(n => n.Items)
.FirstOrDefault(n => n.UserName == sUserName);
//updates the values
foreach (var sItem in items)
{
var updatedItem = allItems.Items.FirstOrDefault(n => n.Text == sItem.sCustomKey);
if (updatedItem == null)
{
//create new item
var newItem = new ShoppingItem();
newItem.Text = sItem.Text;
newItem.ToRemove = sItem.ToRemove;
allItems.Items.Add(newItem);
}
else
updatedItem.ToRemove = sItem.ToRemove;
}
_context.SaveChanges();
}
But this approach does not seem right to me.
Any help regarding these matters would be more than appreciated as I am still learning how to work with ASP.NET Core and web projects.
In your first question, exposing the item ID in the ViewModels is fine. In your domain layer, you can add validation logic that those ID exists/valid item.
Alternatively, you can use a Guid for your item/product because the ID (int) can easily be predicted.
As far as updating the items, you should not use the "username" as Identifier (of the cart) because that can be predicted/altered by the calling client. You can use Guid either persisted(to Db) or
in-memory. You can add validation as well if this Guid belongs to this username/emailAddress. So updating the items in the cart, consider adding/removing one at a time if that is doable
instead of sending list of items.
I think you have misunderstood something.
Here comes the tricky part (at least for me). From video tutorials I have learned, that I should never (or almost never) expose my real database model to the website (I guess it's for security reasons). Due to that (as visible from my GET method above) I have declared my ShoppingItemViewModel which contains only properties I want to expose to the user (e.g. meaning that Id of my item is not visible).
ViewModel <=> Domain Model <=> ReadModel (Database Model)
The point is that you shouldn't use your ReadModel(Database model) as your ViewModel in Presentation Layer (MVC). All three models will have identity.

ASP.NET; Several session variables or a "container object"?

I have several variables that I need to send from page to page...
What is the best way to do this?
Just send them one by one:
string var1 = Session["var1"] == null ? "" : Session["var1"].ToString();
int var2 = Session["var2"] == null ? 0 : int.Parse(Session["var2"].ToString());
and so on...
Or put them all in some kind of container-object?
struct SessionData
{
public int Var1 { get; set; }
public string Var2 { get; set; }
public int Var3 { get; set; }
}
--
SessionData data = Session["data"] as SessionData;
What is the best solution? What do you use?
A hybrid of the two is the most maintainable approach. The Session offers a low-impedance, flexible key-value pair store so it would be wasteful not to take advantage of that. However, for complex pieces of data that are always related to each other - for example, a UserProfile - it makes sense to have a deeply nested object.
If all the data that you're storing in the Session is related, then I would suggest consolodating it into a single object like your second example:
public class UserData
{
public string UserName { get; set; }
public string LastPageViewed { get; set; }
public int ParentGroupId { get; set; }
}
And then load everything once and store it for the Session.
However, I would not suggest bundling unrelated Session data into a single object. I would break each seperate group of related items into their own. The result would be something of a middleground between the two hardline approaches you provided.
I use a SessionHandler, which is a custom rolled class that looks like this
public static class SessionHandler
{
public static string UserId
{
get
{
return Session["UserId"];
}
set
{
Session["UserId"] = value;
}
}
}
And then in code I do
var user = myDataContext.Users.Where(u => u.UserId = SessionHandler.UserId).FirstOrDefault();
I don't think I've every created an object just to bundle other objects for storage in a session, so I'd probably go with the first option. That said, if you have such a large number of objects that you need to bundle them up to make it easier to work with, you might want to re-examine your architecture.
I've used both. In general, many session variable names leads to a possibility of collisions, which makes collections a litte more reliable. Make sure the collection content relates to a single responsibility, just as you would for any object. (In fact, business objects make excellent candidates for session objects.)
Two tips:
Define all session names as public static readonly variables, and make it a coding standard to use only these static variables when naming session data.
Second, make sure that every object is marked with the [Serializable] attribute. If you ever need to save session state out-of-process, this is essential.
The big plus of an object: properties are strongly-typed.

Resources