I'm struggling to get Fluent NHibernate to play nice with SQL Server's Geospatial types. I want to store a geographic point in my Place class, but I keep getting an NHibernate configuration error when I run my ASP.NET MVC app:
Method 'SetParameterValues' in type 'NHibernate.Spatial.Type.GeometryType' from
assembly 'NHibernate.Spatial, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' does not have an implementation.
Update: This is caused by an out-dated NHibernate.Spatial DLL. Referencing the latest version (2.2+) solves the problem. Kudos to psousa for leading me to a solution.
Place class:
using System;
using GisSharpBlog.NetTopologySuite.Geometries;
using NHibernate.Validator.Constraints;
namespace MyApp.Data.Entities
{
public class Place
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual Point Location { get; set; }
}
}
Fluent Place Mapping:
using MyApp.Data.Entities;
using FluentNHibernate.Mapping;
using NHibernate.Spatial.Type;
namespace MyApp.Data.Mappings
{
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
ImportType<GisSharpBlog.NetTopologySuite.Geometries.Point>();
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location)
.CustomType(typeof(GeometryType));
}
}
}
Fluent NHibernate Configuration:
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringKey))
.ShowSql()
.Dialect("NHibernate.Spatial.Dialect.MsSql2008GeographyDialect,NHibernate.Spatial.MsSql2008"))
.ExposeConfiguration(BuildSchema)
.Mappings(x => x.FluentMappings.AddFromAssembly(typeof(UserMap).Assembly)
.Conventions.AddFromAssemblyOf<ColumnNullabilityConvention>());
You're using a Geography dialect but using a CustomType of Geometry on your mapping. You should use a custom type of Geography. Something like:
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location).CustomType(typeof(MsSql2008GeographyType)); //for SQL2008
}
}
Also, there's something else that you may need to do. If your spatial column has an SRID different from 0 (zero), and if you want to skip NH xml mappings, you'll need to declare a custom type like this:
public class Wgs84GeographyType : MsSql2008GeographyType
{
protected override void SetDefaultSRID(GeoAPI.Geometries.IGeometry geometry)
{
geometry.SRID = 4326;
}
}
And then use it on your mapping:
public class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Location).CustomType(typeof(Wgs84GeographyType));
}
}
UPDATE:
You should be referencing NHibernate.Spatial.MsSql2008.dll, and I would advise you to use the strongly-typed Dialect method in your database configuration.
.Dialect<MsSql2008GeographyDialect>()
Related
I have two tables:
class company
{
prop Id //PK
prop EskaId //PK
}
class policy
{
prop Id //PK
prop CompanyEskaId //FK
}
I want to link CompanyEskaId with EskaId using Fluent API.
I've tried
HasRequired(b => b.CompanyObj).WithMany().HasForeignKey(c => c.CompanyEskaId);
But in this case how can determine the target PK is EskaId, not the Id
In EF6 an Entity has only a single Key so you would have to make EskaId the key of CompanyObj.
EF Core supports Alternate Keys where an entity has multiple keys and a Foreign Key can refer to any key. eg:
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogUrl)
.HasPrincipalKey(b => b.Url);
}
}
I've got a database structure where I've got an Equipment table with the columns Equipment_Id, Field_1, and Field_2. I've got an Equipment_Locale table with the fields Equipment_Id and Desc. The Ids are the same in both tables, and there is a one-to-one relationship between these tables.
I've got the following entity:
public class Equipment
{
public long Id { get; set; }
public string Description { get; set; }
public long Field1 { get; set; }
public long Field2 { get; set; }
}
I've got the following EntityTypeConfiguration:
public class EquipmentMapping : EntityTypeConfiguration<Equipment>
{
public EquipmentMapping()
{
ToTable("EQUIPMENT");
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("EQUIPMENT_ID");
Property(e => e.Field1).HasColumnName("FIELD_1");
Property(e => e.Field2).HasColumnName("FIELD_2");
// TODO: Okay, now I need to get the description in here!
}
}
I need to map the description in there, though, which comes from the EQUIPMENT_LOCALE table's DESC column.
This answer gives me a pretty clear idea on how I could use this if I was defining the mapping in ModelBuilder. However, we've been using files with EntityTypeConfigurations on this project and just having the model builder add those configurations, and I'm not sure how to set up a two table mapping in one of those. How can I accomplish this?
It turns out that the answer I linked which did it in ModelBuilder was really, really close to what I needed to simply put in my EntityTypeConfiguration file. I'd just never used Map() in EntityTypeConfiguration before so I was a bit clueless.
The following seems to work for me:
public class EquipmentMapping : EntityTypeConfiguration<Equipment>
{
public EquipmentMapping()
{
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("EQUIPMENT_ID");
Property(e => e.Field1).HasColumnName("FIELD_1");
Property(e => e.Field2).HasColumnName("FIELD_2");
Property(e => e.Description).HasColumnName("DESC");
Map(m =>
{
m.Properties(e => new
{
e.Id,
e.Field1,
e.Field2
});
m.ToTable("EQUIPMENT");
});
Map(m =>
{
m.Properties(e => new
{
e.Id,
e.Description
});
m.ToTable("EQUIPMENT_LOCALE");
});
}
}
You're going to need a navigation property in your parent:
public virtual Equipment_Locale Equipment_Locale { get; set; }
Then you can add to equipment configuration mapping like:
HasRequired(p => p.Equipment_Locale )
.WithMany()
.HasForeignKey(p => p.Equipment_LocaleId )
.WillCascadeOnDelete(false);
See here for relationship mapping: https://msdn.microsoft.com/en-us/data/jj591620.aspx
I am client-server, SQL programmer (RAD guy) and the company decided that we must move to .NET environment. We are examining the platform now.
I studied the MVC4 and the Entities Framework this week and I have read a lot about KendoUI. I was skeptical because most of the examples come with KendoGrid+WebApi. I know awfully little about WebApi but I really liked the Entities thing a lot so I do not think I should give it a chance.
I want to ask some question which may seem naive but the answers will help me a lot
Once I create entities from an existing database, can I have the results in Json format and with this to feed a KendoGrid?
If yes, how? I mean:
How I can convert the results in Json inside the Controller?
In the transport property of the KendoGrid I should put the URL of the Controller/Action?
and the most naive one
Does Telerik have any thoughts of providing a visual tool to create-configure the kendoGrid? To make it more RAD because now need TOO MUCH coding. Maybe a wizard that you can connect entities with Grid columns, datasource, transport selectors etc..
I hope you choose the Kendo Entities path, there will be a learning curve though. No, on question 4. But let me give you a jump start using the Razor view engine and answer 1, 2, and 3.
First, EF is creating business objects. You 'should' convert these to Models in MVC. In this example Person is from EF. I think of this as flattening because it removes the depth from the object, though it i still available so you could reference something like Person.Titles.Name if your database was setup like that. You can also drop in DataAnnotations, which just rock.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Project.Business;
namespace Project.Web.Models
{
public class PersonModel
{
public int Id { get; set; }
[Required(ErrorMessage = "Last Name is required.")]
public string LastName { get; set; }
[Required(ErrorMessage = "First Name is required.")]
public string FirstName { get; set; }
[Display(Name = "Created")]
public System.DateTime StampCreated { get; set; }
[Display(Name = "Updated")]
public System.DateTime StampUpdated { get; set; }
[Display(Name = "Enabled")]
public bool IsActive { get; set; }
public PersonModel()
{}
public PersonModel(Person person)
{
Id = person.Id;
FirstName = person.FirstName;
LastName = person.LastName;
StampCreated = person.StampCreated;
StampUpdated = person.StampUpdated;
IsActive = person.IsActive;
}
public static IList<PersonModel> FlattenToThis(IList<Person> people)
{
return people.Select(person => new PersonModel(person)).ToList();
}
}
}
Moving along...
#(Html.Kendo().Grid<PersonModel>()
.Name("PersonGrid")
.Columns(columns => {
columns.Bound(b => b.Id).Hidden();
columns.Bound(b => b.LastName).EditorTemplateName("_TextBox50");
columns.Bound(b => b.FirstName).EditorTemplateName("_TextBox50");
columns.Bound(b => b.StampUpdated);
columns.Bound(b => b.StampCreated);
columns.Bound(b => b.IsActive).ClientTemplate("<input type='checkbox' ${ IsActive == true ? checked='checked' : ''} disabled />").Width(60);
columns.Command(cmd => { cmd.Edit(); cmd.Destroy(); }).Width(180);
})
.ToolBar(toolbar => toolbar.Create())
.Pageable()
.Filterable()
.Sortable()
.Selectable()
.Editable(editable => editable.Mode(GridEditMode.InLine))
.DataSource(dataSource => dataSource
.Ajax()
.Events(events => events.Error("error_handler"))
.Model(model =>
{
model.Id(a => a.Id);
model.Field(a => a.StampCreated).Editable(false);
model.Field(a => a.StampUpdated).Editable(false);
model.Field(a => a.IsActive).DefaultValue(true);
})
.Create(create => create.Action("CreatePerson", "People"))
.Read(read => read.Action("ReadPeople", "People"))
.Update(update => update.Action("UpdatePerson", "People"))
.Destroy(destroy => destroy.Action("DestroyPerson", "People"))
.PageSize(10)
)
)
Those _TextBox50 are EditorTemplates named _TextBox50.cshtml that MUST go either in a subfolder relative to your view or relative to your Shared folder - the folder must be called EditorTemplates. This one looks like this...
#Html.TextBox(string.Empty, string.Empty, new { #class = "k-textbox", #maxlength = "50" })
Yes, thats all. This is a simple example, they can get much more complicated. Or you don't have to use them initially.
And finally, what I think you are really looking for...
public partial class PeopleController : Controller
{
private readonly IPersonDataProvider _personDataProvider;
public PeopleController() : this(new PersonDataProvider())
{}
public PeopleController(IPersonDataProvider personDataProvider)
{
_personDataProvider = personDataProvider;
}
public ActionResult Manage()
{
>>> Left in as teaser, good to apply a special Model to a View to pass goodies ;)
var model = new PeopleViewModel();
model.AllQualifications = QualificationModel.FlattenToThis(_qualificationDataProvider.Read());
return View(model);
}
[HttpPost]
public JsonResult CreatePerson([DataSourceRequest]DataSourceRequest request, Person person)
{
if (ModelState.IsValid)
{
try
{
person = _personDataProvider.Create(person);
}
catch (Exception e)
{
ModelState.AddModelError(string.Empty, e.InnerException.Message);
}
}
var persons = new List<Person> {person};
DataSourceResult result = PersonModel.FlattenToThis(persons).ToDataSourceResult(request, ModelState);
return Json(result, JsonRequestBehavior.AllowGet);
}
public JsonResult ReadPeople([DataSourceRequest]DataSourceRequest request)
{
var persons = _personDataProvider.Read(false);
DataSourceResult result = PersonModel.FlattenToThis(persons).ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult UpdatePerson([DataSourceRequest]DataSourceRequest request, Person person)
{
if (ModelState.IsValid)
{
try
{
person = _personDataProvider.Update(person);
}
catch (Exception e)
{
ModelState.AddModelError(string.Empty, e.InnerException.Message);
}
}
var persons = new List<Person>() {person};
DataSourceResult result = PersonModel.FlattenToThis(persons).ToDataSourceResult(request, ModelState);
return Json(result, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult DestroyPerson([DataSourceRequest]DataSourceRequest request, Person person)
{
if (ModelState.IsValid)
{
try
{
person = _personDataProvider.Destroy(person);
}
catch (Exception e)
{
ModelState.AddModelError(string.Empty, "There was an error deleting this record, it may still be in use.");
}
}
var persons = new List<Person>() {person};
DataSourceResult result = PersonModel.FlattenToThis(persons).ToDataSourceResult(request, ModelState);
return Json(result, JsonRequestBehavior.AllowGet);
}
}
Notice that, in this case, each method takes the EF Person as a parameter, it would be better to use PersonModel but then I would have to show the opposite of the Flatten. This works becuase they are practically identical. If the model was different or you were using a class factory it gets a bit more tricky.
I intentionally showed you all the CRUD operations. If you don't pass the result back to the grid it will act funny and give you dupicates or not show updates correctly on CREATE and UPDATE. It is passed back on DELETE to pass the ModelState which would have any errors.
And finally the data provider, so as to leave nothing to the imagination...
(Namespace declaration omited.)
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using Project.Business;
public class PersonDataProvider : ProviderBase, IPersonDataProvider
{
public Person Create(Person person)
{
try
{
person.StampCreated = DateTime.Now;
person.StampUpdated = DateTime.Now;
Context.People.Attach(person);
Context.Entry(person).State = EntityState.Added;
Context.SaveChanges();
return person;
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
throw;
}
}
public IList<Person> Read(bool showAll)
{
try
{
return (from q in Context.People
orderby q.LastName, q.FirstName, q.StampCreated
where (q.IsActive == true || showAll)
select q).ToList();
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
throw;
}
}
...
}
Note the Interface and ProviderBase inheritance, you have to make those. Should be simple enough to find examples.
This may seem like a lot of coding, but once you get it down, just copy paste.
Happy coding.
yes you can send the JSON back kendo UI using the JsonResult or using the JSON with allow get specified. you can set the url in the transport and specify the json as the type.
About your last question there is no visual tools right now but there are helper methods available for kendo UI
I have these 2 models:
public class SiteSettingsViewModel
{
public decimal SubscriptionFee { get; set; }
}
public class SiteSettings
{
public decimal SubscriptionFee { get; set; }
}
and the code:
var model = Mapper.Map<SiteSettings, SiteSettingsViewModel>(settingService.GetSettings());
Which throws the error of:
Trying to map WebApp1.Domain.Site.SiteSettings to WebApp1.WebUI.ViewModel.SiteSettingsViewModel.
Missing type map configuration or unsupported mapping.
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
Why do I need to put the code:
Mapper.CreateMap<SiteSettings, SiteSettingsViewModel>();
To me, this seems like I'm writing monkey code. This is not needed.
Why didn't the 1 line work?
One reason is that it's useful for more complex mapping scenarios where you need to define more specific behaviors. For example (from CodePlex):
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date))
.ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour))
.ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute));
Another option for simple mapping like what you're doing is to make a generic mapper that takes care of the CreateMap call for you, like this:
public interface IMapper<T1, T2>
{
T1 Map(T2 source);
}
public class Mapper<T1, T2> : IMapper<T1, T2> where T1 : class where T2 : class
{
public Mapper()
{
Mapper.CreateMap<T2, T1>();
}
public T1 Map(T2 source)
{
return Mapper.Map<T2, T1>(source);
}
}
And then you can just instantiate them directly like:
var mapper = new Mapper<SiteSettings, SiteSettingsViewModel>();
Or register them to be injected into your controllers, or wherever else you plan on using them. Hope that helps.
I have a problem with the Ninject.
My binding rules:
this.Bind<ISphinxQLServer>().To<SQLServer>();
this.Bind<IMySQLServer>().To<SQLServer>();
this.Bind<ISQLLogger>().To<StandardSQLLogger>()
.InRequestScope();
this.Bind<DatabaseConnections>()
.ToMethod(x => ConnectionFactory.GetConnections())
.InRequestScope();
this.Bind<SQLServer>().ToSelf()
.InRequestScope()
.WithConstructorArgument("connections", Kernel.Get<DatabaseConnections>())
.WithConstructorArgument("logger", Kernel.Get<ISQLLogger>());
Where
SQLServer, ISphinxQLServer and IMySQLServer are:
public class SQLServer: ISphinxQLServer, IMySQLServer
{
public DatabaseConnections Connections { get; internal set; }
public ISQLLogger Logger { get; internal set; }
public SQLServer(DatabaseConnections connections)
{
this.Connections = connections;
}
public SQLServer(DatabaseConnections connections, ISQLLogger logger)
{
this.Connections = connections;
this.Logger = logger;
}
}
I want that each user request to my asp.net mvc site creates a single SQLServer, a single ISQLLogger and a single DatabaseConnections. But my solution dont work. What am I doing wrong? =(
You don't need to specify the WithConstructorArgument. Resolving the parameters to the constructors of your injected objects is part of what Ninject does for you. So the definitions should look more like this:
this.Bind<SQLServer>()
.ToSelf()
.InRequestScope();
this.Bind<ISphinxQLServer>()
.ToMethod( x => x.Kernel.Get<SQLServer>() );
this.Bind<IMySQLServer>()
.ToMethod( x => x.Kernel.Get<SQLServer>() );
this.Bind<ISQLLogger>()
.To<StandardSQLLogger>()
.InRequestScope();
this.Bind<DatabaseConnections>()
.ToMethod(x => ConnectionFactory.GetConnections())
.InRequestScope();