Do navigation properties have to be explicitly set in EF Core? - asp.net

So let's say I have two models:
public class Simple
{
public int SimpleId { get; set; }
public int TestId { get; set; }
public Test Test { get; set; }
}
public class Test
{
public int TestId { get; set; }
public string Name { get; set; }
}
And I want to add a new Simple like this:
public void AddSimple()
{
var simple = new Simple
{
SimpleId = 11,
TestId = 1 //Assume that this exists in the DB with name = "Testtest"
};
_repo.Add(simple);
}
When I get this from the database like so:
public async override Task<Simple> Get(int id)
{
var simple = _entries
.Include(n => n.Test)
.FirstOrDefault(n => n.SimpleId == id);
return simple;
}
Is EF Core supposed to automatically infer and get Simple.Test from Simple.TestId?
Because right now the way I have to create new Simple records is by setting Simple.Test explicitly like this:
public void AddSimple()
{
var simple = new Simple
{
SimpleId = 11,
TestId = 1 //Assume that this exists in the DB with name = "Testtest",
Test = _testRepo.GetById(1)
};
_repo.Add(simple);
}
Or else, the Simple.Test navigation property will be null. This doesn't seem like the way it's supposed to be done.

The simple answer is "No", you don't need to set navigation properties, but you arguably "should". The behaviour you will see will behave more like "It Depends". What you see will depend on whether the DbContext is tracking relative entities or not.
For a simple example with a Parent and Child entity where a Child has a Parent reference. An existing Parent ID #1 exists, and we aren't adding a duplicate child.
using (var context = new AppDbContext())
{
var child = new Child { Name = "Sven", ParentId = 1 };
context.Children.Add(child);
var parent = child.Parent; // Null
context.SaveChanges();
parent = child.Parent; // Still null.
child = context.Children.Include(x => x.Parent).Single(x => x.Name == "Sven");
parent = child.Parent; // Returns Parent /w ID 1.
}
The "depends" behavior is whether the DbContext isn't already aware of the referenced entity. For example if we do this:
using (var context = new AppDbContext())
{
var tossThis = context.Parents.Single(x => x.ParentId == 1);
// Read for demonstration, the context is now tracking parent Id 1, we're not actually going to use it...
var child = new Child { Name = "Sven", ParentId = 1 };
context.Children.Add(child);
var parent = child.Parent; // Returns Parent /w ID 1.
}
The reference for child.Parent will match tossThis even though we didn't explicitly set it. EF will provide that reference when Adding child because it is tracking it. If the DbContext happens to be tracking an entity that you reference by ID when creating a new related entity, EF will automatically associate these related entities as soon as it associates the new entity.
This can lead to some inconsistent behaviour with your entity state if you have code logic called against a newly created or updated entity. When you set FKs but not navigation properties, the navigation properties might be available, or they might not. Exposing FKs also adds a second source of truth for dealing with associated references. For instance, what is the ID of a child's parent assumed to be? child.ParentId or child.Parent.ParentId? Some code may use one or the other. This introduces opportunities for unexpected behaviour if code changes a Parent reference. For example moving a child's parent:
var child = context.Children.Include(x => x.Parent).Single(x => x.Name == "Sven");
child.ParentId = 2;
var parent = child.Parent; // Still points to Parent ID #1.
context.SaveChanges();
parent = child.Parent; // Now it depends.
What child.Parent refers to after SaveChanges will depend on whether EF happens to be tracking the parent with the new ID. If it isn't tracking parent ID #2, then child.Parent will now be null. If it was tracking parent ID #2, then child.Parent will be referencing that entity after SaveChanges.
vs.
var newParent = context.Parents.Single(x => x.ParentId == 2);
child.Parent = newParent;
var parentId = child.ParentId; // Still set to 1.
context.SaveChanges();
parentId = child.ParentId; // Updated to 2.
This behaviour is a bit more consistent. Setting the parent doesn't automatically update the FK until SaveChanges is called.
Classic Parent/Child relationships don't typically see "parents" change, but Many-to-One relationships such as Order to OrderStatus are cases where an Order could see it's Status change. By setting FKs for OrderStatus where there is a navigation property available, your behaviour could change subtly depending if the context had happened to previously work with an order with the new status or not. (Whether that updated Status might be tracked already or not.)
Overall to avoid the risk of inconsistent behaviour and the bugs that that sometimes crop up when dealing with navigation properties vs. their FKs, my advice is to only use one or the other. For general use entities where having navigation properties available is beneficial, then use navigation properties along with shadow properties for the FKs. For situations where the navigation property isn't required and we want raw performance, use FKs alone. (Bounded contexts can help manage separating entity definitions for general use /w navigation properties vs. cases where you want raw read/update performance)
The additional benefit of fetching related entities is that it can provide a more meaningful validation. For instance with an Order / OrderStatus update scenario:
var order = context.Orders.Single(x => x.OrderId == dto.OrderId);
// update various order details...
order.OrderStatusId = dto.OrderStatusId;
context.SaveChanges(); // Throws possible exception.
vs.
var orderStatus = context.OrderStatuses.Single(x => x.OrderStatusId == dto.OrderStatusId); // Throws exception if status ID is not valid.
var order = context.Orders.Single(x => x.OrderId == dto.OrderId);
// update various order details...
order.OrderStatus = orderStatus;
context.SaveChanges(); // Throws possible exception for other violations.
In the first example, any invalid/illegal data combination including FK violations will occur on SaveChanges which isn't much of a hint what went wrong. Where-as in the second example, if the OrderStatus ID provided wasn't valid, the exception details would be on the line that attempted to load that ID. Debugging issues when resolving the references is a lot easier to see exactly what/where the issue lies.
Fetching entities can "feel" expensive, but it really isn't. EF will return references it is tracking from cache, or it will go to the DB if necessary. Fetching a row by ID is about as efficient as a DB operation can get. In cases where you might be dealing with a number of references (I.e. updating a set of data) you can consolidate the read operations ahead of time. (Get the relevant IDs from your ViewModels/DTOs, then pre-load those related rows in one read call, then set the references from that set.)

From what you are saying, this looks like a Primary Key - Foreign Key relationship.
Add the annotation inside Simple class -> Test property to specify FK relationship as below:
public class Simple
{
public int SimpleId { get; set; }
public int TestId { get; set; }
[ForeignKey("TestId")]
public virtual Test Test { get; set; }
}
public class Test
{
public int TestId { get; set; }
public string Name { get; set; }
}
This explicitly tells EF that you have a relationship and helps filling in the navigational property.
You can also use fluent APIs to specify the above relationship.

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.

Cannot Update Entity Using EF 6 - ObjectStateManager Error

I'm trying to update an entity using Entity Framework version 6.
I'm selecting the entity from the database like so...
public T Find<T>(object id) where T : class
{
return this._dbContext.Set<T>().Find(id);
}
And updating the entity like so..
public T Update<T>(T entity) where T : class
{
// get the primary key of the entity
object id = this.GetPrimaryKeyValue(entity);
// get the original entry
T original = this._dbContext.Set<T>().Find(id);
if (original != null)
{
// do some automatic stuff here (taken out for example)
// overwrite original property values with new values
this._dbContext.Entry(original).CurrentValues.SetValues(entity);
this._dbContext.Entry(original).State = EntityState.Modified;
// commit changes to database
this.Save();
// return entity with new property values
return entity;
}
return default(T);
}
The GetPrimaryKeyValue function is as so...
private object GetPrimaryKeyValue<T>(T entity) where T : class
{
var objectStateEntry = ((IObjectContextAdapter)this._dbContext).ObjectContext
.ObjectStateManager
.GetObjectStateEntry(entity);
return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
}
Just for clarity. I'm selecting the original entry out as I need to perform some concurrency logic (that Ive taken out). I'm not posting that data with the entity and need to select it manually out of the DB again to perform the checks.
I know the GetPrimaryKeyValue function is not ideal if there's more than one primary key on the entity. I just want it to work for now.
When updating, entity framework coughs up the error below when trying to execute the GetPrimaryKeyValue function.
The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'NAME_OF_ENTITY_IT_CANNOT_FIND'
I've written many repositories before and I've never had this issue, I cannot seem to find why its not working (hence the post).
Any help would be much appreciated.
Thanks guys!
Steve
It seems like you are having issues getting the PK from the entity being passed in. Instead of trying to go through EF to get this data you could either use their Key attribute or create your own and just use reflection to collect what the key names are. This will also allow you to retrieve multiple keys if it is needed. Below is an example I created inside of LinqPad, you should be able to set it to "Program" mode and paste this in and see it work. Hack the code up and use what you may. I implemented an IEntity but it is not required, and you can change the attribute to anything really.
Here are the results:
Keys found:
CustomIdentifier
LookASecondKey
Here is the code:
// this is just a usage demo
void Main()
{
// create your object from wherever
var car = new Car(){ CustomIdentifier= 1, LookASecondKey="SecretKey", Doors=4, Make="Nissan", Model="Altima" };
// pass the object in
var keys = GetPrimaryKeys<Car>(car);
// you have the list of keys now so work with them however
Console.WriteLine("Keys found: ");
foreach(var k in keys)
Console.WriteLine(k);
}
// you probably want to use this method, add whatever custom logic or checking you want, maybe put
private IEnumerable<string> GetPrimaryKeys<T>(T entity) where T : class, IEntity
{
// place to store keys
var keys = new List<string>();
// loop through each propery on the entity
foreach(var prop in typeof(T).GetProperties())
{
// check for the custom attribute you created, replace "EntityKey" with your own
if(prop.CustomAttributes.Any(p => p.AttributeType.Equals(typeof(EntityKey))))
keys.Add(prop.Name);
}
// check for key and throw if not found (up to you)
if(!keys.Any())
throw new Exception("No EntityKey attribute was found, please make sure the entity includes this attribute on at least on property.");
// return all the keys
return keys;
}
// example of the custom attribute you could use
[AttributeUsage(AttributeTargets.Property)]
public class EntityKey : Attribute
{
}
// this interface is not NEEDED but I like to restrict dal to interface
public interface IEntity { }
// example of your model
public class Car : IEntity
{
[EntityKey] // add the attribure to property
public int CustomIdentifier {get;set;}
[EntityKey] // i am demonstrating multiple keys but you can have just one
public string LookASecondKey {get;set;}
public int Doors {get;set;}
public string Make {get;set;}
public string Model {get;set;}
}

Entity to Model and foreign key objects

I have an EF object called SportDivision. For simplicity's sake, I won't include every field, just the ones that are relevant:
[Table("SportDivision", Schema = "dbo")]
public class SportDivision: BaseReferenceEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int SportId { get; set; }
[ForeignKey("SportId")]
public virtual Sport Sport { get; set; }
}
So it has a SportId and it's a foreign key that points to the table Sport.
Now, I can't just use an EF object in my views, so I have a model class that's mapped to SportDivision called SportDivisionModel:
public class SportDivisionModel: BaseReferenceModel
{
public int Id { get; set; }
public string Name { get; set; }
public int SportId { get; set; }
//Read only fields
public string Sport { get; set; }
}
I use automapper to transfer data from SportDivision to SportDivisionModel and vice versa. The mapping looks like this:
Mapper.CreateMap<SportDivision, SportDivisionModel>()
.ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));
Mapper.CreateMap<SportDivisionModel, SportDivision>();
And I have a genericized service that CRUDs and translates data from entity to model or model to entity. Everything works fine except on Create, of which the function is shown below:
public TModel Create<TModel, TEntity>(TModel entry)
where TModel : BaseReferenceModel
where TEntity : BaseReferenceEntity
{
var dm = ServiceLocator.Current.GetInstance<ICrudService<TEntity>>();
var raw = Mapper.Map<TModel, TEntity>(entry);
var created = dm.CreateOrUpdate(raw);
return Mapper.Map<TEntity, TModel>(dm.FindById(created.Id));
}
In the very last line, where you see dm.FindById(created.Id), it returns a SportDivisionModel object with no Sport name. A null reference exception is found in .ForMember(x => x.Sport, c => c.MapFrom(e => e.Sport.Name));. It didn't load Sport after the entry was just created in the database.
I've debugged the code, and I see that the entry with a valid SportId is entered into the SportDivision table of my database, but when I try and bring it over to my MVC application, it doesn't get all the information.
This only is an issue on create. If I simply get data from the database without creating it beforehand, or if I edit the information, then the Sport field in my model object does get populated. I don't know why this is happening, and I can't use the .Include in my generic service call (because not all BaseReferenceEntity classes have a foreign key pointing to Sport).
Please advise. Thanks in advance.
I must play Sherlock Holmes and try to derive what could be the content of CreateOrUpdate and FindById from the indications in your question:
You say that you don't use Include because of the generic service. I assume that you also don't use explicit loading (Load) because you would face the same problem that you cannot really make it generic.
Conclusion: Because the Sport navigation property in the SportDivision gets loaded in certain scenarios (Edit) this can only happen due to lazy loading. The conclusion is backed by the fact that the Sport property is marked as virtual.
Lazy loading relies on proxies. If your SportDivision entity is a proxy then
either loading the Sport entity works
or you get an exception telling you that the context is already disposed (if you have disposed the context)
Number 2 is not the case -> Conclusion: Number 1 must be the case if the pre-condition is fulfilled
But Number 1 also isn't the case (loading Sport does not work)
Conclusion: The pre-condition that your SportDivision entity is a proxy is not true.
So: SportDivision is not a proxy. Could this mean that you have lazy loading in the context disabled? No: Because you are saying that editing works it means that when you load entities from the database they are loaded as proxies and support lazy loading.
Editing works, lazy loading isn't disabled but creating a new entity does not work in the way that the Sport entity is loaded when you proceed to use the newly created entity.
Conclusion: Your newly created entity (returned from CreateOrUpdate) is not a proxy and CreateOrUpdate looks similar to this:
public TEntity CreateOrUpdate(TEntity raw) where TEntity : class
{
if (blabla)
; //update
else
{
context.Set<TEntity>().Add(raw);
context.SaveChanges();
return raw;
}
}
and FindById is just:
public TEntity FindById(int id)
{
return context.Set<TEntity>().Find(id);
}
Since you are passing raw directly into the Add method of the DbSet<T> the question raises where does raw come from and how is it created.
Obviously AutoMapper creates the entity after this line: var raw = Mapper.Map<TModel, TEntity>(entry);
How does Automapper create an entity? Probably by calling new TEntity or by using some reflection code like Activator.CreateInstance or...
It doesn't really matter how, but for sure AutoMapper doesn't instantiate an Entity Framework proxy which had to be created by:
var entity = context.Set<TEntity>().Create();
If all this is true, I feel totally screwed by AutoMapper and generic excesses. If all this wouldn't be generic we could solve the problem by:
context.Set<SportDivision>().Add(raw);
context.SaveChanges();
context.Entry(raw).Reference(r => r.Sport).Load();
Instead we must try some ugly tricks now:
context.Set<TEntity>().Add(raw);
context.SaveChanges();
context.Entry(raw).State = EntityState.Detached;
// We hope that raw is now really out of the context
raw = context.Set<TEntity>().Find(raw.Id);
// raw must be materialized as a new object -> Hurray! We have a proxy!
return raw;
(I'm really not sure if the Detached trick above does work. Aside from that you are forced to reload an entity from the database you just have created and saved which is stupid somehow.)
Potential trick number 2 (without reloading from DB but for the price of being a further step more ugly):
context.Set<TEntity>().Add(raw);
context.SaveChanges();
context.Entry(raw).State = EntityState.Detached;
// We hope that raw is now really out of the context
var anotherRaw = context.Set<TEntity>().Create(); // Proxy!
anotherRaw.Id = raw.Id;
context.Set<TEntity>().Attach(anotherRaw);
context.Entry(anotherRaw).CurrentValues.SetValues(raw);
context.Entry(anotherRaw).State = EntityState.Unchanged;
return anotherRaw; // Proxy! Lazy loading will work!
Does AutoMapper have a feature of a "custom allocator or instantiator" and can custom user data (a context) be supplied? Then there would be a chance to let AutoMapper call context.Set<TEntity>().Create();. Or is it possible to instantiate the object by hand, pass it to AutoMapper and AutoMapper just updates the object's properties?
BTW: The line...
context.Entry(anotherRaw).CurrentValues.SetValues(raw);
...is kind of EF's built-in "AutoMapper". The parameter of SetValues is a general System.Object (could be your ...Model object) and the method maps property values from the supplied object to properties of attached entities by identical property names. Maybe you can leverage this feature somehow instead of using the mapping from model to entity done by AutoMapper.

Error displaying details (ObjectContext instance has been disposed)

I'm building an ASP.NET MVC 3 app and I've got a model that looks something like so:
public partial class Flavor
{
// ...
public string Name { get; set; }
public bool HasNuts {get; set; }
public virtual ICollection<SaleData> Sales {get; set;}
// ...
}
which retrieves some data from a db as such:
public PartialViewResult Details(int id)
{
using (var db = new IceCreamDBFlavors())
{
Flavor someFlavor = db.Flavors.Find(id);
someFlavor.Sales = db.Sales.Where(c => c.FlavorID == id).ToList();
return PartialView("details", someFlavor);
}
}
over on the view I do something like this:
<fieldset>
<legend>Sales Data</legend>
#foreach (var sale in Model.Sales)
{
<div>Weekly</div>
<div>#sale.Weekly</div>
}
</fieldset>
If I don't retrieve the Sales data, my Flavor data displays fine with no errors, but adding the call to retrieve the list of sales data causes an error "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." to occur.
I've read a few other posts about this and guess I'm missing something here. I believe this error happens due to lazy loading, at least based on what I've read here and elsewhere. Setting a breakpoint in the Controller before returning the PartialView and checking the object, I believe, causes the evaluation to take place, so everything displays as I would want.
I was under the impression that the ToList() call would force the Sales collection to be filled in. Since I don't have the issue when that line's commented out, I assume the problem is still related to that and when the View is attempting to iterate the Sales, it can't. Am I correct here? I guess I thought I was forcing the evaluation. How do I resolve this?
My suspicion is that Flavor has other collections (and not just Sales) and it is in fact at the time of accessing those that it breaks.
Here you are replacing only Sales while other collections or complex properties would still need the object context.
Don't dispose the IceCreamDBFlavors class that inherits from ObjectContext, it needs to have a lifetime greater than is currently allowed.
Change
using (var db = new IceCreamDBFlavors())
{
Flavor someFlavor = db.Flavors.Find(id);
someFlavor.Sales = db.Sales.Where(c => c.FlavorID == id).ToList();
return PartialView("details", someFlavor);
}
To
try
{
var db = new IceCreamDBFlavors();
Flavor someFlavor = db.Flavors.Find(id);
someFlavor.Sales = db.Sales.Where(c => c.FlavorID == id).ToList();
return PartialView("details", someFlavor);
}
catch(Exception ex)
{
// log exeption
}

Need help rewriting this Linq query to move it from code-behind to DAL as reusable object

I'm working to move my simple asp.net website to a three layer architecture. Currently I have Linq queries like the one below in my code-behind files. Basically this code snippet retrieves a collection of customer data from the database and then binds it to a grid control.
I'm wondering if someone can guide me on how to rewrite this in order to move it to my newly-created data access layer. I'm thinking I will turn it into a class (e.g. GetUserBoxesByStatus()) that can be reused throughout the site.
var boxes = from p in sbm.Packages
where p.UserID == CurrentUserId && p.StatusID > 1 && p.StatusID < 3
select new { p.PackageTag, p.PackageName, p.DateReceived, p.DateShipped };
GridView1.DataSource = boxes;
DataBind();
Some of the options that I've investigated but have not had success implementing are the following:
DataTable --- returning a DataTable seems like the best solution but it also appears to require a lot of potentially unecessarry code to define a table (isn't the data source already mapped in my Linq 2 Entities dbml?)
IEneuerable --- I think I could pass an IEnumerable list between the layers but after reading many tutorials about Linq I'm still a little lost
DTO --- Conceptually I think I understand what a DTO is but I am not clear on how to begin implementing this approach
POCO --- Again, the concept seems logical enough but I don't know how to put this into practice
I'm hoping someone here can look at my code example and propose how they would tackle this using one of the above or some other solution.
Create a class with the properties you need. Select into that class. Return a strongly-typed List (so that the query is actually performed in the DAL, not in your view). Bind your data source to the list.
public class PackageViewModel
{
public string Tag { get; set; }
public string Name { get; set; }
public DateTime Received { get; set; }
public DateTime Shipped { get; set; }
}
DAL
public List<PackageViewModel> GetUserBoxesByStatus( int userID, int minStatus, int maxStatus )
{
return sbm.Packages
.Where( p => p.UserID == userID
&& p.StatusID > minStatus
&& p.StatusID < maxStatus )
.Select( p => new PackageViewModel
{
Tag = p.PackageTag,
Name = p.PackageName,
Received = p.DateReceived,
Shipped = p.DateShipped
})
.ToList();
}

Resources