I'm trying to map the results of a stored procedure into a model that contains another model several times. I'm not sure if this is possible, but wanted to ask and see if this is a valid approach, or if it would be better to define everything out individually.
My model classes look like this:
public class ClientSummary {
public virtual int Id { get; set; }
public virtual int Client_ID { get; set; }
public virtual string Client_Name { get; set; }
public virtual string State { get; set; }
public virtual EDITotal Totals { get; set; }
public virtual EDITotal In_Totals { get; set; }
public virtual EDITotal Out_Totals { get; set; }
public virtual EDITotal Direct_Totals { get; set; }
public virtual EDITotal CPN_Totals { get; set; }
}
public class EDITotal {
public virtual int Count { get; set; }
public virtual double Charges { get; set; }
public virtual double StateSavings { get; set; }
public virtual double PPOSavings { get; set; }
public virtual float PPO_Pct_Bill { get; set; }
public virtual float PPO_Pct_State { get; set; }
public virtual int Unique_TaxIds { get; set; }
}
What I'm not sure of is what my class map would look like in Fluent NHibernate, or if something like this is possible. I'm mainly trying to keep things tidy, and reusable. I'm sure I'll have other reports that will use similar metrics. Am I going about this the right way, or do I really need to define a full model for each report I build? This is all taking place inside a MVC Web application.
Thanks in advance!
Yes this is indeed possible and right or wrong you can get it working with the way you have your classes setup. To do this, you can use a mapper like this:
public class ClientSummaryMappingOverride : IAutoMappingOverride<ClientSummary>
{
public void Override(AutoMapping<ClientSummary> mapping)
{
mapping.References(x => x.Totals, "Totals_id");
mapping.References(x => x.In_Totals, "In_Totals_id");
mapping.References(x => x.Out_Totals, "Out_Totals_id");
mapping.References(x => x.Direct_Totals, "Direct_Totals_id");
mapping.References(x => x.CPN_Totals, "CPN_Totals_id");
}
}
Once you have your map configured, you just need to make sure your Stored Procedure or SQL Query returns the ClientSummary records with the appropriate "Totals_id" type fields. NHibernate will pick up those ids and map them to the correct data (Lazy Load I believe, depending on your conventions or other mappings).
Related
I have some model created, which is supposed to store GUIDs (strings) as identifiers and I want the class that has a foreign key with it to also have the object to be accessed. I remembered while using LinqToSql was pretty simple, but now I'm running into some troubles with EF.
I have tried putting the object as virtual but I think I have to do something on the DbContext, because when I try to create the migration it fails due to recursion limit (I guess this is due to making sort of circular references).
For example, I have the following classes:
public class Calculation
{
[Required]
public Guid Id { get; set; }
[Required]
public Guid UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
[Required]
public string Title { get; set; }
[Required]
public Guid CausativeId { get; set; }
[ForeignKey("CausativeId")]
public virtual Causative Causative { get; set; }
[Required]
public bool Active { get; set; }
}
public class Causative
{
[Required]
public Guid Id { get; set; }
[Required]
public string Document { get; set; }
public virtual Calculation Calculation { get; set; }
}
The idea is that the Calculation knows who is it's Causative, but I also want to be able to do it the other way around (this was something pretty useful on LinqToSql), if this is not possible, just automatically filling the Causative property with the corresponding data without the need of explicitly making another call to the database.
Should I do something in the DbContext instance? I have this in place:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Calculation>().HasOne(calculation => calculation.Causative).WithOne(causative => causative.Calculation);
}
I'm sure I'm doing something wrong, but I'm not sure what.
Thanks in advice!
For that model you created, you should change this one for OnModelCreating:
modelBuilder.Entity<Causative>()
.HasOne(p => p.Calculation)
.WithOne(i => i.Causative)
.HasForeignKey<Calculation>(b => b.CausativeId);
and this is for one to many if you have IEnumerable<Calculation> in Causative:
modelBuilder.Entity<Calculation>()
.HasOne(p => p.Causative)
.WithMany(b => b.Calculation);
I think that you just need to replace this line:
public virtual Calculation Calculation { get; set; }
with this:
public virtual List<Calculation> Calculations { get; set; }
or this:
public virtual ICollection<Calculation> Calculations { get; set; }
this will create for you the right navigation property and FK
I am trying to map a many to one relationship. I feel I'm close but not quite there.
I have this sensor table:
SensorId
FK_LocationId
Name
etc...
Which holds many data records in the Data Table.
DataId
FK_SensorId
Time
Value
And I am trying to create a Model for this.
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
[ForeignKey("DataSensorId")]
public virtual DataSensor datasensor { get; set; }
}
It should actually be the other way around, at least in my mind. Where the sensor would hold a List<Data> but there is no FK to link that in Sensor. A Data record is just mapped to a sensor by the FK of that sensor.
The problem I'm facing here is that I would have this line in my view now:
#model IEnumerable<DataVisualization.Models.Data>
And instead of looping over my sensors to display the information and then show the data (eventually a chart). I have to loop all the data, somehow organize it how I want and then display it. So I would still need:
#model IEnumerable<DataVisualization.Models.DataSensor>
But this does not give me access to the data since that is in Data and DataSensor does not expose any of that afaik. So I thought about somekind of class that maps them together:
public class DataViewModel
{
public DataSensor dataSensor { get; set; }
public List<GatheredData> gatheredData { get; set; }
}
And my view would require:
#model IEnumerable<DataVisualization.Models.DataViewModel>
This seemed an elegant way but I was not able to make it work. Probably since this would require public DbSet<DataViewModel> dataViewModel { get; set; } in the DbContext and that would produce a awkward table in my database.
So any help on how to create the Model, work with it in the Controller and displaying it in the View would be greatly appreciated.
Edit
What about this Model so I have access to the data connected to this?
public class DataSensor
{
[Key]
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
However, this creates a column DataSensor_Id in the database table. This obviously is undesirable since then it would be a one to one.
I am going to leave my other answer below because it still provides valuable information.
This is how you can accomplish just pulling the data from the database for display. You are using the database first approach. I would recommend creating a new project to test this and get accustomed to what is taking place. Now you are going to want to go to tools in the ribbon and select connect to database. Enter the relevant information to connect to the database. Now create new project, if you haven't already. For the purpose of learning create an Asp.Net5 web application with no authentication. Now, go to your NuGet Package Manager Console. Run " Install-Package EntityFramework.MicrosoftSqlServer -Pre ". Once that is completed Run " Install-Package EntityFramework.Commands –Pre " and " Install-Package EntityFramework.MicrosoftSqlServer.Design –Pre ". Once those are installed go to your Project.Json file and in the commands section add "ef": "EntityFramework.Commands" . Now, go to command prompt and cd to your projects directory. The easiest way I have found to do this is to right click your project and open folder in file explorer. Once you do that, go up one level so all you see is one folder. If you see all of the contents of your project then that is NOT the right place. Shift+RightClick on the folder and you should see the option to Open Command Window Here. Click that and once Command Prompt has opened in your project directory. Run
dnvm use 1.0.0-rc1-update1
or I have found that not to work in some cases. If that doesn't work then Run
dnvm use 1.0.0-rc1-final
If neither of these work you need to install 1.0.0-rc1. Once one of those work, Run
dnx ef dbcontext scaffold
"Server=EnterYourConnectionStringHere;Database=YourDataBaseNameHere;Trusted_Connection=True;"
EntityFramework.MicrosoftSqlServer --outputDir Models
Once that is complete you should have your models that were created from the database in the Models Directory. Go to your newly created Context Class in the Models Directory. Once in there you will see an override void OnConfiguring method. Delete that. Open your Startup.cs and put these using statements at the top.
using YourProject.Models;
using Microsoft.Data.Entity;
Now in your ConfigureServices Method in Startup.cs add
var connection =#"Server=YourConnectionString;Database=YourDatabaseName;Trusted_Connection=True;";
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<YourContextName>(options => options.UseSqlServer(connection));
Then the rest is just creating controllers and views for your newly registered context.
Old Answer Below
Try using the fluent API to specify the relationship. Like this: (Hint: this is in your ApplicationDbContext.)
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<DataSensor>()
.HasMany(p => p.Data)
.WithOne();
}
public DbSet<DataSensor> DataSensor { get; set; }
public DbSet<GatheredData> GatheredData { get; set; }
And your classes like this:
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
[ForeignKey("DataSensorId")]
public virtual DataSensor datasensor { get; set; }
}
You can also do this by convention like this
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
public virtual DataSensor datasensor { get; set; }
}
or by data annotations like this
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
[ForeignKey("DataSensorId")]
public virtual DataSensor datasensor { get; set; }
}
If you do this by convention and it doesn't work, data annotations could help map them. If all else fails then the fluent API will override everything and map the relationship.
Now, for how to display this data. If you are just trying to display the data and not edit it then I think creating a partial view will be your best bet. In your shared folder create a blank view. Add your model to the new view.
#model IEnumerable<DataVisualization.Models.GatheredData>
Then do a foreach loop to iterate through that data.
#foreach (var item in Model)
{
<p>#Html.DisplayFor(modelItem => item.Id)</p>
<p>#Html.DisplayFor(modelItem => item.Time)</p>
<p>#Html.DisplayFor(modelItem => item.Value)</p>
}
Then back in your main View for the Datasensor put the following where you want the data.
#Html.Partial("StringNameOfThePartialView", Model.Data)
The first overload is the name of the partial view, and the second is the model data to be passed to that view.
So, i have this class here:
public class Platillo
{
public virtual int ID { get; set; }
public virtual String NombrePlatillo { get; set; }
public virtual int idRestaurante { get; set; }
public virtual String DescripcionPlatillo { get; set; }
public virtual bool esAprobado { get; set; }
public virtual bool esDisponible { get; set; }
public virtual double precio { get; set; }
public virtual DateTime vigenciaPlatillo { get; set; }
public virtual List<ListaIngredientes> listadeIngredientes { get; set;}
}
I've created a strongly typed PlatilloController that makes all basic CRUD operations. The problem is, the View renderes everything but the List.
My idea is to create a List that allows to add new ingredients(Ingredientes) and the amount of servings (Porciones) for each one on a dish (Platillo).
public class ListaIngredientes
{
public virtual int ID { get; set; }
public virtual Ingrediente ingrediente { get; set; }
public virtual int porciones { get; set; }
}
public class Ingrediente
{
public virtual int ID { get; set; }
public virtual String NombreIngrediente { get; set; }
//...
}
So, what i was thinking was to implement a PartialView that rendered a list of every ingredient on the dish, and that allowed to add new ingredients. How exactly do I do that? And, since i plan on creating and updating new ingredients on the same page, i'm sure i should be taking AJAX into account. How exactly can i use Ajax to Create and display ListaIngrediente's list entries?
Just a sample using a partial view page, try this:
#model <Project>.Models.Platillo
#foreach(ListaIngredientes ing in listadeIngredientes)
{
<div>#ing.ID</div>
<div>#ing.Ingrediente.NombreIngrediente</div>
}
I am alittle bit confused about your design why would you use a list public
virtual List<ListaIngredientes> listadeIngredientes { get; set;} I would start but first creating a Platillo foreign key in your ListaIngredientes that would match with Platillo something like PlatilloID so now you would have a list of ListaIngredientes that match the Platilloios and it ingridients.
and then sure you can create an ajax partial view at which you can add Ingrediente and then match specific ingridients with your Platilloio on a separate view in another word create separately ingridiets and Platilloio and then on another view add ingridiets from the ingridients in your Ingridients table to Pelatio.
I am using code first and EF 4.1 im my MVC3 App and have two models that have two
relationships between them. Here is the classes that refers to these models:
public class Cattle
{
public int CattleID { get; set; }
public int FarmID { get; set; }
public int SituationID { get; set; }
public int DiscardID { get; set; }
public int Stamp { get; set; }
public string Sex { get; set; }
public string Condition { get; set; }
public virtual Farm Farm { get; set; }
[InverseProperty("Cattles2")]
public virtual ICollection<Farm> Farms { get; set; }
}
public class Farm
{
public int FarmID { get; set; }
public string Name { get; set; }
public virtual ICollection<Cattle> Cattles { get; set; }
public virtual ICollection<Cattle> Cattles2 { get; set; }
}
One relationship is one to one or many cause a catle can only be in a farm and a farm can contains many catles. Another relationship is many to many that a catle can be transferred between farms and i would generate a third table to store the transfers using Fluent API. I also would like to insert a Date property to the new table and don´t know how to do it.Here is the code in the FarmContext inside the OnModelCreating method:
modelBuilder.Entity<Catle>()
.HasMany(c => c.Farms).WithMany(i => i.Catles)
.Map(t => t.MapLeftKey("CatleID")
.MapRightKey("FarmID")
.ToTable("CatleTransfer"));
When i build the project it seems to work fine and the generated tables Catle and Farm are fed by the FarmInitializer but some information that points to this relationship is missing in the index page of Catle. its appearing in blank to me. Here is the code that gets the information:
#Html.DisplayFor(modelItem => item.Farm.Name)
I need Knowing what i am doing wrong or if there is a more appropriate method to resolve that issue.
You may need to be specific about the relationship when dealing with multiple joins.
Try adding a Foreign Key attribute to your Farm navigation property.
[ForeignKey("FarmID")]
public virtual Farm Farm { get; set; }
I'm new to NHibernate and FNH as well. I'm familiar with ORM's and decided to see what all the buzz was about with this particular one mostly because of the productivity gains. At this point I think my time would have been better spent using something else but I don't want to let this defeat me and I'm hoping it's a stupid error I'm making because I want so badly for this to impress me. Anyway, I tried manually mapping the entities (Fluent) to no avail and am now trying to use the automap feature. Here is my domain model.
public class Book
{
public virtual int Id { get; protected set; }
public virtual string Isbn { get; set; }
public virtual string Title { get; set; }
public virtual string Author { get; set; }
public virtual string Publisher { get; set; }
public virtual User Seller { get; set; }
public virtual ICollection<string> Pics {get; private set;}
public virtual string Condition { get; set; }
public virtual decimal Price { get; set; }
public virtual ExchangeMethod PreferedExchangeMethod { get; set; }
}
public class Course
{
public virtual int Id { get; protected set; }
public virtual University University { get; set; }
public virtual Semester Semester { get; set; }
public virtual string Description { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Book> RequiredBooks { get; private set; }
}
public class Exchange
{
public virtual int Id { get; protected set; }
public virtual User Buyer { get; set; }
public virtual User Seller { get; set; }
public virtual Book Book { get; set; }
public virtual ExchangeMethod Method { get; set; }
public virtual decimal Price { get; set; }
public virtual int SellerFeedbackRating { get; set; }
public virtual int BuyerFeedbackRating{get; set;}
public virtual string SellerComment { get; set; }
public virtual string BuyerComment { get; set; }
}
public class Semester
{
public virtual int id { get; protected set; }
public virtual University University { get; set; }
public virtual int Year { get; set; }
public virtual string Term { get; set; }
}
public class University
{
public virtual string Name { get; set; }
public virtual int Connections { get; set; }
public virtual ICollection<Course> Courses { get; private set; }
}
public class User
{
public virtual string UserName { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string Email { get; set; }
public virtual ICollection<Course> Enrollment { get; private set; }
public virtual ICollection<Book> Library { get; private set; }
public virtual ICollection<Exchange> Exchanges { get; private set; }
}
And here is my mapping
public static ISessionFactory CreateSessionFactory()
{
IPersistenceConfigurer persistenceConfigurer = SQLiteConfiguration.Standard.UsingFile(DbFile);
return Fluently.Configure()
.Database(persistenceConfigurer)
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<User>(assem => assem.Namespace == "DataTransferObjects")
.Override<User>(map => map.Id(user => user.UserName))
.Override<University>(map => map.Id(univ => univ.Name))
))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private static void BuildSchema(Configuration config)
{
// delete the existing db on each run
if (File.Exists(DbFile))
File.Delete(DbFile);
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config).Create(false, true);
}
This is the error I keep getting which is pretty vague:
XML validation error: The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'property' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'meta, subselect, cache, synchronize, comment, tuplizer, id, composite-id' in namespace 'urn:nhibernate-mapping-2.2'
Thanks for any help and sorry for the long post.
EDIT
Does the way I layed out my model leave me prone to errors with circular logic e.g. User has a book and the book has a user? I would expect the automapping to be able to pick that up.
Are you using the latest version of FNH and NHibernate 2.1 RTM?
I am not so sure about your Overrides of the Id code. Have you tried without this and having an Id in each table?
Here is how I do my mappings with Automap. I tell it what Assembly to pull from, what Namespace in particular and then I give it the BaseTypes that my entities derive from.
I use the ConventionDiscovery to change some of the foreign key naming conventions and set up cascading and inverse properties, etc.
PersistenceModel = AutoPersistenceModel
.MapEntitiesFromAssemblyOf<>()
.Where(type => type.Namespace != null && type.Namespace.Contains("Model"))
.WithSetup(s =>
{
s.IsBaseType = type => type == typeof (DateTimeBase)
|| type == typeof (SimpleBase);
})
.ConventionDiscovery.AddFromAssemblyOf();
Then I add the persistence model to the automappings. I use the ExportTo method to get a copy of the generated xml files - looking at the xml helps to diagnose some problems.
.Mappings(m => m.AutoMappings
.Add(persistenceModel)
.ExportTo(#"../../ExportAutoMaps"));
AutoMapping has worked great for me - though it did take time to learn and experiment.
I am using jet database and I have to explicitly create my database file for NHibernate to run the schema on. I am unfamiliar with how sqlLite works.
Well, as I thought it was a stupid error. For the Semester Entity I had "id" instead of "Id". Kind of disapointing that FNH is case sensitive when doing the automapping, I would have thought otherwise with the "strong typing" support they have to prevent typos in config files.
FYI, When doing automapping it by default tries to map to a property called "Id" on each of your entities unless you specify other wise.