Entity Framework exception for parameters that have 'allow nulls' enabled - c#

I am adding a new register into the SQL Server database using Entity Framework like this:
var configSlot = new DeviceConfig_DB
{
ID_DeviceConfig = 95,
ConfigName = configName,
ParamName = "Param1",
ParamValue = "1000",
ToolName = null,
ToolValue = null,
};
It returns a validation error exception because of ToolName and ToolValue fields, and only are accepted if I set a string value (that fields are nvarchar).
If I insert a new register using a SQL Server Management Studio query, it is accepted even if I don't provide those fields. They do have the 'allow nulls' option.
INSERT INTO [dbo].[DeviceConfig_DB] ([ID_DeviceConfig], [ConfigName], [ParamName], [ParamValue])
VALUES (96, 'test', 'Param1', '1000')
How can I insert a new register with Entity Framework with null values for those fields?
EDIT:
Here is the DeviceConfig_DB class
public partial class DeviceConfig_DB
{
public int ID_DeviceConfig { get; set; }
public Nullable<int> ID_PhysicalDevice { get; set; }
public string ConfigName { get; set; }
public string ParamName { get; set; }
public string ParamValue { get; set; }
public string ToolName { get; set; }
public string ToolValue { get; set; }
public virtual PhysicalDevice_DB PhysicalDevice_DB { get; set; }
}

Related

Entity Framework setting foreign key, primary key violation

When trying to create a new database entry of type TestForm2 I include the related object Unit Type's ID as a foreign key, except when I perform context.SaveChanges() after adding the new model I get the following SQL exception:
SqlException: Violation of PRIMARY KEY constraint 'PK_dbo.UnitTypes'. Cannot insert duplicate key in object 'dbo.UnitTypes'. The duplicate key value is (2d911331-6083-4bba-a3ad-e50341a7b128). The statement has been terminated.
What this means to me is that it thinks that the foreign entry I'm trying to relate to the new model is instead a new object that it's attempting to insert into the UnitTypes table and failing because it sees an existing entry with the same primary key.
For context (pun not intended), this is my data context, the database model, and the erroring "Create" function.
public class DataContext : IdentityDbContext<ApplicationUser>
{
public DataContext() : base("DefaultConnection")
{
}
public static DataContext Create()
{
return new DataContext();
}
public DbSet<SafetyIncident> SafetyIncidents { get; set; }
public DbSet<ProductionLine> ProductionLines { get; set; }
public DbSet<ProductionOrder> ProductionOrders { get; set; }
public DbSet<SerialOrder> SerialOrder { get; set; }
public DbSet<QualityError> QualityErrors { get; set; }
public DbSet<PSA> PSAs { get; set; }
public DbSet<TestStation> TestStations { get; set; }
public DbSet<ProductionGoal> ProductionGoals { get; set; }
public DbSet<DailyWorkStationCheck> DailyWorkStationChecks { get; set; }
public DbSet<TestForm> TestForms { get; set; }
public DbSet<User> AppUsers { get; set; }
public DbSet<Options> Options { get; set; }
public DbSet<DriveList> DriveSerials { get; set; }
public DbSet<MRPController> MRPControllers { get; set; }
public DbSet<TestOption> TestOptions { get; set; }
public DbSet<UnitType> UnitTypes { get; set; }
public DbSet<UnitTypeMap> UnitTypeMaps { get; set; }
public DbSet<TestForm2> TestForm2s { get; set; }
public DbSet<TestFormSection> TestFormSections { get; set; }
public DbSet<TestFormSectionStep> TestFormSectionSteps { get; set; }
}
public class TestForm2 : BaseEntity
{
public string SerialNumber { get; set; }
public string MaterialNumber { get; set; }
public string UnitTypeId { get; set; }
public UnitType UnitType { get; set; }
public bool UsesStandardOptions { get; set; }
public bool OptionsVerified { get; set; } // This will only be used when UsesStandardOptions is true, otherwise its value doesn't matter
public ICollection<TestOption> AllOptions { get; set; } // List of all options (at time of form creation)
public ICollection<TestOption> Options { get; set; } // The options on a unit
public ICollection<TestFormSection> Sections { get; set; }
}
public FormViewModel Create(FormViewModel vm)
{
using (var context = new DataContext())
{
List<string> optionListStrings = GetOptionListForModelNumber(vm.MaterialNumber); // returns list of option codes
List<TestOption> matchingOptions = context.TestOptions
.Where(optionInDb =>
optionListStrings.Any(trimOption => trimOption == optionInDb.OptionCode)).ToList();
var unitType = context.UnitTypes.FirstOrDefault(x => x.Name == vm.UnitType);
string unitTypeId = unitType.Id;
TestForm2 newForm = new TestForm2
{
// ID & CreatedAt instantiated by Base Entity constructor
SerialNumber = vm.SerialNumber,
MaterialNumber = vm.MaterialNumber,
UnitTypeId = unitType.Id,
UsesStandardOptions = vm.UsesStandardOptions,
OptionsVerified = vm.OptionsVerified,
//AllOptions = context.TestOptions.ToList(),
//Options = matchingOptions,
Sections = vm.Sections,
};
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
context.TestForm2s.Add(newForm);
context.SaveChanges(); // THIS IS WHERE THE SQL EXCEPTION IS HAPPENING
return vm;
}
return null;
}
Lastly, I'm not sure if it's relevant, but a full copy of the related UnitType is viewable as part of newForm only after context.TestForm2s.add(newForm) resolves. This is weird to me since I don't think it should be automatically relating the data object like that.
I haven't been able to try much since everything looks properly configured to me. Please let me know if this is not the case or if I should include any other info.
Found the issue. The vm.Sections was not using viewmodels to contain the section data, so the vm.Sections contained UnitType database models. Since this was instantiated in the controller (before opening the data context in the TestForm2 Create method) EF assumed that these data were new and needed to be added to the UnitType table.
Hope this thread helps someone else running into similar issues.

MSSQL Stored Proc and .FromSql with EntityFrameWorkCore 2

I have the following method for calling a stored procedure;
public IList<Trader> GetTradersWithinRadius(int category, decimal latitude, decimal longitude)
{
var sproc = "FindTradersWithinRadiusLatLong";
var sqlParams = new List<SqlParameter>()
{
new SqlParameter("#CATEGORY", category),
new SqlParameter("#LAT", latitude),
new SqlParameter("#LONG", longitude),
};
var parameters = sqlParams.ToArray<object>();
var traders = this.Traders.FromSql($"{sproc} #CATEGORY, #LAT, #LONG", parameters).ToList();
return traders;
}
Now if I execute the stored procedure directly in SQL Management studio;
EXEC #return_value = [FindTradersWithinRadiusLatLong]
#LAT = 43.590000,
#LONG = -111.120000,
#CATEGORY = 1
I get a result. However when I call my above method I am getting an empty set being returned?
The Trader class is;
public class Trader : AuditableEntity
{
public string Name { get; set; }
public string Telephone { get; set; }
public string Email { get; set; }
public string Website { get; set; }
public List<Profile> Profiles { get; set; }
public List<Address> Addresses { get; set; }
public List<Contact> Contacts { get; set; }
public List<TraderCategory> Categories { get; set; }
public string Notes { get; set; }
public List<TraderGallery> Gallery { get; set; }
public List<Review> Reviews { get; set; }
public TraderReviewStatistic ReviewStatistic { get; set; }
public ApplicationUser User { get; set; }
}
Is there any reason its not generating the list I get through MSSQL Manager?
I think the problem is in how your composing the query. At the very least, you're making it more difficult than it needs to be. FromSql can handle named parameters out of the box, simply by passing values (it uses relative position to determine which value is substituted for what). Long and short, try:
var traders = this.Traders.FromSql($"{sproc} #CATEGORY, #LAT, #LONG", category, latitude, longitude).ToList();
There's no need to create an explicit array of SqlParameters.

Problems with servicestack and typelinks

I have problems with gerenating types, it returns error
500 - InvalidDataException
I can't understand whats wrong as my project builds fine and API works.
None of the types works except the metadata
https://testapi.bokamera.se/types/
Please help as i'm stuck
Regards Kristian
Here you can see my call using Postman
and here you can see my DTO causing the problem
namespace BokaMera.API.ServiceModel.Dtos
{
[Route("/customfields",
Verbs = "GET",
Summary = "Find custom fields",
Notes =
"Find custom fields defined for the bookings that the current admin user is authorized to view."
)]
[ApiResponse(HttpStatusCode.Unauthorized, "You were unauthorized to call this service")]
[ApiResponse(HttpStatusCode.Forbidden, "You have too low privilegies to call this service")]
public class CustomFieldQuery :
QueryBase<CustomFieldConfig, CustomFieldQueryResponse>
{
[ApiMember(
Description =
"One or multiple id's of custom fields to find",
ParameterType = "query",
IsRequired = false)]
public int[] Ids { get; set; }
[ApiMember(
Description =
"Company id to find custom fields for",
ParameterType = "query",
IsRequired = false)]
public Guid? CompanyId { get; set; }
[ApiMember(
Description =
"Table to which the custom field belongs to",
ParameterType = "query",
IsRequired = false)]
public string Table { get; set; }
[ApiMember(
Description =
"Active or removed fields, empty parameter includes both",
ParameterType = "query",
IsRequired = false)]
public bool? Active { get; set; }
}
public class CustomFieldQueryResponse
{
[ApiMember(Description = "Custom field id")]
public int Id { get; set; }
[ApiMember(Description = "Reference to company that owns the custom field configuration")]
public Guid CompanyId { get; set; }
[ApiMember(Description = "Group id")]
public int? GroupId { get; set; }
[ApiMember(Description = "Config id")]
public int ConfigId { get; set; }
[ApiMember(Description = "Configuration name. Example: \"Number of persons\".")]
public string ConfigName { get; set; }
[ApiMember(Description = "Field width. Example: 20")]
public int Width { get; set; }
[ApiMember(Description = "Column in database where to store the information. Example: \"TextField1\"")]
public string Column { get; set; }
[ApiMember(Description = "Custom field description. Example: \"For how many persons is this booking?\"")]
public string Description { get; set; }
[ApiMember(Description = "Data field of custom field. Valid values are: TextBox, ... Example: \"TextBox\"")]
public string DataType { get; set; }
[ApiMember(Description = "Default value of the field. Example: \"3\"")]
public string DefaultValue { get; set; }
[ApiMember(Description = "Determines if the field is required to have a value or not")]
public bool Required { get; set; }
[ApiMember(Description = "Error message shown to the user if the field data is required but not entered")]
public string MandatoryErrorMessage { get; set; }
[ApiMember(Description = "Max lenght of the field")]
public int MaxLength { get; set; }
[ApiMember(Description = "")]
public bool MultipleLineText { get; set; }
[ApiMember(Description = "Regular expression used for validation of the field")]
public string RegEx { get; set; }
[ApiMember(Description = "Error message shown if the regular expression validation failed")]
public string RegExErrorMessage { get; set; }
[ApiMember(Description = "If the custom field is active or have been removed")]
public bool Active { get; set; }
[ApiMember(Description = "Table to which the field belongs")]
public string Table { get; set; }
[ApiMember(Description = "")]
public List<CustomFieldLookupResponse> Values { get; set; }
}
[Alias("V_FreeFieldFieldLookUp")]
public class CustomFieldLookupResponse
{
[PrimaryKey]
[Alias("FieldLookupId")]
public int? Id { get; set; }
[Alias("FreeFieldId")]
public int CustomFieldId { get; set; }
[Alias("FieldlookupActive")]
public bool? Active { get; set; }
[Alias("FieldLookupGroupId")]
public int GroupId { get; set; }
[Alias("FieldlookupSortOrder")]
public int? SortOrder { get; set; }
[Alias("FieldlookupValue")]
public string Value { get; set; }
}
//Used when sending in values on a DTO request object to the services to save on the tables.
public class CustomFieldDataValue
{
public int Id { get; set; }
public string Column { get; set; }
public string Value { get; set; }
}
//Used to list all values on a DTO response object
public class CustomFieldData
{
public int Id { get; set; }
public string Column { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Value { get; set; }
}
//Used when post or put values for different DTOS, see example Resource Dtos
public class AddCustomField
{
public int Id { get; set; }
public string Value { get; set; }
}
}
And my service
namespace BokaMera.API.ServiceInterface.Services
{
public class CustomFieldService : AppServiceBase
{
public IAutoQueryDb AutoQuery { get; set; }
[Authenticate]
[RequiredRole(Role.ApplicationAdmin)]
public object Get(CustomFieldQuery request)
{
// Get the autoquery that we will append to
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
// The user can only see his/her own time exceptions
q = q.Where(te => te.CompanyId == UserSession.CompanyId);
// Execute counts and return the query response
var response = AutoQuery.Execute(request, q);
return response;
}
}
}
If you were on v4.0.58, it had an issue with Add ServiceStack Reference which can be resolved by upgrading to any release after that.
The issue with your DTOs was because they contained illegal " character in the Metadata attributes, i.e:
[ApiMember(Description = "Configuration name. Example: \"Number of persons\".")]
public string ConfigName { get; set; }
We validate metadata attributes to prevent an attacker from potentially injecting illegal data that they could use to generate malicious in Service consumers.
So to prevent the error you can remove double quotes (") from strings in your metadata attributes, replacing them with single quotes (') is fine, otherwise you can disable token verification in your AppHost.Configure() with:
NativeTypesFeature.DisableTokenVerification = true;
Incidentally Exception details from Code Generation will be more visible in the next release from this commit, so it will be clearer to tell what the error was.
Deprecated Warnings
Please take note of the Deprecation messages (also covered in Breaking changes in each Release Notes). QueryBase has been renamed to QueryDb, e.g:
public class CustomFieldQuery :
QueryDb<CustomFieldConfig, CustomFieldQueryResponse>
Also it's better for your custom AutoQuery implementations to pass the entire IRequest instead of just the parameters, e.g:
var q = AutoQuery.CreateQuery(request, base.Request);
This has the same behavior except it also allows this AutoQuery Service to participate in Multitenancy requests.

Getting data from multiple tables (C# Entity), linked by foreign keys, converted to anonymous object

I'm new to Entity/Linq/Lambda and I have the following problem:
I have a web application which provides a JSON Api through ASP.NET MVC. The database is MSSQL and I use the C# entity framework as data access layer.
When getting data from a single table, I need to convert this to an anonymous object, before I can convert it to JSON to avoid a circular reference error.
This is a simplified example, but take these tables for example:
If I simply want to return all the translators in JSON, this is how I go about it:
DBEntities db = new DBEntities();
var data = db.Translator.Select(x => new
{
TranslatorID = x.TranslatorID,
FirstName = x.FirstName,
LastName = x.LastName,
Email = x.Email,
Referenced = x.TranslatorLanguage.Count != 0
});
return Json(data, JsonRequestBehavior.AllowGet);
The generated Model classes by Entity would look something like this:
public partial class Translator
{
public Translator()
{
this.TranslatorLanguage = new HashSet<TranslatorLanguage>();
}
public int TranslatorID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public virtual ICollection<TranslatorLanguage> TranslatorLanguage { get; set; }
}
public partial class TranslatorLanguage
{
public int TranslatorLanguageID { get; set; }
public int SourceLanguageID { get; set; }
public int TargetLanguageID { get; set; }
public virtual Translator Translator { get; set; }
public virtual Language Language1 { get; set; }
public virtual Language Language2 { get; set; }
}
public partial class Language
{
public Language()
{
this.TranslatorLanguage = new HashSet<TranslatorLanguage>();
this.TranslatorLanguage1 = new HashSet<TranslatorLanguage>();
}
public int TranslatorLanguageID { get; set; }
public int SourceLanguageID { get; set; }
public int TargetLanguageID { get; set; }
public virtual ICollection<TranslatorLanguage> TranslatorLanguage { get; set; }
public virtual ICollection<TranslatorLanguage> TranslatorLanguage1 { get; set; }
}
But I would like to be able to return a JSON with all the translators where each Translator-object contains an array with the TranslatorLanguage entries, and for each source- and target language to have it's varchar code and description values.
I have no idea how to go about this,
Thanks in advance.
The same way you project (select) Translator to anonymous type, you can project TranslatorLanguage to a nested anonymous type list.
Since you have defined the necessary navigation properties, it's quite easy - all you need is to follow the navigation properties (i.e. navigate) inside the query like if they were objects:
var data = db.Translator.Select(t => new
{
TranslatorID = t.TranslatorID,
FirstName = t.FirstName,
LastName = t.LastName,
Email = t.Email,
Languages = t.TranslatorLanguage.Select(tl => new
{
SourceCode = tl.Language1.Code,
SourceDescription = tl.Language1.Description,
TargetCode = tl.Language2.Code,
TargetDescription = tl.Language2.Description,
}).ToList()
}).ToList();

How to retrieve Data Annotation Alias("tablename") and Alias("field name") from ServiceStack ORMLite?

I need to return the alias from the Data Annotation of this class "tblAccounts" and "AccountNumber". It uses ServiceStack ORM Lite.
[Alias("tblAccounts")]
[Schema("task")]
public class Account : IHasId<int>
{
[Alias("AccountNumber")]
public int Id { get; set; }
[Required]
public int UnitId { get; set; }
[Required]
public int OldAccountNumber { get; set; }
[Required]
}
You can query this from OrmLite's ModelDefinition that's created for every POCO Table, e.g:
var modelDef = typeof(Account).GetModelMetadata();
var tableName = modelDef.ModelName;
var idName = modelDef.PrimaryKey.FieldName;
Which in both cases will return the [Alias] if it exists.

Categories