I am trying to do some dynamic code using Entity Framework. I have a model (Model1) with one table(Test1), it's simple. What I'm trying to do is accessing the model Test1 programatically with the name of the table, to use it after in differents tasks. I was looking for in google and I have found Finding entities by key in entity framework but it's doesnt work, or I don't have any idea...
When I ran this code it breaks on trying to set entityProperty
Model1Container m = new Model1Container();
PropertyInfo entityProperty = m.GetType().GetProperties().Where(t => t.Name == "Test1").Single();
var baseQuery = (IQueryable<IIdentity>)entityProperty.GetValue(m, null);
Sorry for the explanation.
Any ideas?
You create an object with a string name and set its properties:
public class Test
{
//All my classes have these properties
//You can set up an interface and in the method you can set entity to an interface type
//You can even put these interfaces on edmx generated entities
//http://stackoverflow.com/questions/14059455/adding-validation-attributes-with-an-entity-framework-data-model
public string AString { get; set; }
public DateTime ADate { get; set; }
}
public class HomeController : Controller
{
public ActionResult IndexStackOverflow101()
{
Assembly assembly = Assembly.Load("Testy20161006");
Type t = assembly.GetType("Testy20161006.Controllers." + "Test");
Object entity = (Object)Activator.CreateInstance(t);
PropertyInfo entityProperty = t.GetProperty("AString");
PropertyInfo entityPropertyTwo = t.GetProperty("ADate");
entityProperty.SetValue(entity, Convert.ChangeType("ap", entityProperty.PropertyType), null);
entityPropertyTwo.SetValue(entity, Convert.ChangeType(DateTime.Now, entityPropertyTwo.PropertyType), null);
Related
I am using Asp.Net identity within my MVC app. I can see that this has it's own ApplicationDbContext - albeit it is connected to the same SQL db as my own DbContext I am using elsewhere.
So I am trying to access some of my own data via my own code within the AccountController - it does not seem to work I presume because of some confusion over which DBContext it thinks is active?
My Code :
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private PostageManager postmgr;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, PostageManager _postmgr)
{
UserManager = userManager;
SignInManager = signInManager;
postmgr = _postmgr;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//create select list items for countries drop down
List<SelectListItem> countries;
countries = postmgr.GetCountries().Select(item => new SelectListItem
{
Value = item.Country,
Text = item.Country
}).ToList();
countries.Insert(0, new SelectListItem { Value = string.Empty, Text = "Select delivery country or region...", Selected = true });
RegisterViewModel mode = new RegisterViewModel
{
Countries = countries
};
return View();
}
}
}
PostageManager is just a class that sits over my DAL to fetch some data (which uses repository pattern) - I'm using just a kind of pass through method to grab a list of countries, and using it in exactly the same way I have in other controllers which works fine. Underneath that class is my repository code that is linked to my default connection string (DBContext). It's balking at the following line with a null reference exception, I think postmgr is null :
countries = postmgr.GetCountries().Select(item => new SelectListItem
In reverse to get access to the identity data within my own controllers I have done the following :
public BasketController(BasketManager _mgr, PostageManager _postmgr, ProductManager _prodmgr)
{
mgr = _mgr;
postmgr = _postmgr;
prodmgr = _prodmgr;
shopper = Cart.GetShopperId();
this.applicationDbContext = new ApplicationDbContext();
this.userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.applicationDbContext));
}
protected ApplicationDbContext applicationDbContext { get; set; }
protected UserManager<ApplicationUser> userManager { get; set; }
Which as far as I understand it points the identity code to use the right DbContext - I looked at doing this in reverse in my AccountController but can't fathom it out.
I basically just want to be able to use my own code that grabs my own data from within the Identity controllers to help pass extra data etc through to the views.
I might be wrong but most probably postmgr field is not initialized from constructor and that is why you have this error.
Explanation:
By default Asp will try to create controller instance by constructor without parameters. If Asp can't find constructor without parameters it will try to call constructor with parameters, but to make it possible you have to configure IoC in your app. As your controler has constructor without parameters it will be selected by Asp. So all 3 fields are empty.
But in properties SignInManager and UserManager you try to take value from field or from OwinContext. As field is empty your code will take value from OwinContext. OwinContext is quite complex and smart tool that create its context automatically based on configuration provided in Startup.Auth.cs file or any other file under App_Start folder.
I think I have figured it out - added the following to my NinjectControllerFactory :
ninjectKernel.Bind<IAuthenticationManager>().ToMethod(c => HttpContext.Current.GetOwinContext().Authentication); //.InRequestScope();
ninjectKernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
ninjectKernel.Bind<UserManager<ApplicationUser>>().ToSelf();
ninjectKernel.Bind<IRoleStore<IdentityRole, string>>().To<RoleStore<IdentityRole, string, IdentityUserRole>>();
ninjectKernel.Bind<RoleManager<IdentityRole>>().ToSelf();
And changed my constructor to :
public AccountController(PostageManager _postmgr)
{
postmgr = _postmgr;
}
I am trying to make a custom data annotation attribute with a simple bool result, if the property have the data annotation, then perform an action. My objective is to make an array with all the properties that have the Filter dataanotation to later make a DropDownListFor in view with that information.
public class Foo
{
public string property1 { get; set; }
[Filter]
public string property2 { get; set; }
}
Then somewhere in the code:
IList<PropertyInfo> properties = typeof(Foo).GetProperties().ToList();
ArrayList propertiesThatCanBeFiltered = new ArrayList();
foreach (var propertyInfo in properties)
{
if (propertyInfo.Attributes("Filter").Exists)
{
propertiesThatCanBeFiltered.Add(propertyInfo.Name);
}
}
You need GetCustomAttribute<T> Documentation
And here how to apply it:
IList<PropertyInfo> properties = typeof(Foo).GetProperties().ToList();
ArrayList propertiesThatCanBeFiltered = new ArrayList();
foreach (var propertyInfo in properties)
{
var filterAttribute = propertyInfo.GetCustomAttribute<Filter>();
if (filterAttribute != null)
{
propertiesThatCanBeFiltered.Add(propertyInfo.Name);
}
}
I believe custom ActionFilters is what you are after:
http://msdn.microsoft.com/en-us/library/dd410056(v=vs.100).aspx
This is what you are stating in your question where when a certain attribute is applied to then execute some logic... AF's does just that.
Other than that, there is a way to create your own custom data annotation:
How to create Custom Data Annotation Validators
http://weblogs.asp.net/brijmohan/asp-net-mvc-3-custom-validation-using-data-annotations
but it depends exactly what you want to do. Do not get the 2 mixed up together.
I have an Interface IBasicData that implements IVeryBasicData for users information (inherited code):
public interface IBasicData:IVeryBasicData
{
byte[] Password { get; set; }
string Email { get; set; }
}
public interface IVeryBasicData:IUser
{
string Name { get; set; }
string UserId { get; set; }
string Description { get; set; }
string Url { get; set; }
}
public interface IUser
{
DateTime CreationTime { get; set; }
string PartitionKey { get; set; }
string RowKey { get; set; }
}
Then I have a method GetUsers from UsersDataSource that returns an IQueryable<IBasicData>, I want to be able to use this IQueryable as the model for a View. But when I try to do it an Exception comes out: the properties cannot be found when calling them in #Hml.DisplayNameFor(model => model.UserId) for example.
So, I've come with this solution:
foreach (var user in usersDataSource.GetUsers())
{
var addUser = new UserViewModel()
{
CreationTime = user.CreationTime,
Description = user.Description,
Email = user.Email,
Name = user.Name,
PartitionKey = user.PartitionKey,
Password = user.Password,
RowKey = user.RowKey,
Url = user.Url,
UserId = user.UserId
};
usersViewModel.Add(addUser);
}
return View(usersViewModel);
UserViewModel is a class implementing IBasicData. This works, but seems rather ugly to me. Is there a better way to be able to use an IQuaryable<IBasicData> as the View model?
Update your view to take an IEnumerable in the model declaration first.
View
#model IEnumerable<IBasicData>
#{foreach(var user in Model){
//I don't know how you are wanting to render out the data...example only
#user.Email<br/>
#user.Name
}}
Then, in your controller, you should be able to do the following:
var modelData = usersDataSource.GetUsers().Select(user=>new UserViewModel{
CreationTime = user.CreationTime,
Description = user.Description,
Email = user.Email,
Name = user.Name,
PartitionKey = user.PartitionKey,
Password = user.Password,
RowKey = user.RowKey,
Url = user.Url,
UserId = user.UserId
}).ToList();
return View(modelData );
Calling .ToList() causes the query to execute immediately and retrieve the data before going to your View.
Are you sure the exception isn't being thrown because the model binder, operating on objects of interface IBasicData, only knows about the Password and Email properties - the model binder probably uses reflection and is maybe not picking up the base interfaces?
Quote from This article on MSDN Magazine, emphasis mine
For example, even though the Microsoft .NET Framework provides
excellent support for object-oriented principles, the
DefaultModelBinder offers no support for binding to abstract base
classes and interfaces.
To be honest I'd recommend having your ViewModel class just explicitly implement all 3 interfaces, and then keep the projection from your actual EF entity into an instance of this class (i.e usersDataSource.Select(u=>new UserViewModel{...}); and then bind on the view with #model IEnumerable
Interface inheritance doesn't sound like the right approach here; I wouldn't worry about the projection code you have, it's pretty normal and bear in mind it's providing a nice separation between your EF layer and your presentation layer (View).
I do agree with Tommy though on the IEnumerable; once you get out of your EF layer, IEnumerable-ise your data right away so as to materialise your data and close the database connection as quickly as possible.
Just a little idea I'm playing with, not sure if it's viable or has much of a use.
I'm trying to generate a very basic EF Code First database using the Roslyn CTP.
Code:
var scriptEngine = new ScriptEngine(new[] { "System", "System.Core", typeof(DbContext).Assembly.Location });
var session = Roslyn.Scripting.Session.Create();
var t = scriptEngine.CompileSubmission<DbContext>(#"
using System.Data.Entity;
public class Car {
public int Id {get; set;}
public string Name {get; set; }
}
public class Context : DbContext {
public DbSet<Car> Cars {get; set; }
}
new Context();
", session);
t.Execute();
When executed I get the following exception
Exception:
The type 'Submission#0+Car' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.
Looking through the list of possible issues, I'm guessing that Roslyn is making a nested class as part of the code gen. This makes sense otherwise the "new Context();" call would need to be wrapped into a class/method of some sort. I could emit an assembly, which would confirm the above but likely wouldn't have any clues on how to write it correctly.
I also went down the route of Syntax.ClassDeclaration, but ended up with a few hundred lines of code just to make a class with 1 property and no obvious way how to instantiate that class.
Question
Is there an easy way to create a class in Roslyn that is publicly accessible (eg not nested in another class)?
You can use Roslyn to create actual DLL library that contains your type based on your source code and then use that from your script:
var classCode = #"
using System.Data.Entity;
public class Car {
public int Id { get; set; }
public string Name { get; set; }
}
public class Context : DbContext {
public DbSet<Car> Cars { get; set; }
}";
var syntaxTree = SyntaxTree.ParseCompilationUnit(classCode);
var compilation = Compilation.Create(
"car",
new CompilationOptions(assemblyKind: AssemblyKind.DynamicallyLinkedLibrary))
.AddReferences(
new AssemblyFileReference(typeof(object).Assembly.Location), // mscorlib
new AssemblyFileReference(typeof(Uri).Assembly.Location), // System
new AssemblyFileReference(typeof(IOrderedQueryable<>).Assembly.Location), // System.Data
new AssemblyFileReference(typeof(DbContext).Assembly.Location) // EntityFramework
)
.AddSyntaxTrees(syntaxTree);
var dllPath = "car.dll";
using (var stream = File.OpenWrite(dllPath))
{
compilation.Emit(stream);
}
var code = #"new Context();";
var scriptEngine = new ScriptEngine(new[] { new FileInfo(dllPath).FullName, "EntityFramework" });
var context = scriptEngine.Execute<DbContext>(code);
I want to know if there is a way to achieve a related list of the same entity on the same class or use migrations to setup a model with this attribute in it?
public class Person
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
[InverseProperty("FamilyMembers")]
public List<Person> FamilyMembers { get; set; }
}
At the moment when I am using migrations (AddMigration) to setup the database I get the following exception:
Add-Migration : A relationship cannot be established from property
'FamilyMembers' on type 'ConsoleApplication3.Person' to property
'FamilyMembers' on type 'ConsoleApplication3.Person'. Check the values
in the InversePropertyAttribute to ensure relationship definitions
are unique and reference from one navigation property to its
corresponding inverse navigation property.
Also I get the same error adding Person's to the People dbset when running the a test app.
var p1 = new Person();
p1.Id = Guid.NewGuid();
p1.Name = "p1";
var p2 = new Person();
p2.Id = Guid.NewGuid();
p2.Name = "p2";
var c = new TestContext();
c.People.Add(p1);
Is there another attribute that is not unique and references the same navigation property?
The simple answer is no. Each end of the relation must have its own navigation property.