I have been googling and stackoverflowing for the last two hours and couldn't find an answer for my question:
I'm using ASP.NET MVC and NHibernate and all I'm trying to do is to manually map my entities without mapping its base class. I'm using the following convention:
public class Car : EntityBase
{
public virtual User User { get; set; }
public virtual string PlateNumber { get; set; }
public virtual string Make { get; set; }
public virtual string Model { get; set; }
public virtual int Year { get; set; }
public virtual string Color { get; set; }
public virtual string Insurer { get; set; }
public virtual string PolicyHolder { get; set; }
}
Where EntityBase SHOULD NOT be mapped.
My NHibernate helper class looks like this:
namespace Models.Repository
{
public class NHibernateHelper
{
private static string connectionString;
private static ISessionFactory sessionFactory;
private static FluentConfiguration config;
/// <summary>
/// Gets a Session for NHibernate.
/// </summary>
/// <value>The session factory.</value>
private static ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
{
// Get the connection string
connectionString = ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString;
// Build the configuration
config = Fluently.Configure().Database(PostgreSQLConfiguration.PostgreSQL82.ConnectionString(connectionString));
// Add the mappings
config.Mappings(AddMappings);
// Build the session factory
sessionFactory = config.BuildSessionFactory();
}
return sessionFactory;
}
}
/// <summary>
/// Add the mappings.
/// </summary>
/// <param name="mapConfig">The map config.</param>
private static void AddMappings(MappingConfiguration mapConfig)
{
// Load the assembly where the entities live
Assembly assembly = Assembly.Load("myProject");
mapConfig.FluentMappings.AddFromAssembly(assembly);
// Ignore base types
var autoMap = AutoMap.Assembly(assembly).IgnoreBase<EntityBase>().IgnoreBase<EntityBaseValidation>();
mapConfig.AutoMappings.Add(autoMap);
// Merge the mappings
mapConfig.MergeMappings();
}
/// <summary>
/// Opens a session creating a DB connection using the SessionFactory.
/// </summary>
/// <returns></returns>
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
/// <summary>
/// Closes the NHibernate session.
/// </summary>
public static void CloseSession()
{
SessionFactory.Close();
}
}
}
The error that I'm getting now, is:
System.ArgumentException: The type or
method has 2 generic parameter(s), but
1 generic argument(s) were provided. A
generic argument must be provided for
each generic parameter
This happens when I try to add the mappings. Is there any other way to manually map your entities and tell NHibernate not to map a base class?
IncludeBase<T>
AutoMap.AssemblyOf<Entity>()
.IgnoreBase<Entity>()
.Where(t => t.Namespace == "Entities");
Read more here http://wiki.fluentnhibernate.org/Auto_mapping :)
If you don't want to automap a class, I would recommend using IAutoMappingOverride<T>. I don't about your database, but it might look like:
public class CarOverride : IAutoMappingOverride<Car>
{
public void Override(AutoMapping<Car> mapping){
mapping.Id( x => x.Id, "CarId")
.UnsavedValue(0)
.GeneratedBy.Identity();
mapping.References(x => x.User, "UserId").Not.Nullable();
mapping.Map(x => x.PlateNumber, "PlateNumber");
// other properties
}
}
Assuming you keep these maps centrally located, you could then load them on your autoMap:
var autoMap = AutoMap.Assembly(assembly).IgnoreBase<EntityBase>().IgnoreBase<EntityBaseValidation>()
.UseOverridesFromAssemblyOf<CarOverride>();
I know it's an old question but I think that some things are missing here :
When you use IgnoreBase<T> you are telling that you don't want to map inheritance into your database but Fluent Nhibernate will still map your base class as an individual class while you don't tell it not to do that, so if you want to tell Fluent Nhibnernate not to map the class itself you should inherit DefaultAutoMapConfiguration class and override its bool ShouldMap(Type type) and return false if the type is any type that you don't want to map it at all.
When you use AutoMapping generally you don't need any other mapping classes or overrides unless you want to make a change in your mappings and it's not possible doing that using Conventions or you just want to override a small part of one class.(Although you can do the same using Conventions and Inspectors)
You can use IsBaseType convention for your automappings
Related
i have two projects in my solution, UI as mvc and class project for entitiy model code first. I have severall entities in my model but now I need to extend them by new audit fields where I need to save who changed entity.
I added new interface
public interface IAuditable
{
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
DateTime CreatedDate { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
string CreatedBy { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
DateTime UpdatedDate { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
string UpdatedBy { get; set; }
}
and try to extend SaveChanges in this way
foreach (var auditableEntity in ChangeTracker.Entries<IAuditable>())
{
if (auditableEntity.State == EntityState.Added ||
auditableEntity.State == EntityState.Modified)
{
// implementation may change based on the useage scenario, this
// sample is for forma authentication.
string currentUser = ;
// modify updated date and updated by column for
// adds of updates.
auditableEntity.Entity.UpdatedDate = DateTime.Now;
auditableEntity.Entity.UpdatedBy =
// pupulate created date and created by columns for
// newly added record.
if (auditableEntity.State == EntityState.Added)
{
auditableEntity.Entity.CreatedDate = DateTime.Now;
auditableEntity.Entity.CreatedBy = currentUser;
}
else
{
auditableEntity.Property(p => p.CreatedDate).IsModified = false;
auditableEntity.Property(p => p.CreatedBy).IsModified = false;
}
}
but how do I get the userName here ? I can't use any httpContex getUser becuase this is class project. Any ideas?
this is my contex
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDbContext
so I thought to extend ApplicationUser by another field like LogedUserName, and fill it when user is loging, but how do I get this field in my SaveChanges method ?
If you are sure that this class library will be always used in ASP.NET pipeline you actually can access HttpContext.
You need a reference to System.Web in your class library and then:
using System.Web;
[...]
public void SaveChanges()
{
var username = HttpContext.Current.User.Identity.Name;
}
In this case HttpContext is a static class, not a property.
Ofcourse this will fail badly if this class is ever used outside ASP.NET pipeline (for example in WPF application, console app etc). Also it doesn't seem clean to do it this way. But it's probably the fastest way which requires minimal existing code change.
Another way would be to pass either username or whole identity to either class responsible for saving changes or directly to SaveChanges method.
One implementation could look like this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDbContext
{
private IPrincipal _currentUser;
public ApplicationDbContext(IPrincipal currentUser)
{
_currentUser = currentUser;
}
}
and then in Controller (if you use context directly in MVC controllers):
using(var db = new ApplicationDbContext(User))
{
[...]
}
where User is controller's property holding current user.
I'm developing a code-first database, using Entity Framework 6.
I know I can set [MaxLength(myLen)] on the property of a model.
What I wondered, is if this is possible to do in a filter or a custom attribute, so that all strings take on a default, of say 250, unless specified directly on the property.
Failing this, is there a way to change the default of nvarchar(max)?
Entity Framework introduced Custom Code First Conventions for this in 6.1
modelBuilder.Properties<string>()
.Configure(c => c.HasMaxLength(250));
Conventions operate in a last wins manner and the Fluent API and Data Annotations can be used to override a convention in specific cases
You can do this, which ensures all strings are the maximum length supported by the database provider:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<string>().Configure(p => p.IsMaxLength());
}
Add this method (or modify the existing one) in your DbContext class.
In EF6 you can use a custom code first convention, but you will also need to have a way to specify nvarchar(max) data type to a string property. So, I came up with the following solution.
Also see:
https://msdn.microsoft.com/en-us/data/jj819164#order
/// <summary>
/// Set this attribute to string property to have nvarchar(max) type for db table column.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class TextAttribute : Attribute
{
}
/// <summary>
/// Changes all string properties without System.ComponentModel.DataAnnotations.StringLength or
/// Text attributes to use string length 16 (i.e nvarchar(16) instead of nvarchar(max) by default).
/// Use TextAttribute to a property to have nvarchar(max) data type.
/// </summary>
public class StringLength16Convention : Convention
{
public StringLength16Convention()
{
Properties<string>()
.Where(p => !p.GetCustomAttributes(false).OfType<DatabaseGeneratedAttribute>().Any())
.Configure(p => p.HasMaxLength(16));
Properties()
.Where(p => p.GetCustomAttributes(false).OfType<TextAttribute>().Any())
.Configure(p => p.IsMaxLength());
}
}
public class CoreContext : DbContext, ICoreContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Change string length default behavior.
modelBuilder.Conventions.Add(new StringLength16Convention());
}
}
public class LogMessage
{
[Key]
public Guid Id { get; set; }
[StringLength(25)] // Explicit data length. Result data type is nvarchar(25)
public string Computer { get; set; }
//[StringLength(25)] // Implicit data length. Result data type is nvarchar(16)
public string AgencyName { get; set; }
[Text] // Explicit max data length. Result data type is nvarchar(max)
public string Message { get; set; }
}
In this code ModelBuilder class defines the shape of your entities, the relationships between them, and how they map to the database.
public class WebsiteDBContext : DbContext
{
public WebsiteDBContext(DbContextOptions<WebsiteDBContext> options) : base(options)
{
}
public DbSet<Global> Globals { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
// it should be placed here, otherwise it will rewrite the following settings!
base.OnModelCreating(builder);
builder.Entity<Global>();
builder.Entity<Global>(entity =>
{
entity.Property(global => global.MainTopic).HasMaxLength(150).IsRequired();
entity.Property(global => global.SubTopic).HasMaxLength(300).IsRequired(false);
entity.Property(global => global.Subject).IsRequired(false);
entity.Property(global => global.URL).HasMaxLength(150).IsRequired(false);
});
}
}
I have sets of entities all of them are derived from abstract class
public abstract class NamedEntity : INamedEntity
{
#region Public Properties
public string Description { get; set; }
public string Id { get; set; }
public string Name { get; set; }
#endregion
}
When I persist all entities I want to use Name field as a key, so I override DocumentKeyGenerator and provide such implementation:
store.Conventions.DocumentKeyGenerator = entity =>
{
var namedEntity = entity as NamedEntity;
if (namedEntity != null)
{
return string.Format("{0}/{1}", store.Conventions.GetTypeTagName(entity.GetType()), namedEntity.Name);
}
return string.Format("{0}/", store.Conventions.GetTypeTagName(entity.GetType()));
};
It works fine when I persist the list of entities for the first time, but if I want to persist them again I get an exception
PUT attempted on document 'xxxxx' using a non current etag
I just started using RavenDB, so I cannot understand what I am doing wrong?
Just a guess, but it's probably not with your key generation, but how you are storing them.
On first usage you probably have something like:
var myEntity = new MyEntity(...);
session.Store(myEntity);
...
session.SaveChanges();
That part is fine, but on subsequent usage, you should not be doing the same thing. Instead, it should be more like this:
var myEntity = session.Load<MyEntity>("myentities/foobar");
myEntity.Something = 123;
...
session.SaveChanges();
Note there is no call to .Store() when making changes. This is because the entity is "tracked" by the session, and all changes to it are automatically persisted when you call .SaveChanges()
I am using NHibernate 3.1 and Fluent NHibernate as ORM in my project. I need to have a property of a POCO ignored by Fluent NHibernate. At first, my post might look as exact duplicate of this question, but it is not.
My complications come first from the fact that the POCOs are defined in a different assembly than the mapping and I am using fluent mappings for my POCOs. I have additional requirement not to write ingore-property code where the session factory configuration takes place (this happens at a centralized place outside the modules), but as part of the module that defines the mappings. Ideally, I believe the right place would be the concrete ClassMap implementation, since it knows exactly how to describe a POCO to the ORM.
However, I am stuck on this mainly because this is my first impact with NHibernate and its fluent API. Up to now I am having very good impression of its capabilities and extensibility, and I hope there is a way to achieve my requirement in a way that the mapping related code is encapsulated in its corresponding module.
Here is my configuration, from a centralized place:
List<Assembly> assemblies = GetModules().Select(x => x.GetType().Assembly).ToList();
ISessionFactory nhibernateSessionFactory = Fluently
.Configure()
.Mappings(m => assemblies.ForEach(asm => m.FluentMappings.AddFromAssembly(asm)))
.Database(
MsSqlConfiguration.MsSql2005
.ShowSql()
.ConnectionString(DatabaseConfig.Instance.ConnectionString))
.ExposeConfiguration(c => new SchemaUpdate(c).Execute(true, true))
.BuildSessionFactory();
I use standard class mappings that inherit from ClassMap:
public class User
{
public virtual int ID { get; set; }
public virtual String Username { get; set; }
public virtual String Password { get; set; }
public virtual DateTime DateCreated { get; set; }
public virtual DateTime DateModified { get; set; }
// Must ignore
public string ComputedProperty { get { ... } }
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("User");
Id(x => x.ID).GeneratedBy.Identity();
Map(m => m.Username).Not.Nullable().Length(255).UniqueKey("User_Username_Unique_Key");
Map(m => m.Password).Not.Nullable().Length(255);
Map(m => m.DateCreated).Not.Nullable();
Map(m => m.DateModified).Not.Nullable();
}
}
I know this post is bit old, but I post anyway since I didn't find any up todate posts on the subject.
I guess the easiest way should be to add an attribute to each property we dont want to be persisted to a table. By add a extension that check if it has for eg. has a [NoEntity] attibute.
/// <summary>
/// Tells a single Property to not be persisted to table.
/// </summary>
public class NoEntity : Attribute { }
/// <summary>
/// Extension to ignore attributes
/// </summary>
public static class FluentIgnore
{
/// <summary>
/// Ignore a single property.
/// Property marked with this attributes will no be persisted to table.
/// </summary>
/// <param name="p">IPropertyIgnorer</param>
/// <param name="propertyType">The type to ignore.</param>
/// <returns>The property to ignore.</returns>
public static IPropertyIgnorer SkipProperty(this IPropertyIgnorer p, Type propertyType)
{
return p.IgnoreProperties(x => x.MemberInfo.GetCustomAttributes(propertyType, false).Length > 0);
}
}
And in the fluent config setup:
return Fluently.Configure()
.Database(DatabaseConfig)
.Mappings(m => m.AutoMappings.Add(AutoMap.Assembly(typeof(IDependency).Assembly)
.OverrideAll(p => {
p.SkipProperty(typeof(NoEntity));
}).Where(IsEntity)))
.ExposeConfiguration(ValidateSchema)
.ExposeConfiguration(BuildSchema)
.BuildConfiguration();
I think you are right that the ClassMap is the best place to ignore this property.
Example:
.Override<Shelf>(map =>
{
map.IgnoreProperty(x => x.YourProperty);
});
Documentation: https://github.com/jagregory/fluent-nhibernate/wiki/Auto-mapping#ignoring-properties
As far as getting the mappings from another assembly, it should be as easy as something like this (depending on your current configuration):
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<ProvideClassFromYourOtherAssembly>();
});
Will not Justin. This is the thing with this extension. Just the property you want gets ignored.
public class Person : IEntity{
public virtual string Name{..}
public virtual string Lastname{..}
[NoProperty]
public virtual string FullName{ // Not created property
get { return Name + " " + Lastname; }
}
}
public class Group : IEntity{
public virtual string FullName{..} //Created property
}
I have a class:
public class Tool
{
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public Int64 ID { get; set; }
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public string Name { get; set; }
}
My provider has two methods:
public IEnumerable<Tool> GetFirst()
{
using (var db = new Entitites())
{
return db.Tools.FirstOrDefault();
}
}
public void Update(Tool o)
{
using (var db = new Entities())
{
db.Tools.SaveChanges();
}
}
It doesn't work because they are on different contexts, the parameter on Update method is not even being used. I can however get the object from the database and change the fields one by one with the parameter object then save changes.
What should I do?
Update the object and save?
Keep only one context on providers?
Another approach?
I found the attach method from another question, quite similar
using (var db = new Entitites())
{
// Attach the object on this context
db.Attach(tool);
// Change the state of the context to ensure update
db.ObjectStateManager.GetObjectStateEntry(tool).SetModified();
// ClientWins, flawless victory
db.Refresh(RefreshMode.ClientWins, tool);
}