I have a ViewModel, it takes two parameters in the constructor that are of the same type:
public class CustomerComparerViewModel
{
public CustomerComparerViewModel(CustomerViewModel customerViewModel1,
CustomerViewModel customerViewModel2)
{
}
}
public class CustomerViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
If I wasn't using IOC I could just new up the viewmodel and pass the sub-viewmodels in. I could package the two viewmodels into one class and pass that into the constructor but if I had another viewmodel that only needed one CustomerViewModel I would need to pass in something that the viewmodel does not need.
How do I go about dealing with this using IOC? I'm using Ninject btw.
Thanks
I'm not familiar with Ninject, but it would seem to me that in order for the IoC to know what CustomerViewModels to Inject into your constructor you would have to setup these objects in advance. Using MEF like attributes and Psuedo code it might look something like...
[Export()]
public class CustomerSelectorViewModel
{
[Export("CustomerA")]
public class CustomerViewModel FirstSelection {get;set;}
[Export("CustomerB")]
public class CustomerViewModel SecondSelection {get;set;}
}
[Export()]
public class CustomerComparerViewModel
{
[ImportingConstructor]
public CustomerComparerViewModel([Import("CustomerA")]CustomerViewModel customerViewModel1, [Import("CustomerB")]CustomerViewModel customerViewModel2)
{
}
}
Here's how to do it in Ninject:
Container.Bind<CustomerViewModel>().ToSelf().WhenTargetHas<CustomerA>();
Container.Bind<CustomerViewModel>().ToSelf().WhenTargetHas<CustomerB>();
Then in the constructor of the class you are using them in:
public class CustomerComparerViewModel
{
public CustomerComparerViewModel([CustomerA]CustomerViewModel customerA,
[CustomerB]CustomerViewModel customerB)
{
}
}
Related
Context
I have a model for representing comments that looks like the following:
public class Comment
{
public int Id { get; set; }
public int CommentId { get; set; } // Id of parent comment
...
}
In my DetailsModel class which is a subclass of PageModel, I have the following method for finding replies to a given comment:
public IList<Comment> Replies(int comment_id) =>
_context.Comment.Where(comment => comment.CommentId == comment_id).ToListAsync().Result;
I use it from a Razor page as follows:
Model.Replies(reply.Id).Count
This works fine.
More object-oriented approach?
In a more traditional object-oriented design, Replies might be a method on Comment. So finding the replies would look like this:
reply.Replies()
Moving the Replies method into Comment, we get:
public class Comment
{
public int Id { get; set; }
public int CommentId { get; set; } // Id of parent comment
...
public async Task<IList<Comment>> Replies()
{
return await _context.Comment.Where(comment => comment.CommentId == Id).ToListAsync();
}
}
And now you can see the issue; Comment now has a dependency on the DbContext, which seems like a very odd arrangement.
So my question is, is there a way to get Replies to be a method on Comment in a way that's idiomatic for ASP.NET Core / EF Core?
UPDATE - CommentAlt
Here's one approach I've explored.
I have a CommentAlt class.
It is a subclass of Comment (so as to inherit all the fields from Comment).
The constructor accepts a BasePageModel.
My page models inherit from BasePageModel.
BasePageModel gives CommentAlt access to things like
DbContext
UserManager<IdentityUser>
AuthorizationService
Here's CommentAlt:
public class CommentAlt : Comment
{
private BasePageModel Model { get; }
public CommentAlt(Comment comment, BasePageModel model)
{
Id = comment.Id;
CommentId = comment.CommentId;
...
Model = model;
}
public IList<CommentAlt> Replies() =>
Model.Context.Comment
.Where(comment => comment.CommentId == Id).ToList()
.Select(elt => new CommentAlt(elt, Model)).ToList();
...
}
As you can see, now Replies is a method on it. In one of my Razor pages, I have the following expression:
#if (reply.Replies().Count > 0)
and it works fine.
Here's my BasePageModel which is passed to the CommentAlt contructor:
public class BasePageModel : PageModel
{
public ApplicationDbContext Context { get; }
public IAuthorizationService AuthorizationService { get; }
public UserManager<IdentityUser> UserManager { get; }
public BasePageModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager) : base()
{
Context = context;
UserManager = userManager;
AuthorizationService = authorizationService;
}
}
One thing I feel a little weird about is having the Context, AuthorizationService, and UserManager properties here be public. I changed them to public so that ConmmentAlt would have access to them.
Is the approach taken here with CommentAlt recommended? Is there a better way?
my current task needs to pay attention on mapping between different object types and so I recognized the very nice AutoMapper library.
So far easy to handle but these different objects contains complex interface type properties. Let me show you a code snippet:
public inferface IInterface
{
string TextProperty { get; set;}
}
public class A : IInterface
{
string TextProperty { get; set; }
}
public class B : IInterface
{
string TextProperty { get; set; }
}
public inferface IComplexInterface
{
IInterface ComplexProperty { get; set; }
}
public class ComplexA : IComplexInterface
{
IInterface ComplexProperty { get; set; }
}
public class ComplexB : IComplexInterface
{
IInterface ComplexProperty { get; set; }
}
In my case it is possible that class A is mapped to class B and vice versa.
Mapping from type A to B is no problem by configuring CreateMap<A, B>();
Mapping from class ComplexA to class ComplexB throws an exception:
Error mapping types.
Mapping types:
ComplexA -> ComplexB
NamespaceOfComplexA.ComplexA -> NamespaceOfComplexB.ComplexB
Type Map configuration:
ComplexA -> ComplexB
NamespaceOfComplexA.ComplexA -> NamespaceOfComplexB.ComplexB
Property:
ComplexProperty
A possible solution I already found here on stackoverflow could be a configuration as follows:
CreateMap<A, IInterface>().As<B>();
CreateMap<B, IInterface>().As<A>();
But in my case it is not working.
Any suggestions?
Now, I found a solution that works for me.
I use AutoMapper with a non generic approach and so I configure via:
CreateMap(typeof(ComplexA), typeof(ComplexB))
To consider properties with complex types like interfaces or even abstract classes it is possible to write an own ValueResolver that has to implement the interface:
IValueResolver<object, object, object>
with following method:
public object Resolve(object source, object destination, object destMember, ResolutionContext context)
{
//...
}
To resolve interface/abstract class properties you can configure your types by enhancing the configuration with the method ForMember(...) and define a conrete ValueResolver for the particular property as follows:
CreateMap(typeof(ComplexA), typeof(ComplexB)).ForMember("ComplexProperty", x => x.ResolveUsing(new ValueResolver(/*...*/)));
In my case it was the solution to map the interface property to a concrete implementation of my class definitions.
Hope it is useful.
I use EF 5 with the code first approach. Now I try to define a "code" table in which I want to have several different codes (like address code, medium code, etc.). In this table I just have the following properties: ID (Guid), Name (String), Description (String) and a discriminator (in this case something like the type of the code: address code, medium code, etc.).
So I defined the following base class:
public abstract class Code : EntityBase
{
public string Name { get; set; }
public string Beschreibung { get; set; }
}
Then I derived two classes from code
public class AddressCode : Code {}
public class MediumCode : Code {}
The class EntityBase is abstract and just defines the Id property, we use it for every POCO class...
The goal is that I can use AddressCode as a property on my address POCO class:
public class Adresse : EntityBase
{
#region Properties
public string Name1 { get; set; }
public virtual AddressCode AddressCode { get; set; }
#endregion
}
The question now is, how can I explain EF how to do that? Anyone can help?
Thanks
Marco
Thanks for your answer!
I tried to do it like you said. Unfortunately I get an error because of my EntityBase class:
public abstract class EntityBase
{
#region Properties
public virtual Guid Id { get; set; }
public virtual bool IsValid
{
get
{
{
return Validate();
}
}
}
[NotMappedAttribute]
public virtual IList<ValidationFailure> ValidationFailures { get; set; }
#endregion
#region Methods
private bool Validate()
{
var validatorFactory = new AttributedValidatorFactory();
IValidator validator = validatorFactory.GetValidator(GetType());
if (validator == null)
{
return true;
}
ValidationResult validationResult = validator.Validate(this);
ValidationFailures = validationResult.Errors;
return validationResult.IsValid;
}
#endregion
}
The error message is:
You cannot use Ignore method on the property 'ValidationFailures' on type 'Entities.AdresseCode' because this type inherits from the type 'Entities.EntityBase' where this property is mapped. To exclude this property from your model, use NotMappedAttribute or Ignore method on the base type.
As you can see I already defined the property ValidationFailures as NotMapped but still I get this error.. Do you have an idea?
Thanks
Marco
Just create a context (derived from DbContext)
public class AddressesDb : DbContext
{
public DbSet<Code> Codes { get; set; }
public DbSet<Adresse> Adressen { get; set; }
}
And (when used in code) EF will create a database with default table and column names. It will create a discriminator column of type text (nvarchar) which will contain the names of the classes that derive from Code.
If you want different names and/or types you should either use data annotations or fluent API to configure these.
Finally I got it work!
In the DBContext be aware to define DbSets for the code derived classes before all the other POCO's and then it works!
I would like to add some logic to the insert and update events of some EF objects.
I have a MVC application with category object which has a property which is a slugified version of the name property.
public class Category
{
public string Name { get; set; }
public string UrlName{ get; set; }
}
I would like to set the UrlName property only on the insert and update events because my slugify logic is quite elaborate.
I am aware that I can add some logic inside the SaveChanges() function on the context itself but I rather would like to put the code closer to the entity itself.
Is there a way to accomplish such thing using EF code first?
You can setup a base class with methods to be called before insert and update
public abstract class Entity
{
public virtual void OnBeforeInsert(){}
public virtual void OnBeforeUpdate(){}
}
public class Category : Entity
{
public string Name { get; set; }
public string UrlName{ get; set; }
public override void OnBeforeInsert()
{
//ur logic
}
}
Then in your DbContext
public override int SaveChanges()
{
var changedEntities = ChangeTracker.Entries();
foreach (var changedEntity in changedEntities)
{
if (changedEntity.Entity is Entity)
{
var entity = (Entity)changedEntity.Entity;
switch (changedEntity.State)
{
case EntityState.Added:
entity.OnBeforeInsert();
break;
case EntityState.Modified:
entity.OnBeforeUpdate();
break;
}
}
}
return base.SaveChanges();
}
No there is no such extension point because your entity is POCO - it is not aware of its persistence. Such logic must be triggered in data access layer which is aware of persistence. DbContext API offers only overriding of SaveChanges.
You can expose custom events or methods on your entities and call them during processing in SaveChanges.
I have no clue how i can get an existing object structure based on the following classes (simplified) into a database using Entity Framework (EF is a constraint, i have to use it).
public abstract class WahWahProperty
{
public string Name { get; set; }
public abstract Type PropertyType { get; }
}
// ----------------
public class WahWahProperty<T> : WahWahProperty
{
public T Value { get; set; }
public override Type PropertyType
{
get { return typeof(T); }
}
}
// ----------------
public class WahWahContainer
{
public List<WahWahContainer> Children { get {...}; }
public List<WahWahContainer> Parents { get {...}; } // multiple "Parents" allowed
public List<WahWahProperty> Properties { get {...}; }
//... some more props here ...
}
Any ideas?
The EF doesn't support generic Entity types (which seems to be what you are doing).
Although we have made a change in EF 4.0 (not in Beta1) so you will be able to use a non-generic class derived from a generic class as an Entity.
Anyway hope this helps
Alex
Program Manager Entity Framework Team
Entity Framework Tips