I am implementing a relatively simple model of user management using Castle Active Record with NHibernate on top of MySql, and I have ran into an issue.
Let us say, I have two tables _users and _passwords described by the following SQL create statements
CREATE TABLE _users (
id bigint(20) NOT NULL AUTO_INCREMENT,
username char(32) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY username_UQ (username),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE _passwords (
id bigint(20) NOT NULL AUTO_INCREMENT,
creation_date datetime NOT NULL,
user_id bigint(20) NOT NULL,
password_hash char(64) NOT NULL,
valid_end_date datetime NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY user_id_password_UQ (user_id,password_hash),
KEY user_passwords_FK (user_id),
CONSTRAINT user_passwords_FK FOREIGN KEY (user_id) REFERENCES _users (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The idea is to keep a primitive password history, therefore, the passwords are kept in a separate table _passwords, which has many-to-one relation with the _users table.
Now, the following C# code models this structure using Castle Active Records
namespace DataEntities
{
[ActiveRecord("_users")]
public class User : ActiveRecordBase<User>
{
[PrimaryKey(PrimaryKeyType.Identity, "id", Access = PropertyAccess.NosetterLowercase)]
public ulong Id
{
get;
set;
} // Id
[Property("username", ColumnType = "String", NotNull = true, Unique = true)]
[ValidateIsUnique]
public string Username
{
get;
set;
} // Username
[HasMany(typeof(Password))]
public IList<Password> Passwords
{
get;
set;
} // Passwords
public string ValidPasswordHash
{
get
{
DateTime l_dtNow = DateTime.Now;
if (Passwords.Count != 1 || Passwords[0].ValidFrom >= l_dtNow || Passwords[0].ValidUntil <= l_dtNow)
{
throw new Exception();
}
return Encoding.UTF8.GetString(Passwords[0].PasswordHash);
}
} // ValidPasswordHash
public static User FindByCredentials(string i_sUsername, string i_sHashedPassword)
{
return FindOne(Restrictions.Eq("Username", i_sUsername), Restrictions.Eq("ValidPasswordHash", i_sHashedPassword));
} // FindByCredentials
} // User
[ActiveRecord("_passwords")]
public class Password : ActiveRecordBase<Password>
{
[PrimaryKey(PrimaryKeyType.Identity, "id", Access = PropertyAccess.NosetterLowercase)]
public ulong Id
{
get;
set;
} // Id
[BelongsTo("user_id", NotNull = true, UniqueKey = "_passwords_UQ1")]
public ulong UserId
{
get;
set;
} // UserId
[Property("password_hash", ColumnType = "UInt64", NotNull = true, UniqueKey = "_passwords_UQ1")]
public byte[] PasswordHash
{
get;
set;
} // PasswordHash
[Property("creation_date", ColumnType = "DateTime", NotNull = true)]
public DateTime ValidFrom
{
get;
set;
} // ValidFrom
[Property("valid_end_date", ColumnType = "DateTime", NotNull = true)]
public DateTime ValidUntil
{
get;
set;
} // ValidUntil
} // Password
} // DataEntities
and on my application start the framework is initialized
try
{
ActiveRecordStarter.Initialize(ActiveRecordSectionHandler.Instance, typeof(Password),
typeof(User));
}
catch (Exception l_excpt)
{
// handle exception
}
At the end, when this code runs, it generates the following exception:
Castle.ActiveRecord.Framework.ActiveRecordException: ActiveRecord tried to infer details about the relation User.Passwords but it could not find a 'BelongsTo' mapped property in the target type DataEntities.Password
at Castle.ActiveRecord.Framework.Internal.SemanticVerifierVisitor.VisitHasMany(HasManyModel model) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\SemanticVerifierVisitor.cs:line 544
at Castle.ActiveRecord.Framework.Internal.AbstractDepthFirstVisitor.VisitNodes(IEnumerable nodes) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\AbstractDepthFirstVisitor.cs:line 45
at Castle.ActiveRecord.Framework.Internal.AbstractDepthFirstVisitor.VisitModel(ActiveRecordModel model) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\AbstractDepthFirstVisitor.cs:line 59
at Castle.ActiveRecord.Framework.Internal.SemanticVerifierVisitor.VisitModel(ActiveRecordModel model) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\SemanticVerifierVisitor.cs:line 122
at Castle.ActiveRecord.Framework.Internal.AbstractDepthFirstVisitor.VisitNodes(IEnumerable nodes) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\Internal\Visitors\AbstractDepthFirstVisitor.cs:line 45
at Castle.ActiveRecord.ActiveRecordStarter.RegisterTypes(ISessionFactoryHolder holder, IConfigurationSource source, IEnumerable`1 types, Boolean ignoreProblematicTypes) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\ActiveRecordStarter.cs:line 927
at Castle.ActiveRecord.ActiveRecordStarter.Initialize(IConfigurationSource source, Type[] types) in c:\daten\dev\External\Castle\AR2.0\ActiveRecord\Castle.ActiveRecord\Framework\ActiveRecordStarter.cs:line 202
at Global.Application_Start(Object sender, EventArgs e) in C:\Projects Code\xyz\Global.asax.cs:line 22
Well, I have stared endlessly at the UserId property of the Password class, I have googled, and now I am quite lost. So the community is my last hope... Can anybody help me understanding what causes this exception and how to fix it?
Thank you all in advance for your replies and comments.
You should have a User User { get; set; } reference property instead of a foreign key.
The official docs are a good place to start.
Related
In a professional social network, how would I represent connections between users? (like Linkedin) Should I create a connection class for which there would be an instance for every connection between 2 users or is that redundant? Should the user class instead have a self-association (reflexive association)?
Your User class would have a collection of Followings:
public class User
{
// ... the other code is omitted for the brevity
public IEnumerable<User> Followings { get; set; }
}
So if your database has the Following table:
CREATE TABLE Followings(
User_Id INT NOT NULL,
Following_Id INT NOT NULL,
DateCreated DATE NOT NULL,
);
Do not forget to create constraints and foreign keys in your table. Then it is possible to have Following class:
public class Followings {
public UserId int { get; set; }
public FollowingId int { get; set; }
public DateCreated DateTime { get; set; }
}
and then you can write easily the following query:
select * from Following where UserId = x.id -- or vice versa
I have 2 tables, Foo and Bar, Foo has a link to a Bar record
public class Foo
{
public int Id { get; set; }
public Bar SomeBar{ get; set; }
}
public class Bar
{
public int? Id { get; set; }
...
}
the SQL table (with the FK constraint between the two tables):
CREATE TABLE [dbo].[Foo] (
[Id] INT IDENTITY (1, 1) NOT NULL PRIMARY KEY,
[SomeBarId] INT NOT NULL,
CONSTRAINT [FK_Foo_Bar] FOREIGN KEY ([SomeBarId]) REFERENCES [Bar]([Id]),
);
when I save the table, Entity does not use SomeBarId in the query, producing an error, while I have set a FK constraint between the two tables
Cannot insert the value NULL into column 'SomeBarId ', table 'dbo.foo'; column does not allow nulls. INSERT fails.
how do I tell entity to use the field SomeBarId when doing the insert ?
var someBar = await _context.Bars.FindAsync(1); // fetch a Bar record
foo.SomeBar = someBar; // linking the objects
_context.Foo.Add(foo);
await _context.SaveChangesAsync();
I expect EF to get someBar.Id and use it in the query when inserting Foo in DB
thanks for the time you'll spend helping me on this
Try adding ForeignKey attribute in class Foo and remove the nullable Id in Bar
public class Foo
{
[ForeignKey("Bar")]
public int Id { get; set; }
public Bar SomeBar{ get; set; }
}
public class Bar
{
public int Id { get; set; }
...
}
Table in postgres:
CREATE TABLE "public"."filters" (
"Id" int4 NOT NULL DEFAULT nextval('"filters_Id_seq"'::regclass),
"Name" varchar(200) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"Type" int4 NOT NULL,
"Alias" varchar(200) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying
)
;
-- ----------------------------
-- Indexes structure for table filters
-- ----------------------------
CREATE UNIQUE INDEX "filters_unique_alias_key" ON "public"."filters" USING btree (
"Alias" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
CREATE UNIQUE INDEX "filters_unique_name_key" ON "public"."filters" USING btree (
"Name" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
-- ----------------------------
-- Primary Key structure for table filters
-- ----------------------------
ALTER TABLE "public"."filters" ADD CONSTRAINT "filters_primary_key" PRIMARY KEY ("Id");
Model:
[Table("filters")]
public class Filter
{
[Key] public int Id { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; }
[Required]
[StringLength(200)]
public string Alias { get; set; }
[Required] public FilterType Type { get; set; }
}
public enum FilterType
{
Logic = 0,
Text = 1,
Number = 2
}
Context:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Filter>().HasIndex(model => model.Name).HasName("filters_unique_name_key").IsUnique();
builder.Entity<Filter>().HasIndex(model => model.Alias).HasName("filters_unique_alias_key").IsUnique();
}
I'm making post request with NOT unique values and receive DbUpdateException from postgres:
public IActionResult Create(Filter filter = null)
{
if (ModelState.IsValid && filter != null)
{
// code
}
}
Why if I defined indexes by the fluent api a modelstate is valid but index validation isn't working? Seems ef core ignore indexes validation but why? How to validate it?
The unique index fluent API config doesn't affect model validation. It can't know whether a particular posted value is unique or not without querying the database, which is not something the modelbinder is empowered to do or even should do. If you want to add validation for this, you'll need to do so manually.
var filter = await _context.Filters.FirstOrDefaultAsync(x => x.Name == model.Name || x.Alias == model.Alias);
if (filter?.Name == model.Name)
ModelState.AddModelError(nameof(model.Name), "Name must be unique.");
if (filter?.Alias == model.Alias)
ModelState.AddModelError(nameof(model.Alias), "Alias must be unique.");
I have the following classes:
public class Event
{
public virtual Guid Id { get; set; }
public virtual string UserName { get; set; }
public virtual EventId EventId { get; set; }
}
public class EventId
{
public virtual Guid EventGuid {get; private set;}
}
I am using a fluent NHibernate automapping in order to map my classes and I override specific properties when needed.
Class: Event is in s_typesToMap and class: EventId is in s_components.
As a result I get the following table generated:
create table "Event" (
Id UNIQUEIDENTIFIER not null,
UserName NVARCHAR(255) null,
EventIdentifierEventGuid UNIQUEIDENTIFIER null,
primary key (Id)
)
I want to create an index on EventIdentifierEventGuid which is a property in Event component.
I tried to do it as follows:
.Override<Event>(obj => obj.Map(x => x.EventId.EventGuid).Index("EventId_index"))
When I generate the ddl I get the following:
create index EventId_index on "Event" (EventGuid)
The expected result should be an index on EventIdentifierEventGuid instead of EventGuid
How can I do it?
I have the following tables and entities which need to be mapped in Fluent NHibernate.
Tables:
CREATE TABLE workarea
(
id uuid NOT NULL,
name character varying(255) NOT NULL,
CONSTRAINT pk_workarea PRIMARY KEY (id),
)
CREATE TABLE element
(
id uuid NOT NULL,
name character varying(255) NOT NULL,
CONSTRAINT pk_element PRIMARY KEY (id),
)
CREATE TABLE attachment
(
id uuid NOT NULL,
filename character varying(255) NOT NULL,
CONSTRAINT pk_attachment PRIMARY KEY (id),
)
CREATE TABLE objectattachment
(
id uuid NOT NULL,
attachmentid uuid NOT NULL,
attachmenttype string NOT NULL,
objectid uuid NOT NULL,
CONSTRAINT pk_objectattachment PRIMARY KEY (id),
CONSTRAINT fk_oa_a FOREIGN KEY (attachmentid)
REFERENCES attachment (id) MATCH SIMPLE
ON UPDATE RESTRICT ON DELETE RESTRICT,
CONSTRAINT fk_oa_at FOREIGN KEY (attachmenttypeid)
REFERENCES attachmenttype (id) MATCH SIMPLE
ON UPDATE RESTRICT ON DELETE RESTRICT
)
The idea under this database design is as follows:
A "workarea" or an "element" could have several "attachment" files and an "attachment" file could be referred to by several "workarea"s or "element"s.
A "workarea" or an "element" could refer to the same "attachment" file.
So the relations between "attachment"s and "workarea"s or "element"s are stored in "objectattachment
" table, in which:
"attachmentid" field refers to the identifier of a specific "attachment"s.
"attachmenttype" field (discriminator) defines whether this relation
is between "attachment"s and "workarea"s or between "attachment"s and
"element"s.
"objectid" field refers to the identifier of a specific "workarea"s or "element"s, depending on the value of the above "attachmenttype" field.
Based on the database design, I then define domain model classes as follows:
public class WorkArea
{
private Guid _id = Guid.Empty;
private string _name;
public virtual Guid Id
{
get { return _id ; }
set { _id = value; }
}
public virtual string Name
{
get { return _name ; }
set { _name = value; }
}
}
public class Element
{
private Guid _id = Guid.Empty;
private string _name;
public virtual Guid Id
{
get { return _id ; }
set { _id = value; }
}
public virtual string Name
{
get { return _name ; }
set { _name = value; }
}
}
public class Attachment
{
private Guid _id = Guid.Empty;
private string _fileName;
public virtual Guid Id
{
get { return _id ; }
set { _id = value; }
}
public virtual string FileName
{
get { return _fileName; }
set { _fileName= value; }
}
}
public class WorkAreaAttachment : Attachment
{
private WorkArea _workArea;
public virtual WorkArea WorkArea
{
get { return _workArea; }
set { _workArea = value; }
}
}
public class ElementAttachment : Attachment
{
private Element _element;
public virtual Element Element
{
get { return _element; }
set { _element = value; }
}
}
Now my question is whether I could mapping these domain model classes with the above database design. If yes, then how could I do that? If no, then how do I change the domain model classes to support Fluent NHibernate mapping against the designed database as I don't want to change the current database design (i.e. create separate "attachment" tables for "workarea" and "element").
Regards,
Quan
public class AttachmentLink
{
private Attachment _attachment;
public virtual Attachment Parent
{
get { return _attachment; }
set { _attachment = value; }
}
private IHasAttachments _linkedTo;
public virtual IHasAttachments LinkedTo
{
get { return _linkedTo; }
set { _linkedTo = value; }
}
}
// in AttachmentMap
HasMany(x => x.Links)
.Table("objectattachment");
// map the component
sealed class AttachmentLinkMap : ComponentMap<AttachmentLink>
{
public AttachmentLinkMap()
{
References(x => x.Attachment, "attachmentid");
ReferencesAny(x => x.LinkedTo)
.IdentityType<Guid>()
.EntityIdentifierColumn("objectid")
.EntityTypeColumn("attachmenttype")
.AddMetaValue<WorkArea>(typeof(WorkArea).Name.ToLower())
.AddMetaValue<Element>(typeof(Element).Name.ToLower())
.Not.LazyLoad(); // to prevent false proxies
}
}
// in ElementMap, and almost the same in WorkAreaMap
HasManyToMany(x => x.Attachments)
.Where("attachmenttype='element'")
Note: you don't need an Id column in the link table