Entity Framework - Explicit loading objects - c#

I'm currently having an issue with explicit loading in Entity Framework.
I've disabled Proxy Creation and Lazy Loading in my datacontext.
public DataContext()
: base("")
{
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
}
This has been done because I need to be able to serialize my entity framework objects, which I cannot do when using virtual properties because this creates a virtual property.
I would like to retrieve the entity 'ContentType':
[Table("T_CONTENT_TYPES")]
public class ContentType : Entity
{
#region Properties
/// <summary>
/// Gets or sets the name of the content type.
/// </summary>
[Required]
[Column(Order = 1)]
public string Name { get; set; }
/// <summary>
/// Gets or sets the <see cref="Folder"/> in which this item belongs.
/// If this field is empty, it means that this item is stored in no particular folder, which means it's stored in the root.
/// </summary>
[Column(Order = 100)]
public Folder Folder { get; set; }
#endregion
}
This is done by using this code:
var contentType = UnitOfWork.ContentTypeRepository.GetById(Id);
As you see, my entity has a reference to a Folder. Since LazyLoading & Proxy Creation is disabled, I use the following code to retrieve the folder:
((DataContext) UnitOfWork.DataContext).Entry(contentType).Reference(x => x.Folder).Load();
My complete method is then:
public ContentType GetById(int Id)
{
var contentType = UnitOfWork.ContentTypeRepository.GetById(Id);
((DataContext) UnitOfWork.DataContext).Entry(contentType).Reference(x => x.Folder).Load();
return contentType;
}
The GetById method looks like the following:
public TEntity GetById(int id)
{
if (GetEntityById != null)
{ GetEntityById(this, new EventArgs()); }
var returnData = dbSet.FirstOrDefault(x => x.Id == id);
if (GetEntityByIdFound != null)
{ GetEntityByIdFound(this, new EventArgs()); }
return returnData;
}
However, the 'Folder' property is still null.
Anyone has an idea why this is not working?
If somebody knows a good working alternative for serializing lazy loaded entities, I'm prepared to integrate that one.

Related

Creating a computed property with Entity Framework 6

I am using Database First approach and I have created model from a database. Now I have a datagrid view in my Winforms app, that is bound to a binding source. And all that works fine (an appropriate data is shown in datagrid view). Now the problem is, how to add a computed property that consists of two values (already found in db) ? For an example:
Lets say that I have a table user (id, username, first_name, last_name, user_type) but I want to have different columns in my bound datagrid view, and I want to have these columns:
username, full name, type
where "full name" is what would I get with first_name + " " + last_name.
I guess I can't just modify a model class manually like this:
public string FullName
{
get
{
return FirstName + " " + LastName;
}
protected set {}
}
because this class is generated automatically , and my code will be deleted each time a generate models from an existing database (when I make some change), so this is not the real option...
Actually, I solved this by using partial class functionality:
I have created another file that contains another portion of my User model class (with my additional properties) and everything went just fine.
namespace User.Model
{
public partial class User
{
public string FullName
{
get
{
return (this.firstName + " " + this.lastName;
}
protected set { }
}
}
}
Now when I generate model classes, this new file is not affected by EF. Also this new field is correctly shown by datagrid view...
I still cant add comments, so I will have to do it this way.
Regarding your approach, why don't you create a data transfer model that you will bind to the data grid view?
With this approach, your new model will have the needed property FullName and you can show this in your grid view. You database entity model will remain the same. With this, you have decoupled the database model from the view model and achieved what you wanted.
UPDATE:
/// <summary>
/// Assuming this is the EF model, generated with the database first approach. We leave it as is.
/// </summary>
public class UserEntityModel
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int UserType { get; set; }
}
/// <summary>
/// This model represents your grid presentation specific data. As you can see, we have the fields
/// that we will show. This is the new DTO model
/// </summary>
public class UserGridViewModel
{
public string UserName { get; set; }
public string FullName { get; set; }
public int UserType { get; set; }
}
/// <summary>
/// This method demonstrates the retrieving and model to model mapping.
/// </summary>
public UserGridViewModel GetUser(int userId)
{
//retrieve the UserEntityModel
var efObject = _context.User.Where(user => user.Id == userId).SingleOrDefault();
if (efObject == null) {
return null;
}
// construct the new object, set the required data
return new UserGridViewModel()
{
UserName = efObject.UserName,
UserType = efObject.UserType,
FullName = $"{efObject.FirstName} {efObject.LastName}"
};
}
Additional explanation:
Let's assume that UserEntityModel is your database first generated data model. we will leave it as is.
We will create a second model, UserGridViewModel that contains data only that you will show in your grid. This is the DTO model.
The GetUser Method should conceptually demonstrate the usage of the first (ef model) and then the second (DTO) model. We retrieve the data from the database, we construct the DTO model and pass it to the grid.
You can find more information here and here.
Hope this helps, cheers and happy coding!

AutoMapperMappingException: Missing type map configuration or unsupported mapping, Mapping List of Types

I have the following code:
The structure of my project is like this:
My classes (relevant for this bug)
SizeEntity.cs
namespace DataObjects.EntityFramework
{
public class SizeEntity
{
public int Id { get; set; }
public string SizeName { get; set; }
}
}
Size.cs (on business objects class library)
namespace BusinessObjects
{
// Product business object
// ** Enterprise Design Pattern: Domain Model, Identity Field, Foreign key mapping
public class Size : BusinessObject
{
// ** Enterprise Design Pattern: Identity field pattern
public Size()
{
// establish business rules
AddRule(new ValidateRequired("SizeName"));
AddRule(new ValidateLength("SizeName", 1, 3));
}
public int Id { get; set; }
public string SizeName { get; set; }
}
}
SizeDao.cs
public class SizeDao : ISizeDao
{
/// <summary>
/// Constructor to initialize AutoMapper
/// </summary>
static SizeDao()
{
Mapper.Initialize(cfg => cfg.CreateMap<SizeEntity, Size>());
Mapper.Initialize(cfg => cfg.CreateMap<List<SizeEntity>, List<Size>>());
}
/// <summary>
/// Inserts size into database
/// </summary>
/// <param name="size"></param>
public void InsertSize(Size size)
{
using (var context = new ExamContext())
{
var entity = Mapper.Map<Size, SizeEntity>(size);
context.Sizes.Add(entity);
context.SaveChanges();
// update business object with new id
size.Id = entity.Id;
}
}
/// <summary>
/// Gets all size from database
/// </summary>
/// <returns>Returns a list of Sizes</returns>
public List<Size> GetSizes()
{
using (var context = new ExamContext())
{
var sizes = context.Sizes.ToList();
return Mapper.Map<List<SizeEntity>, List<Size>>(sizes);
}
}
}
I am getting the mapping error on the last line of this code.
return Mapper.Map, List>(sizes);
What am I missing here?
Error message is in the title:
AutoMapperMappingException: Missing type map configuration or unsupported mapping, Mapping List of Types
Update 1:
I removed that line suggest and still get:
Missing type map configuration or unsupported mapping.
Mapping types:
SizeEntity -> Size
DataObjects.EntityFramework.SizeEntity -> BusinessObjects.Size
For first you should remove the initialization of the mapping where you specify the List of objects, you just need to define the mapping with the base objects. That initialization would lead to an empty result when you call Map method.
static SizeDao()
{
Mapper.Initialize(cfg => cfg.CreateMap<SizeEntity, Size>());
// This is not needed.
// Mapper.Initialize(cfg => cfg.CreateMap<List<SizeEntity>, List<Size>>());
}
Another thing, as suggested #CodeCaster, don't put your mapping definition in the static constructor, but, for example, in the entry point of the whole application and verify that those lines get called before you invoke the Map method.
Because Automapper didnt work, then I had to do the following:
/// <summary>
/// Gets all size from database
/// </summary>
/// <returns>Returns a list of Sizes</returns>
public List<Size> GetSizes()
{
using (var context = new ExamContext())
{
var sizes = context.SizeEntities.ToList();
//Convert SizeEntities list to Size (Business Objects) list
var targetList = sizes
.Select(x => new Size() { Id = x.Id, SizeName = x.SizeName})
.ToList();
return targetList;
//return Mapper.Map<List<SizeEntity>, List<Size>>(sizes);
}
}

Can't get ShouldDecompile configuration in DelegateDecompiler to work

I'm trying to get an auto decompile configuration for DelegateDecompiler to work, as shown here: http://daveaglick.com/posts/computed-properties-and-entity-framework
But it doesn't work :(
Not sure what I'm doing wrong.
Here's the class that has a computed value.
public class Person
{
public int Id { get; set; }
public string First { get; set; }
public string Last { get; set; }
[NotMapped]
[Computed]
public string Full { get { return First + " " + Last; } }
}
This is the configuration.
public class DelegateDecompilerConfiguration : DefaultConfiguration
{
public override bool ShouldDecompile(MemberInfo memberInfo)
{
// Automatically decompile all NotMapped members
return base.ShouldDecompile(memberInfo) || memberInfo.GetCustomAttributes(typeof(NotMappedAttribute), true).Length > 0;
}
}
I also tried removing the [NotMapped] and then changed typeof(NotMappedAttribute) to typeof(ComputedAttribute) in the above configuration.
Then I register it like so
DelegateDecompiler.Configuration.Configure(new DelegateDecompilerConfiguration());
In Startup.cs. I also tried putting it directly into my action.
public ActionResult Test()
{
DelegateDecompiler.Configuration.Configure(new DelegateDecompilerConfiguration());
var ctx = new ApplicationDbContext();
var result = ctx.People.Where(x => x.Full.Contains("foo bar")).ToList();
return View();
}
Neither work :(
If I put .Decompile() on the query then it works as expected. So the DelegateDecompiler is working, but not the configuration.
As you discovered in your GitHub issue, you always have to call .Decompile() within your LINQ query. The extra configuration just eliminates the need to decorate all of your computed properties with the Computed attribute, instead relying on the Entity Framework NotMapped attribute.
As Dave says it requires Decompile()
So you don't have to remember to call it you can create a mapper extension that wraps what you are doing.
public static class MapperExtensions
{
/// <summary>
/// Creates a list from automapper projection. Also wraps delegate decompiler to supprt Computed Domain properties
/// </summary>
/// <typeparam name="TDestination"></typeparam>
/// <param name="projectionExpression"></param>
/// <returns></returns>
public static List<TDestination>
ToList<TDestination>(this IProjectionExpression projectionExpression)
{
return projectionExpression.To<TDestination>().Decompile().ToList();
}
}

C# Entity Framework high memory usage, memory leak?

I've got a small MVC webapplication running using Entity Framework 6. When starting the application by browwsing to the homepage (eg. www.mywebsite.dev) on my dev. machine the applicationpool get's started and the page get loaded as expected.
Despithe the fact that the homepage is pretty ligthweight and only get's a few things from the database (2 menu's, 2 paragraphs with text, and a collection with 3-4 objects) the application pool is already > 200 MB (!) after just loading the homepage once..
Using this and this article i've managed to figure out how to profile the manage memory, and I also removed a few static properties blocking the disposal of the context.
The DbContext has lazy loading disabled,
public class MyContext: DbContext
{
private readonly Dictionary<Type, EntitySetBase> _mappingCache = new Dictionary<Type, EntitySetBase>();
#region dbset properties
//Membership sets
public IDbSet<UserProfile> UserProfiles { get; set; }
public IDbSet<Project> Project { get; set; }
public IDbSet<Portfolio> Portfolio { get; set; }
public IDbSet<Menu> Menu { get; set; }
public IDbSet<MenuItem> MenuItem { get; set; }
public IDbSet<Page> Page { get; set; }
public IDbSet<Component> Component { get; set; }
public IDbSet<ComponentType> ComponentType { get; set; }
public IDbSet<BlogCategory> BlogCategory { get; set; }
public IDbSet<Blog> Blog { get; set; }
public IDbSet<Caroussel> Carousel { get; set; }
public IDbSet<CarouselItem> CarouselItem { get; set; }
public IDbSet<Redirect> Redirect { get; set; }
public IDbSet<TextBlock> TextBlock { get; set; }
public IDbSet<Image> Image { get; set; }
public IDbSet<ImageContent> ImageContent { get; set; }
#endregion
/// <summary>
/// The constructor, we provide the connectionstring to be used to it's base class.
/// </summary>
public MyContext() : base("name=MyConnectionstring")
{
//Disable lazy loading by default!
Configuration.LazyLoadingEnabled = false;
Database.SetInitializer<BorloContext>(null);
}
//SOME OTHER CODE
}
I still see a lot of objects in memory from which I expect they're related to entity framework's lazy loading.
I've setup the website with a few layers;
Controller - The usual stuff
Service - Trought a using statement used in the controllers. The services are disposable and contain a UnitOfWork. The UnitOfWork is initialized in the Constructor of the service and disposed when the service itsself get's disposed.
UnitOfWOrk - The UnitOfWork class contains a readonly private variable containing the context, together with a set of properties instantiating a Generic Repository of type T. Again, the UnitOfWork is disposable and it disposes the context when the Dispose method is called.
The Generic Repository matches an interface, takes the DbContext trought it's constructor and offers a basic set of methods trough an interface.
Below an example of how this is used.
PartialController
public class PartialController : BaseController
{
//private readonly IGenericService<Menu> _menuService;
//private readonly UnitOfWork _unitOfWork = new UnitOfWork();
//private readonly MenuService _menuService;
public PartialController()
{
//_menuService = new GenericService<Menu>();
//_menuService = new MenuService();
}
/// <summary>
/// Renders the mainmenu based on the correct systemname.
/// </summary>
[ChildActionOnly]
public ActionResult MainMenu()
{
var viewModel = new MenuModel { MenuItems = new List<MenuItem>() };
try
{
Menu menu;
using (var service = ServiceFactory.GetMenuService())
{
menu= service.GetBySystemName("MainMenu");
}
//Get the menuItems collection from somewhere
if (menu.MenuItems != null && menu.MenuItems.Any())
{
viewModel.MenuItems = menu.MenuItems.ToList();
return View(viewModel);
}
}
catch (Exception exception)
{
//TODO: Make nice function of this and decide throwing or logging.
if (exception.GetType().IsAssignableFrom(typeof(KeyNotFoundException)))
{
throw;
}
else
{
//TODO: Exception handling and logging
//TODO: If exception then redirect to 500-error page.
}
}
return View(viewModel);
}
}
ServiceFactory
public class ServiceFactory
{
public static IService<Menu> GetMenuService()
{
return new MenuService();
}
}
MenuService
public class MenuService : BaseService, IService<Menu>
{
private readonly UnitOfWork _unitOfWork;
private bool _disposed;
public MenuService()
{
if (_unitOfWork == null)
{
_unitOfWork = new UnitOfWork();
}
}
/// <summary>
/// Retrieves the menu by the provided systemname.
/// </summary>
/// <param name="systemName">The systemname of the menu.</param>
/// <returns>The menu if found. Otherwise null</returns>
public Menu GetBySystemName(string systemName)
{
var menu = new Menu();
if (String.IsNullOrWhiteSpace(systemName)) throw new ArgumentNullException("systemName","Parameter is required.");
if (Cache.HasItem(systemName))
{
menu = Cache.GetItem(systemName) as Menu;
}
else
{
var retrievedMenu = _unitOfWork.MenuRepository.GetSingle(m => m.SystemName.Equals(systemName), "MenuItems,MenuItems.Page");
if (retrievedMenu == null) return menu;
try
{
var exp = GenericRepository<CORE.Entities.MenuItem>.IsPublished();
var menuItems = (exp != null) ?
retrievedMenu.MenuItems.AsQueryable().Where(exp).Select(MenuTranslator.Translate).OrderBy(mi => mi.SortOrder).ToList() :
retrievedMenu.MenuItems.Select(MenuTranslator.Translate).OrderBy(mi => mi.SortOrder).ToList();
menu.MenuItems = menuItems;
}
catch (Exception)
{
//TODO: Logging
}
Cache.AddItem(systemName, menu, CachePriority.Default, CacheDuration.Short);
}
return menu;
}
public IEnumerable<Menu> Get()
{
throw new NotImplementedException();
}
~MenuService()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_unitOfWork.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
GenericRepository
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class, IEntityObject
{
///
/// The database context used.
///
internal MyContext Context;
/// <summary>
/// The loaded set of entities.
/// </summary>
internal DbSet<TEntity> DbSet;
/// <summary>
/// The constructor taking the databasecontext.
/// </summary>
/// <param name="context">The databasecontext to use.</param>
public GenericRepository(MyContext context)
{
//Apply the context
Context = context;
//Set the entity type for the current dbset.
DbSet = context.Set<TEntity>();
}
public IQueryable<TEntity> AsQueryable(bool publishedItemsOnly = true)
{
if (!publishedItemsOnly) return DbSet;
try
{
return DbSet.Where(IsPublished());
}
catch (Exception)
{
//TODO: Logging
}
return DbSet;
}
/// <summary>
/// Gets a list of items matching the specified filter, order by and included properties.
/// </summary>
/// <param name="filter">The filter to apply.</param>
/// <param name="includeProperties">The properties to include to apply eager loading.</param>
/// <param name="publishedItemsOnly">True if only publish and active items should be included, otherwise false.</param>
/// <returns>A collection of entities matching the condition.</returns>
public virtual IQueryable<TEntity> Get(Expression<Func<TEntity, bool>> filter, string includeProperties, bool publishedItemsOnly)
{
var query = AsQueryable(publishedItemsOnly);
if (filter != null)
{
query = query.Where(filter);
}
if (String.IsNullOrWhiteSpace(includeProperties))
return query;
//Include all properties to the dbset to enable eager loading.
query = includeProperties.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
return query;
}
}
To make a very long story short. What in my code / situation could possibly cause the problem that when loading just the homepage a amazing 200 MB or more is used? One strange thing i've noticed is that just before the page is loaded the memory jumps from 111 mb to 232 MB in the example below;
EDIT
Result from trace with dotMemory
EDIT 2
Below the results after i loaded the homepage. The homepage is now empty and in the global asax a single call to one service is made. I've kept the page open for a while and then refreshed, resulting in all the peaks.
Below a more detailed result, with apperently a lot of strings taking a lot of memory..?
EDIT 3
Different view from dotMemory
So, image is much more clearer now.
dotMemory displays, that your app takes only 9Mb of memory, we can see this on the Snapshot view. This is also confirmed by Memory Traffic view. ~73Mb was allocated from the beginning of profiling and ~65Mb was already collected to the Snapshot #1 point.
What about total memory usage displayed on the real time data chart, sorry I did not realized earlier then the most of your app memory usage is generation 0 heap. (And also I missed that your app uses only ~8Mb on the snapshot tile on this screen).
Gen 0 heap size displays the maximum bytes that can be allocated in
generation 0; it does not indicate the current number of bytes
allocated in generation 0.
http://msdn.microsoft.com/en-us/library/x2tyfybc(v=vs.110).aspx
Gen 0 heap size looks abnormally big for my taste, but it is an internal details of .net garbage collector, and it has a right to do that.
I have ventured to suggest that your app is running on the computer with big amount of RAM and/or with big CPU cache. But it can be also special aspects of ASP server implementation.
Conclusion - there is no problem with your app memory usage :) At least on loading just the homepage.
P.S. I would recommend to watch dotMemory video tutorials, in order to learn how to use it

How do you do web forms model validation?

We have an application with three layers: UI, Business, and Data. The data layer houses Entity Framework v4 and auto-generates our entity objects. I have created a buddy class for the entity VendorInfo:
namespace Company.DataAccess
{
[MetadataType(typeof(VendorInfoMetadata))]
public partial class VendorInfo
{
}
public class VendorInfoMetadata
{
[Required]
public string Title;
[Required]
public string Link;
[Required]
public string LinkText;
[Required]
public string Description;
}
}
I want this validation to bubble up to the UI, including custom validation messages assigned to them. In MVC this is a piece of cake but in web forms I have no clue where to begin. What is the best way to utilize model validation in asp.net web forms?
I did find an article that explains how to build a server control for it, but I can't seem to get it working. It compiles and even recognizes the control but I can never get it to fire.
Any ideas?
Thanks everyone.
I solved it. It would appear that the server control I found was not designed to read fields in a buddy class via the MetadataType attribute. I modified the code to look for its validation attributes in the buddy class rather than the entity class itself.
Here is the modified version of the linked server control:
[DefaultProperty("Text")]
[ToolboxData("<{0}:DataAnnotationValidator runat=server></{0}:DataAnnotationValidator>")]
public class DataAnnotationValidator : BaseValidator
{
#region Properties
/// <summary>
/// The type of the source to check
/// </summary>
public string SourceTypeName { get; set; }
/// <summary>
/// The property that is annotated
/// </summary>
public string PropertyName { get; set; }
#endregion
#region Methods
protected override bool EvaluateIsValid()
{
// get the type that we are going to validate
Type source = GetValidatedType();
// get the property to validate
FieldInfo property = GetValidatedProperty(source);
// get the control validation value
string value = GetControlValidationValue(ControlToValidate);
foreach (var attribute in property.GetCustomAttributes(
typeof(ValidationAttribute), true)
.OfType<ValidationAttribute>())
{
if (!attribute.IsValid(value))
{
ErrorMessage = attribute.ErrorMessage;
return false;
}
}
return true;
}
private Type GetValidatedType()
{
if (string.IsNullOrEmpty(SourceTypeName))
{
throw new InvalidOperationException(
"Null SourceTypeName can't be validated");
}
Type validatedType = Type.GetType(SourceTypeName);
if (validatedType == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}",
"Invalid SourceTypeName", SourceTypeName));
}
IEnumerable<MetadataTypeAttribute> mt = validatedType.GetCustomAttributes(typeof(MetadataTypeAttribute), false).OfType<MetadataTypeAttribute>();
if (mt.Count() > 0)
{
validatedType = mt.First().MetadataClassType;
}
return validatedType;
}
private FieldInfo GetValidatedProperty(Type source)
{
FieldInfo field = source.GetField(PropertyName);
if (field == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}",
"Validated Property Does Not Exists", PropertyName));
}
return field;
}
#endregion
}
This code only looks in the buddy class. If you want it to check an actual class and then its buddy class, you'll have to modify it accordingly. I did not bother doing that because usually if you are using a buddy class for validation attributes it's because you are not able to use the attributes in the main entity class (e.g. Entity Framework).
For model validation in web forms I'm using DAValidation library. It supports validation on client side (including unobtrusive validation), extensibility based on same principles as in MVC. It is MS-PL licensed and available via Nuget.
And here is bit out of date article describing with what thoughts control was build.

Categories