I have searched everywhere in CF docs, FAQ's, Blog and SO entries but still can't understand why this model:
<cf:project defaultNamespace="Humanisme" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" xmlns:cfsm="http://www.softfluent.com/codefluent/producers.servicemodel/2007/1" defaultKeyPropertyTypeName="int" defaultMaxLength="240" persistencePropertyNameFormat="{1}" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false">
<cf:import path="Default.Surface.cfp" />
<cf:producer name="Business Object Model (BOM)" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
<cf:configuration compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\Humanisme.classes" produceWebMembershipProvider="false" produceWebProfileProvider="false" produceWebBasicAuthenticationModule="false" cfx:targetProject="..\Humanisme.classes\Humanisme.classes.csproj" cfx:targetProjectLayout="Update">
<subProducer typeName="CodeFluent.Producers.ServiceModel.ServiceProducer, CodeFluent.Producers.ServiceModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1bb6d7cccf1045ec" compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\Humanisme.webApi" silverlightTargetVersion="Unspecified" dataAnnotationsProductionModes="NoAnnotations, All" jsonOptions="EnableJson" outputName="HumanismeService" cfx:targetProject="..\Humanisme.webApi\Humanisme.webApi.csproj" cfx:targetProjectLayout="Update" produceProxy="False" />
</cf:configuration>
</cf:producer>
(....)
<cf:entity name="EtapaVital" namespace="Humanisme" categoryPath="/Humanisme">
<cf:property name="Id" key="true" />
<cf:property name="Nom" />
<cf:property name="Idioma" typeName="{0}.EtapaVitalIdiomaCollection" relationPropertyName="Etapa" />
<cf:property name="Documents" typeName="{0}.DocumentCollection" relationPropertyName="EtapaVital" />
</cf:entity>
Renders to these serializing attributes:
RowVersion and EntityState get the serializing attributes.
EntityDisplayName does not.
[System.Runtime.Serialization.DataMemberAttribute(Order=2147483647)]
private CodeFluent.Runtime.CodeFluentEntityState _entityState;
public EtapaVital()
[System.Runtime.Serialization.DataMemberAttribute()]
public virtual string EntityKey
{
get
{
return this.Id.ToString();
}
set
{
this.Id = ((int)(ConvertUtilities.ChangeType(value, typeof(int), -1)));
}
}
public virtual string EntityDisplayName
{
get
{
return this.Nom;
}
}
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
[System.ComponentModel.DataObjectFieldAttribute(false, true)]
[System.ComponentModel.TypeConverterAttribute(typeof(CodeFluent.Runtime.Design.ByteArrayConverter))]
[System.Runtime.Serialization.DataMemberAttribute()]
public byte[] RowVersion
{
get
{
return this._rowVersion;
}
set
{
if (((value != null)
&& (value.Length == 0)))
{
value = null;
}
this._rowVersion = value;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("RowVersion"));
}
}
[System.ComponentModel.DefaultValueAttribute(((int)(-1)))]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=false, Type=typeof(int))]
[System.ComponentModel.DataObjectFieldAttribute(true)]
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=100)]
public int Id
{
get
{
return this._id;
}
set
{
if ((System.Collections.Generic.EqualityComparer<int>.Default.Equals(value, this._id) == true))
{
return;
}
int oldKey = this._id;
this._id = value;
try
{
this.OnCollectionKeyChanged(oldKey);
}
catch (System.ArgumentException )
{
this._id = oldKey;
return;
}
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Id"));
}
}
[System.ComponentModel.DefaultValueAttribute(default(string))]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Type=typeof(string))]
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=200)]
public string Nom
{
get
{
return this._nom;
}
set
{
this._nom = value;
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Nom"));
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Humanisme.EtapaVitalIdiomaCollection Idioma
{
get
{
if ((this._idioma == null))
{
if (((this.Id == -1)
|| (this.EntityState.Equals(CodeFluent.Runtime.CodeFluentEntityState.Created) == true)))
{
this._idioma = new Humanisme.EtapaVitalIdiomaCollection(this);
return this._idioma;
}
this._idioma = Humanisme.EtapaVitalIdiomaCollection.LoadByEtapa(this);
}
return this._idioma;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Humanisme.DocumentCollection Documents
{
get
{
if ((this._documents == null))
{
if (((this.Id == -1)
|| (this.EntityState.Equals(CodeFluent.Runtime.CodeFluentEntityState.Created) == true)))
{
this._documents = new Humanisme.DocumentCollection(null, this);
return this._documents;
}
this._documents = Humanisme.DocumentCollection.LoadByEtapaVital(this);
}
return this._documents;
}
}
public virtual CodeFluent.Runtime.CodeFluentEntityState EntityState
{
get
{
return this._entityState;
}
set
{
if ((System.Collections.Generic.EqualityComparer<CodeFluent.Runtime.CodeFluentEntityState>.Default.Equals(value, this.EntityState) == true))
{
return;
}
if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.ToBeDeleted)
&& (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
{
return;
}
if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.Created)
&& (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
{
return;
}
this._entityState = value;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntityState"));
}
}
It seems there are not related property options on the model surface "Properties" window and I'm not able to find the xml tags or attributes in the cfp file.
I will be very grateful if you can give me any clue or reference where to start learning the CodeFluent Model options that lead to these outputs in the classes.
Thanks again, I'm really turning into a CodeFluent enthusiast!
The RowVersion property is generated by CodeFluent Entities because you set the concurrency mode to Optimistic (documentation). If you don't have the latest value of this property, you cannot save the entity (CodeFluentConcurrencyException). So this property needs to be serialized.
The EntityDisplayName is for UI purposes. Its value is computed from one or many properties. So there is no need to serialize it as you can compute it client side.
There is currently no attributes to change the serialization behavior for these specific properties. However you can write a custom sub producer to add/remove serialization attributes on properties. You'll find an example that adds Json.NET specific attributes on properties (GitHub)
Here's are some attributes to change the serialization behavior:
serializable (Entity): Determines if this entity is marked as serializable
serializeEntityKey (Entity): Determines if the EntityKey must be serialized
serializeTypeName (Entity): Determines if the TypeName must be serialized
serializationMode (Property): documentation
dataMember or includeInSerialization (Property) => Prefer using serializationMode
serializationOrder (Property): Defines the sort order of this property when serialized
serializationNullable (Property): Determines if the property can be skipped when serialized
serializePrivateMember (Property): Determines if the private field of a non-serializable property is serializable
publicSerializationCallbacks (Project): Determines if the serialization callback methods are public (OnEntityDeserializing, OnEntityDeserialized, etc.)
Related
I am trying to display default values in Swashbuckle. Is there a way to define default values in a query list parameter in a .NET Core API.
Something like:
[HttpGet("test")]
public ActionResult<string> TestFunc([FromQuery, BindRequired] List<string> testList = ["value1", "value2"]) {
//Do some stuff
return Ok(results);
}
As far as I know, we couldn't set the default parameter value must be compile-time constant which means we couldn't set a default value for a list or array of string.
That means there is no way to set the defualt value inside the web api paramter.
If you want to show the default value inside the swagger. You could create a class which inherit from IOperationFilter.
Then you could check the paramter name, if the name is equals the testList ,you could set the custom description.
More details, you could refer to below codes example:
Custom class:
public class ParameterClass : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
{
return;
}
foreach (var parameter in operation.Parameters) {
if (parameter.Name == "testList")
{
parameter.Description = #"Default value: ['value1', 'value2']";
}
}
}
}
Register the swaggergen with filter :
services.AddSwaggerGen(c =>
{
c.OperationFilter<ParameterClass>();
});
Result:
Update:
If you want to set the parameter like query string, you could modify the apply method as below:
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
{
return;
}
foreach (var parameter in operation.Parameters) {
if (parameter.Name == "testList")
{
parameter.In = 0;
parameter.Description = #"['value1', 'value2']";
parameter.Schema = new OpenApiSchema() { Type = "string" ,Items = null };
}
}
}
Result:
is there any option to setup log4net to eliminate duplicate messages? I have an application which works in cycles...there is an infinate while cycle. If any error occure (for example database is not accesible), same message is logged again and again in every loop. I need to log the message just once.
I have implemented log4net filter to eliminate same messages during specified time interval.
The class:
public class DuplicityFilter : FilterSkeleton
{
private String lastMessage = null;
private List<Tuple<DateTime, String>> lastMessages = new List<Tuple<DateTime,string>>();
private Int32 _timeWindow = 0;
public Int32 timeWindow
{
get { return _timeWindow; }
set { _timeWindow = value; }
}
public Boolean _lastOnly = false;
public Boolean lastOnly
{
get { return _lastOnly; }
set { _lastOnly = value; }
}
public override FilterDecision Decide(log4net.Core.LoggingEvent loggingEvent)
{
if (_lastOnly)
{
if (lastMessage == loggingEvent.RenderedMessage)
{
return FilterDecision.Deny;
}
else
{
lastMessage = loggingEvent.RenderedMessage;
return FilterDecision.Accept;
}
}
else
{
if (_timeWindow <= 0)
return FilterDecision.Accept;
// Removes old messages
lastMessages.RemoveAll(m => m.Item1 < DateTime.Now.AddSeconds(0 - _timeWindow));
if (!lastMessages.Any(m => m.Item2 == loggingEvent.RenderedMessage))
{
lastMessages.Add(new Tuple<DateTime, string>(loggingEvent.TimeStamp, loggingEvent.RenderedMessage));
return FilterDecision.Accept;
}
else
{
return FilterDecision.Deny;
}
}
}
And the XML definition:
<filter type="your.namespace.here.DuplicityFilter">
<timeWindow value="900" /> <!-- 15min -->
<lastOnly value="false" />
</filter>
I have a function that creates regular Objects of a same type and I cannot avoid that step.
When I use List.addAll(*) I will get many "Duplications" that are not equal in sense of Objectivity.
I have a very bad coded solution and want to ask if there could be a better or more effective one maybe with Java-Util-functions and defining a Comparator for that single intermezzo?
Here is my bad smell:
private void addPartial(List<SeMo_WikiArticle> allnewWiki, List<SeMo_WikiArticle> newWiki) {
if(allnewWiki.isEmpty())
allnewWiki.addAll(newWiki);
else{
for(SeMo_WikiArticle nn : newWiki){
boolean allreadyIn = false;
for(SeMo_WikiArticle oo : allnewWiki){
if(nn.getID()==oo.getID())
allreadyIn= true;
}
if(!allreadyIn)
allnewWiki.add(nn);
}
}
}
Any Ideas?
Add an override function of equals() into class SeMo_WikiArticle :
class SeMo_WikiArticle {
// assuming this class has two properties below
int id;
String name;
SeMo_WikiArticle(int id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object obj) {
// implement your own comparison policy
// here is an example
if (obj instanceof SeMo_WikiArticle) {
SeMo_WikiArticle sw = (SeMo_WikiArticle)obj;
if (this.id == sw.id && (this.name == sw.name || this.name.equals(sw.name))) {
return true;
}
}
return false;
}
}
After that you can use contains() to judge if the list has already contains the specific object of SeMo_WikiArticle.
Here is the code:
private void addPartial(List<SeMo_WikiArticle> allnewWiki, List<SeMo_WikiArticle> newWiki) {
for (SeMo_WikiArticle sw : newWiki) {
if (!allnewWiki.contains(sw)) {
allnewWiki.add(sw);
}
}
}
I am creating an application with MVC4 and entity framework 5. How do can I implement this?
I have looked around and found that I need to override SaveChanges .
Does anyone have any sample code on this? I am using code first approach.
As an example, the way I am saving data is as follows,
public class AuditZoneRepository : IAuditZoneRepository
{
private AISDbContext context = new AISDbContext();
public int Save(AuditZone model, ModelStateDictionary modelState)
{
if (model.Id == 0)
{
context.AuditZones.Add(model);
}
else
{
var recordToUpdate = context.AuditZones.FirstOrDefault(x => x.Id == model.Id);
if (recordToUpdate != null)
{
recordToUpdate.Description = model.Description;
recordToUpdate.Valid = model.Valid;
recordToUpdate.ModifiedDate = DateTime.Now;
}
}
try
{
context.SaveChanges();
return 1;
}
catch (Exception ex)
{
modelState.AddModelError("", "Database error has occured. Please try again later");
return -1;
}
}
}
There is no need to override SaveChanges.
You can
Trigger Context.ChangeTracker.DetectChanges(); // may be necessary depending on your Proxy approach
Then analyze the context BEFORE save.
you can then... add the Change Log to the CURRENT Unit of work.
So the log gets saved in one COMMIT transaction.
Or process it as you see fit.
But saving your change log at same time. makes sure it is ONE Transaction.
Analyzing the context sample:
I have a simple tool, to Dump context content to debug output so when in debugger I can use immediate window to check content. eg
You can use this as a starter to prepare your CHANGE Log.
Try it in debugger immediate window. I have FULL dump on my Context class.
Sample Immediate window call. UoW.Context.FullDump();
public void FullDump()
{
Debug.WriteLine("=====Begin of Context Dump=======");
var dbsetList = this.ChangeTracker.Entries();
foreach (var dbEntityEntry in dbsetList)
{
Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
switch (dbEntityEntry.State)
{
case EntityState.Detached:
case EntityState.Unchanged:
case EntityState.Added:
case EntityState.Modified:
WriteCurrentValues(dbEntityEntry);
break;
case EntityState.Deleted:
WriteOriginalValues(dbEntityEntry);
break;
default:
throw new ArgumentOutOfRangeException();
}
Debug.WriteLine("==========End of Entity======");
}
Debug.WriteLine("==========End of Context======");
}
private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
}
}
private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
}
}
}
EDIT: Get the changes
I use this routine to get chnages...
public class ObjectPair {
public string Key { get; set; }
public object Original { get; set; }
public object Current { get; set; }
}
public virtual IList<ObjectPair> GetChanges(object poco) {
var changes = new List<ObjectPair>();
var thePoco = (TPoco) poco;
foreach (var propName in Entry(thePoco).CurrentValues.PropertyNames) {
var curr = Entry(thePoco).CurrentValues[propName];
var orig = Entry(thePoco).OriginalValues[propName];
if (curr != null && orig != null) {
if (curr.Equals(orig)) {
continue;
}
}
if (curr == null && orig == null) {
continue;
}
var aChangePair = new ObjectPair {Key = propName, Current = curr, Original = orig};
changes.Add(aChangePair);
}
return changes;
}
edit 2 If you must use the Internal Object tracking.
var context = ???// YOUR DBCONTEXT class
// get objectcontext from dbcontext...
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
// for each tracked entry
foreach (var dbEntityEntry in context.ChangeTracker.Entries()) {
//get the state entry from the statemanager per changed object
var stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(dbEntityEntry.Entity);
var modProps = stateEntry.GetModifiedProperties();
Debug.WriteLine(modProps.ToString());
}
I decompiled EF6 . Get modified is indeed using private bit array to track fields that have
been changed.
// EF decompiled source..... _modifiedFields is a bitarray
public override IEnumerable<string> GetModifiedProperties()
{
this.ValidateState();
if (EntityState.Modified == this.State && this._modifiedFields != null)
{
for (int i = 0; i < this._modifiedFields.Length; ++i)
{
if (this._modifiedFields[i])
yield return this.GetCLayerName(i, this._cacheTypeMetadata);
}
}
}
I have an existing application that I am modifying to use Autofac Property Injection. It seems regardless of which method I use to register my types with properties, the properties are always null unless they have public setters. With other IoC containers (e.g. Structuremap) it's possible to scope the setter internal and make it available using the InternalsVisibleTo attribute on the assembly. This would seem nice to restrict clients from modifying the assignment.
Is this possible with Autofac? Or is there another approach when working with property injection to keep the assignments secure?
I've tried using reflection with PropertiesAutoWired() as well as resolving .WithParameter() from my WebApi Global.asax - specifying the specific parameter to be set with no success as an internal setter.
[assembly: InternalsVisibleTo("MyWebAPI.dll")]
[assembly: InternalsVisibleTo("Autofac.dll")]
[assembly: InternalsVisibleTo("Autofac.Configuration.dll")]
namespace My.Namespace
{
public class BaseContext
{
public MyPublicClass _dbHelper { get; internal set; }
public BaseContext()
{
}
protected string DbConnectionString
{
get
{
return _dbHelper.DbConn; //<-Always null unless setter is public
}
}
}
}
You cannot inject internal setters with autofac, because the AutowiringPropertyInjector class is only looking for public properties (see source).
However a logic in the AutowiringPropertyInjector is very simple so you can create your own version which does injection for non public properties:
public static class AutowiringNonPublicPropertyInjector
{
public static void InjectProperties(IComponentContext context,
object instance, bool overrideSetValues)
{
if (context == null)
throw new ArgumentNullException("context");
if (instance == null)
throw new ArgumentNullException("instance");
foreach (
PropertyInfo propertyInfo in
//BindingFlags.NonPublic flag added for non public properties
instance.GetType().GetProperties(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
Type propertyType = propertyInfo.PropertyType;
if ((!propertyType.IsValueType || propertyType.IsEnum) &&
(propertyInfo.GetIndexParameters().Length == 0 &&
context.IsRegistered(propertyType)))
{
//Changed to GetAccessors(true) to return non public accessors
MethodInfo[] accessors = propertyInfo.GetAccessors(true);
if ((accessors.Length != 1 ||
!(accessors[0].ReturnType != typeof (void))) &&
(overrideSetValues || accessors.Length != 2 ||
propertyInfo.GetValue(instance, null) == null))
{
object obj = context.Resolve(propertyType);
propertyInfo.SetValue(instance, obj, null);
}
}
}
}
}
And now you can use this class in the OnActivated event
var builder = new ContainerBuilder();
builder.RegisterType<MyPublicClass>();
builder.RegisterType<BaseContext>()
.OnActivated(args =>
AutowiringNonPublicPropertyInjector
.InjectProperties(args.Context, args.Instance, true));
However the above listed solution now injects all kind of properties so even private and protected ones so you may need to extend it with some additional checks to make sure that you will only inject the properties what you would expect.
I'm using a solution like this:
builder.RegisterType<MyPublicClass>();
builder.RegisterType<BaseContext>()
.OnActivating(CustomPropertiesHandler);
With a handler like this:
//If OnActivated: Autofac.Core.IActivatedEventArgs
public void CustomPropertiesHandler<T>(Autofac.Core.IActivatingEventArgs<T> e)
{
var props = e.Instance.GetType()
.GetTypeInfo().DeclaredProperties //Also "private prop" with "public set"
.Where(pi => pi.CanWrite) //Has a set accessor.
//.Where(pi => pi.SetMethod.IsPrivate) //set accessor is private
.Where(pi => e.Context.IsRegistered(pi.PropertyType)); //Type is resolvable
foreach (var prop in props)
prop.SetValue(e.Instance, e.Context.Resolve(prop.PropertyType), null);
}
Since both IActivatingEventArgs and IActivatedEventArgs has instance and context, you might want to use wrapping methods that uses those parameters on CustomPropertiesHandler instead.
Also we can write #nemesv implementation as an extension method.
public static class AutofacExtensions
{
public static void InjectProperties(IComponentContext context, object instance, bool overrideSetValues)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (instance == null)
{
throw new ArgumentNullException(nameof(instance));
}
foreach (var propertyInfo in instance.GetType().GetProperties(BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic))
{
var propertyType = propertyInfo.PropertyType;
if ((!propertyType.IsValueType || propertyType.IsEnum) && (propertyInfo.GetIndexParameters().Length == 0) && context.IsRegistered(propertyType))
{
var accessors = propertyInfo.GetAccessors(true);
if (((accessors.Length != 1) ||
!(accessors[0].ReturnType != typeof(void))) &&
(overrideSetValues || (accessors.Length != 2) ||
(propertyInfo.GetValue(instance, null) == null)))
{
var obj = context.Resolve(propertyType);
propertyInfo.SetValue(instance, obj, null);
}
}
}
}
public static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InjectPropertiesAsAutowired<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registration)
{
return registration.OnActivated(args => InjectProperties(args.Context, args.Instance, true));
}
To Use;
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<StartupConfiguration>().As<IStartupConfiguration>().AsSelf().InjectPropertiesAsAutowired().AsImplementedInterfaces().SingleInstance();
}
Current version of Autofac defined optional IPropertySelector parameter for PropertiesAutowired which is used to filter out injectable properties.
default implementation for IPropertySelector is DefaultPropertySelector, which filters non public properties.
public virtual bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
if (!propertyInfo.CanWrite || propertyInfo.SetMethod?.IsPublic != true)
{
return false;
}
....
}
define custom IPropertySelector which allows injection to non public properties
public class AccessRightInvariantPropertySelector : DefaultPropertySelector
{
public AccessRightInvariantPropertySelector(bool preserveSetValues) : base(preserveSetValues)
{ }
public override bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
if (!propertyInfo.CanWrite)
{
return false;
}
if (!PreserveSetValues || !propertyInfo.CanRead)
{
return true;
}
try
{
return propertyInfo.GetValue(instance, null) == null;
}
catch
{
// Issue #799: If getting the property value throws an exception
// then assume it's set and skip it.
return false;
}
}
}
Use
builder.RegisterType<AppService>()
.AsImplementedInterfaces()
.PropertiesAutowired(new AccessRightInvariantPropertySelector(true));
Alternatively
Install
PM> Install-Package Autofac.Core.NonPublicProperty
Use
builder.RegisterType<AppService>()
.AsImplementedInterfaces()
.AutoWireNonPublicProperties();